JsonSchemaContentValidator.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;
  28. import gov.nist.secauto.metaschema.core.util.ObjectUtils;

  29. import org.everit.json.schema.Schema;
  30. import org.everit.json.schema.ValidationException;
  31. import org.everit.json.schema.loader.SchemaLoader;
  32. import org.json.JSONException;
  33. import org.json.JSONObject;
  34. import org.json.JSONTokener;

  35. import java.io.IOException;
  36. import java.io.InputStream;
  37. import java.io.Reader;
  38. import java.net.URI;
  39. import java.util.Collections;
  40. import java.util.List;
  41. import java.util.Objects;
  42. import java.util.stream.Collectors;
  43. import java.util.stream.Stream;

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

  45. public class JsonSchemaContentValidator
  46.     extends AbstractContentValidator {
  47.   @NonNull
  48.   private final Schema schema;

  49.   public JsonSchemaContentValidator(@NonNull Reader reader) {
  50.     this(new JSONTokener(reader));
  51.   }

  52.   public JsonSchemaContentValidator(@NonNull InputStream is) {
  53.     this(new JSONTokener(is));
  54.   }

  55.   public JsonSchemaContentValidator(@NonNull JSONObject jsonSchema) {
  56.     this(ObjectUtils.notNull(SchemaLoader.load(jsonSchema)));
  57.   }

  58.   protected JsonSchemaContentValidator(@NonNull JSONTokener tokenizer) {
  59.     this(new JSONObject(tokenizer));
  60.   }

  61.   protected JsonSchemaContentValidator(@NonNull Schema schema) {
  62.     this.schema = ObjectUtils.requireNonNull(schema, "schema");
  63.   }

  64.   @Override
  65.   public IValidationResult validate(InputStream is, URI documentUri) throws IOException {
  66.     JSONObject json;
  67.     try {
  68.       json = new JSONObject(new JSONTokener(is));
  69.     } catch (JSONException ex) {
  70.       throw new IOException(String.format("Unable to parse JSON from '%s'", documentUri), ex);
  71.     }
  72.     return validate(json, documentUri);
  73.   }

  74.   @SuppressWarnings("null")
  75.   @NonNull
  76.   public IValidationResult validate(@NonNull JSONObject json, @NonNull URI documentUri) {
  77.     IValidationResult retval;
  78.     try {
  79.       schema.validate(json);
  80.       retval = IValidationResult.PASSING_RESULT;
  81.     } catch (ValidationException ex) {
  82.       retval = new JsonValidationResult(
  83.           handleValidationException(ex, documentUri)
  84.               .collect(Collectors.toList()));
  85.     }

  86.     return retval;
  87.   }

  88.   @SuppressWarnings("null")
  89.   @NonNull
  90.   protected Stream<JsonValidationFinding> handleValidationException(@NonNull ValidationException ex,
  91.       @NonNull URI documentUri) {
  92.     JsonValidationFinding finding = new JsonValidationFinding(ex, documentUri);
  93.     Stream<JsonValidationFinding> childFindings = ex.getCausingExceptions().stream()
  94.         .flatMap(exception -> {
  95.           return handleValidationException(exception, documentUri);
  96.         });
  97.     return Stream.concat(Stream.of(finding), childFindings);
  98.   }

  99.   public static class JsonValidationFinding implements IValidationFinding {
  100.     @NonNull
  101.     private final ValidationException exception;
  102.     @NonNull
  103.     private final URI documentUri;

  104.     public JsonValidationFinding(@NonNull ValidationException exception, @NonNull URI documentUri) {
  105.       this.exception = ObjectUtils.requireNonNull(exception, "exception");
  106.       this.documentUri = ObjectUtils.requireNonNull(documentUri, "documentUri");
  107.     }

  108.     @Override
  109.     public IConstraint.Level getSeverity() {
  110.       return IConstraint.Level.ERROR;
  111.     }

  112.     @Override
  113.     public URI getDocumentUri() {
  114.       return documentUri;
  115.     }

  116.     @SuppressWarnings("null")
  117.     @Override
  118.     public String getMessage() {
  119.       return getCause().getLocalizedMessage();
  120.     }

  121.     @NonNull
  122.     @Override
  123.     public ValidationException getCause() {
  124.       return exception;
  125.     }
  126.   }

  127.   private static class JsonValidationResult implements IValidationResult {
  128.     @NonNull
  129.     private final List<JsonValidationFinding> findings;

  130.     @SuppressWarnings("null")
  131.     public JsonValidationResult(@NonNull List<JsonValidationFinding> findings) {
  132.       this.findings = Collections.unmodifiableList(Objects.requireNonNull(findings, "findings"));
  133.     }

  134.     @Override
  135.     public IConstraint.Level getHighestSeverity() {
  136.       return findings.isEmpty() ? IConstraint.Level.INFORMATIONAL : IConstraint.Level.ERROR;
  137.     }

  138.     @Override
  139.     public List<? extends IValidationFinding> getFindings() {
  140.       return findings;
  141.     }

  142.   }
  143. }