View Javadoc
1   /*
2    * Portions of this software was developed by employees of the National Institute
3    * of Standards and Technology (NIST), an agency of the Federal Government and is
4    * being made available as a public service. Pursuant to title 17 United States
5    * Code Section 105, works of NIST employees are not subject to copyright
6    * protection in the United States. This software may be subject to foreign
7    * copyright. Permission in the United States and in foreign countries, to the
8    * extent that NIST may hold copyright, to use, copy, modify, create derivative
9    * works, and distribute this software and its documentation without fee is hereby
10   * granted on a non-exclusive basis, provided that this notice and disclaimer
11   * of warranty appears in all copies.
12   *
13   * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14   * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15   * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17   * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18   * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
19   * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20   * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21   * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22   * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23   * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24   * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25   */
26  
27  package gov.nist.secauto.metaschema.cli.util;
28  
29  import static org.fusesource.jansi.Ansi.ansi;
30  
31  import gov.nist.secauto.metaschema.core.model.constraint.ConstraintValidationFinding;
32  import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level;
33  import gov.nist.secauto.metaschema.core.model.validation.IValidationFinding;
34  import gov.nist.secauto.metaschema.core.model.validation.IValidationResult;
35  import gov.nist.secauto.metaschema.core.model.validation.JsonSchemaContentValidator.JsonValidationFinding;
36  import gov.nist.secauto.metaschema.core.model.validation.XmlSchemaContentValidator.XmlValidationFinding;
37  
38  import org.apache.logging.log4j.LogBuilder;
39  import org.apache.logging.log4j.LogManager;
40  import org.apache.logging.log4j.Logger;
41  import org.fusesource.jansi.Ansi;
42  import org.fusesource.jansi.Ansi.Color;
43  import org.xml.sax.SAXParseException;
44  
45  import java.util.List;
46  
47  import edu.umd.cs.findbugs.annotations.NonNull;
48  
49  public final class LoggingValidationHandler {
50    private static final Logger LOGGER = LogManager.getLogger(LoggingValidationHandler.class);
51  
52    private static final LoggingValidationHandler NO_LOG_EXCPTION_INSTANCE = new LoggingValidationHandler(false);
53    private static final LoggingValidationHandler LOG_EXCPTION_INSTANCE = new LoggingValidationHandler(true);
54  
55    private final boolean logExceptions;
56  
57    public static LoggingValidationHandler instance() {
58      return instance(false);
59    }
60  
61    public static LoggingValidationHandler instance(boolean logExceptions) {
62      return logExceptions ? LOG_EXCPTION_INSTANCE : NO_LOG_EXCPTION_INSTANCE;
63    }
64  
65    protected LoggingValidationHandler(boolean logExceptions) {
66      this.logExceptions = logExceptions;
67    }
68  
69    public boolean isLogExceptions() {
70      return logExceptions;
71    }
72  
73    public boolean handleValidationResults(IValidationResult result) {
74      handleValidationFindings(result.getFindings());
75      return result.isPassing();
76    }
77  
78    public void handleValidationFindings(@NonNull List<? extends IValidationFinding> findings) {
79      for (IValidationFinding finding : findings) {
80        if (finding instanceof JsonValidationFinding) {
81          handleJsonValidationFinding((JsonValidationFinding) finding);
82        } else if (finding instanceof XmlValidationFinding) {
83          handleXmlValidationFinding((XmlValidationFinding) finding);
84        } else if (finding instanceof ConstraintValidationFinding) {
85          handleConstraintValidationFinding((ConstraintValidationFinding) finding);
86        } else {
87          throw new IllegalStateException();
88        }
89      }
90    }
91  
92    private void handleJsonValidationFinding(@NonNull JsonValidationFinding finding) {
93      Ansi ansi = generatePreamble(finding.getSeverity());
94  
95      getLogger(finding).log(
96          ansi.a('[')
97              .fgBright(Color.WHITE)
98              .a(finding.getCause().getPointerToViolation())
99              .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 }