001/* 002 * Portions of this software was developed by employees of the National Institute 003 * of Standards and Technology (NIST), an agency of the Federal Government and is 004 * being made available as a public service. Pursuant to title 17 United States 005 * Code Section 105, works of NIST employees are not subject to copyright 006 * protection in the United States. This software may be subject to foreign 007 * copyright. Permission in the United States and in foreign countries, to the 008 * extent that NIST may hold copyright, to use, copy, modify, create derivative 009 * works, and distribute this software and its documentation without fee is hereby 010 * granted on a non-exclusive basis, provided that this notice and disclaimer 011 * of warranty appears in all copies. 012 * 013 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER 014 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY 015 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF 016 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM 017 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE 018 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT 019 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, 020 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, 021 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, 022 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR 023 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT 024 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. 025 */ 026 027package gov.nist.secauto.metaschema.cli.util; 028 029import static org.fusesource.jansi.Ansi.ansi; 030 031import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationFinding; 032import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level; 033import gov.nist.secauto.metaschema.core.model.validation.IValidationFinding; 034import gov.nist.secauto.metaschema.core.model.validation.IValidationResult; 035import gov.nist.secauto.metaschema.core.model.validation.JsonSchemaContentValidator.JsonValidationFinding; 036import gov.nist.secauto.metaschema.core.model.validation.XmlSchemaContentValidator.XmlValidationFinding; 037 038import org.apache.logging.log4j.LogBuilder; 039import org.apache.logging.log4j.LogManager; 040import org.apache.logging.log4j.Logger; 041import org.fusesource.jansi.Ansi; 042import org.fusesource.jansi.Ansi.Color; 043import org.xml.sax.SAXParseException; 044 045import java.util.List; 046 047import edu.umd.cs.findbugs.annotations.NonNull; 048 049public final class LoggingValidationHandler { 050 private static final Logger LOGGER = LogManager.getLogger(LoggingValidationHandler.class); 051 052 private static final LoggingValidationHandler NO_LOG_EXCPTION_INSTANCE = new LoggingValidationHandler(false); 053 private static final LoggingValidationHandler LOG_EXCPTION_INSTANCE = new LoggingValidationHandler(true); 054 055 private final boolean logExceptions; 056 057 public static LoggingValidationHandler instance() { 058 return instance(false); 059 } 060 061 public static LoggingValidationHandler instance(boolean logExceptions) { 062 return logExceptions ? LOG_EXCPTION_INSTANCE : NO_LOG_EXCPTION_INSTANCE; 063 } 064 065 protected LoggingValidationHandler(boolean logExceptions) { 066 this.logExceptions = logExceptions; 067 } 068 069 public boolean isLogExceptions() { 070 return logExceptions; 071 } 072 073 public boolean handleValidationResults(IValidationResult result) { 074 handleValidationFindings(result.getFindings()); 075 return result.isPassing(); 076 } 077 078 public void handleValidationFindings(@NonNull List<? extends IValidationFinding> findings) { 079 for (IValidationFinding finding : findings) { 080 if (finding instanceof JsonValidationFinding) { 081 handleJsonValidationFinding((JsonValidationFinding) finding); 082 } else if (finding instanceof XmlValidationFinding) { 083 handleXmlValidationFinding((XmlValidationFinding) finding); 084 } else if (finding instanceof ConstraintValidationFinding) { 085 handleConstraintValidationFinding((ConstraintValidationFinding) finding); 086 } else { 087 throw new IllegalStateException(); 088 } 089 } 090 } 091 092 private void handleJsonValidationFinding(@NonNull JsonValidationFinding finding) { 093 Ansi ansi = generatePreamble(finding.getSeverity()); 094 095 getLogger(finding).log( 096 ansi.a('[') 097 .fgBright(Color.WHITE) 098 .a(finding.getCause().getPointerToViolation()) 099 .reset() 100 .a(']') 101 .format(" %s [%s]", 102 finding.getMessage(), 103 finding.getDocumentUri().toString())); 104 } 105 106 private void handleXmlValidationFinding(XmlValidationFinding finding) { 107 Ansi ansi = generatePreamble(finding.getSeverity()); 108 SAXParseException ex = finding.getCause(); 109 110 getLogger(finding).log( 111 ansi.format("%s [%s{%d,%d}]", 112 finding.getMessage(), 113 finding.getDocumentUri().toString(), 114 ex.getLineNumber(), 115 ex.getColumnNumber())); 116 } 117 118 private void handleConstraintValidationFinding(@NonNull ConstraintValidationFinding finding) { 119 Ansi ansi = generatePreamble(finding.getSeverity()); 120 121 getLogger(finding).log( 122 ansi.format("[%s] %s", finding.getNode().getMetapath(), finding.getMessage())); 123 } 124 125 @NonNull 126 private LogBuilder getLogger(@NonNull IValidationFinding finding) { 127 LogBuilder retval; 128 switch (finding.getSeverity()) { 129 case CRITICAL: 130 retval = LOGGER.atFatal(); 131 break; 132 case ERROR: 133 retval = LOGGER.atError(); 134 break; 135 case WARNING: 136 retval = LOGGER.atWarn(); 137 break; 138 case INFORMATIONAL: 139 retval = LOGGER.atInfo(); 140 break; 141 default: 142 throw new IllegalArgumentException("Unknown level: " + finding.getSeverity().name()); 143 } 144 145 assert retval != null; 146 147 if (finding.getCause() != null && isLogExceptions()) { 148 retval.withThrowable(finding.getCause()); 149 } 150 151 return retval; 152 } 153 154 @SuppressWarnings("static-method") 155 @NonNull 156 private Ansi generatePreamble(@NonNull Level level) { 157 Ansi ansi = ansi().fgBright(Color.WHITE).a('[').reset(); 158 159 switch (level) { 160 case CRITICAL: 161 ansi = ansi.fgRed().a("CRITICAL").reset(); 162 break; 163 case ERROR: 164 ansi = ansi.fgBrightRed().a("ERROR").reset(); 165 break; 166 case WARNING: 167 ansi = ansi.fgBrightYellow().a("WARNING").reset(); 168 break; 169 case INFORMATIONAL: 170 ansi = ansi.fgBrightBlue().a("INFO").reset(); 171 break; 172 default: 173 ansi = ansi().fgBright(Color.MAGENTA).a(level.name()).reset(); 174 break; 175 } 176 ansi = ansi.fgBright(Color.WHITE).a("] ").reset(); 177 178 assert ansi != null; 179 return ansi; 180 } 181}