XmlSchemaContentValidator.java

  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. package gov.nist.secauto.metaschema.core.model.validation;

  27. import gov.nist.secauto.metaschema.core.model.constraint.IConstraint.Level;
  28. import gov.nist.secauto.metaschema.core.util.ObjectUtils;

  29. import org.xml.sax.ErrorHandler;
  30. import org.xml.sax.SAXException;
  31. import org.xml.sax.SAXParseException;

  32. import java.io.IOException;
  33. import java.io.InputStream;
  34. import java.net.URI;
  35. import java.util.Collections;
  36. import java.util.LinkedList;
  37. import java.util.List;

  38. import javax.xml.XMLConstants;
  39. import javax.xml.transform.Source;
  40. import javax.xml.transform.stream.StreamSource;
  41. import javax.xml.validation.Schema;
  42. import javax.xml.validation.SchemaFactory;
  43. import javax.xml.validation.Validator;

  44. import edu.umd.cs.findbugs.annotations.NonNull;

  45. public class XmlSchemaContentValidator
  46.     extends AbstractContentValidator {
  47.   private final Schema schema;

  48.   @SuppressWarnings("null")
  49.   @NonNull
  50.   private static Schema toSchema(@NonNull List<? extends Source> schemaSources) throws SAXException {
  51.     SchemaFactory schemafactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
  52.     // schemafactory.setResourceResolver(new ClasspathResourceResolver());
  53.     Schema retval;
  54.     if (schemaSources.isEmpty()) {
  55.       retval = schemafactory.newSchema();
  56.     } else {
  57.       retval = schemafactory.newSchema(schemaSources.toArray(new Source[0]));
  58.     }

  59.     // TODO verify source input streams are closed
  60.     return retval;
  61.   }

  62.   public XmlSchemaContentValidator(@NonNull List<? extends Source> schemaSources) throws SAXException {
  63.     this(toSchema(ObjectUtils.requireNonNull(schemaSources, "schemaSources")));
  64.   }

  65.   protected XmlSchemaContentValidator(@NonNull Schema schema) {
  66.     this.schema = ObjectUtils.requireNonNull(schema, "schema");
  67.   }

  68.   public Schema getSchema() {
  69.     return schema;
  70.   }

  71.   @Override
  72.   public IValidationResult validate(InputStream is, URI documentUri) throws IOException {
  73.     Source xmlSource = new StreamSource(is, documentUri.toASCIIString());

  74.     Validator validator = schema.newValidator();
  75.     XmlValidationErrorHandler errorHandler = new XmlValidationErrorHandler(documentUri);
  76.     validator.setErrorHandler(errorHandler);
  77.     try {
  78.       validator.validate(xmlSource);
  79.     } catch (SAXException ex) {
  80.       throw new IOException(String.format("Unexpected failure during validation of '%s'", documentUri), ex);
  81.     }
  82.     return errorHandler;
  83.   }

  84.   public static class XmlValidationFinding implements IValidationFinding {
  85.     @NonNull
  86.     private final URI documentUri;
  87.     @NonNull
  88.     private final SAXParseException exception;
  89.     @NonNull
  90.     private final Level severity;

  91.     public XmlValidationFinding(@NonNull Level severity, @NonNull SAXParseException exception,
  92.         @NonNull URI documentUri) {
  93.       this.documentUri = ObjectUtils.requireNonNull(documentUri, "documentUri");
  94.       this.exception = ObjectUtils.requireNonNull(exception, "exception");
  95.       this.severity = ObjectUtils.requireNonNull(severity, "severity");
  96.     }

  97.     @Override
  98.     public Level getSeverity() {
  99.       return severity;
  100.     }

  101.     @SuppressWarnings("null")
  102.     @Override
  103.     public URI getDocumentUri() {
  104.       String systemId = getCause().getSystemId();
  105.       return systemId == null ? documentUri : URI.create(systemId);
  106.     }

  107.     @SuppressWarnings("null")
  108.     @Override
  109.     public String getMessage() {
  110.       return getCause().getLocalizedMessage();
  111.     }

  112.     @NonNull
  113.     @Override
  114.     public SAXParseException getCause() {
  115.       return exception;
  116.     }
  117.   }

  118.   private static class XmlValidationErrorHandler implements ErrorHandler, IValidationResult {
  119.     @NonNull
  120.     private final URI documentUri;
  121.     @NonNull
  122.     private final List<XmlValidationFinding> findings = new LinkedList<>();
  123.     @NonNull
  124.     private Level highestSeverity = Level.INFORMATIONAL;

  125.     public XmlValidationErrorHandler(@NonNull URI documentUri) {
  126.       this.documentUri = ObjectUtils.requireNonNull(documentUri, "documentUri");
  127.     }

  128.     @NonNull
  129.     public URI getDocumentUri() {
  130.       return documentUri;
  131.     }

  132.     private void adjustHighestSeverity(@NonNull Level severity) {
  133.       if (highestSeverity.ordinal() < severity.ordinal()) {
  134.         highestSeverity = severity;
  135.       }
  136.     }

  137.     @SuppressWarnings("null")
  138.     @Override
  139.     public void warning(SAXParseException ex) throws SAXException {
  140.       findings.add(new XmlValidationFinding(Level.WARNING, ex, getDocumentUri()));
  141.       adjustHighestSeverity(Level.WARNING);
  142.     }

  143.     @SuppressWarnings("null")
  144.     @Override
  145.     public void error(SAXParseException ex) throws SAXException {
  146.       findings.add(new XmlValidationFinding(Level.ERROR, ex, getDocumentUri()));
  147.       adjustHighestSeverity(Level.ERROR);
  148.     }

  149.     @SuppressWarnings("null")
  150.     @Override
  151.     public void fatalError(SAXParseException ex) throws SAXException {
  152.       findings.add(new XmlValidationFinding(Level.CRITICAL, ex, getDocumentUri()));
  153.       adjustHighestSeverity(Level.CRITICAL);
  154.     }

  155.     @SuppressWarnings("null")
  156.     @Override
  157.     @NonNull
  158.     public List<XmlValidationFinding> getFindings() {
  159.       return Collections.unmodifiableList(findings);
  160.     }

  161.     @Override
  162.     public Level getHighestSeverity() {
  163.       return highestSeverity;
  164.     }
  165.   }
  166. }