1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.metaschema.core.model.validation;
28
29 import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
30 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
31
32 import org.everit.json.schema.Schema;
33 import org.everit.json.schema.ValidationException;
34 import org.everit.json.schema.loader.SchemaLoader;
35 import org.json.JSONException;
36 import org.json.JSONObject;
37 import org.json.JSONTokener;
38
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.Reader;
42 import java.net.URI;
43 import java.util.Collections;
44 import java.util.List;
45 import java.util.Objects;
46 import java.util.stream.Collectors;
47 import java.util.stream.Stream;
48
49 import edu.umd.cs.findbugs.annotations.NonNull;
50
51 public class JsonSchemaContentValidator
52 extends AbstractContentValidator {
53 @NonNull
54 private final Schema schema;
55
56 public JsonSchemaContentValidator(@NonNull Reader reader) {
57 this(new JSONTokener(reader));
58 }
59
60 public JsonSchemaContentValidator(@NonNull InputStream is) {
61 this(new JSONTokener(is));
62 }
63
64 public JsonSchemaContentValidator(@NonNull JSONObject jsonSchema) {
65 this(ObjectUtils.notNull(SchemaLoader.load(jsonSchema)));
66 }
67
68 protected JsonSchemaContentValidator(@NonNull JSONTokener tokenizer) {
69 this(new JSONObject(tokenizer));
70 }
71
72 protected JsonSchemaContentValidator(@NonNull Schema schema) {
73 this.schema = ObjectUtils.requireNonNull(schema, "schema");
74 }
75
76 @Override
77 public IValidationResult validate(InputStream is, URI documentUri) throws IOException {
78 JSONObject json;
79 try {
80 json = new JSONObject(new JSONTokener(is));
81 } catch (JSONException ex) {
82 throw new IOException(String.format("Unable to parse JSON from '%s'", documentUri), ex);
83 }
84 return validate(json, documentUri);
85 }
86
87 @SuppressWarnings("null")
88 @NonNull
89 public IValidationResult validate(@NonNull JSONObject json, @NonNull URI documentUri) {
90 IValidationResult retval;
91 try {
92 schema.validate(json);
93 retval = IValidationResult.PASSING_RESULT;
94 } catch (ValidationException ex) {
95 retval = new JsonValidationResult(
96 handleValidationException(ex, documentUri)
97 .collect(Collectors.toList()));
98 }
99
100 return retval;
101 }
102
103 @SuppressWarnings("null")
104 @NonNull
105 protected Stream<JsonValidationFinding> handleValidationException(@NonNull ValidationException ex,
106 @NonNull URI documentUri) {
107 JsonValidationFinding finding = new JsonValidationFinding(ex, documentUri);
108 Stream<JsonValidationFinding> childFindings = ex.getCausingExceptions().stream()
109 .flatMap(exception -> {
110 return handleValidationException(exception, documentUri);
111 });
112 return Stream.concat(Stream.of(finding), childFindings);
113 }
114
115 public static class JsonValidationFinding implements IValidationFinding {
116 @NonNull
117 private final ValidationException exception;
118 @NonNull
119 private final URI documentUri;
120
121 public JsonValidationFinding(@NonNull ValidationException exception, @NonNull URI documentUri) {
122 this.exception = ObjectUtils.requireNonNull(exception, "exception");
123 this.documentUri = ObjectUtils.requireNonNull(documentUri, "documentUri");
124 }
125
126 @Override
127 public IConstraint.Level getSeverity() {
128 return IConstraint.Level.ERROR;
129 }
130
131 @Override
132 public URI getDocumentUri() {
133 return documentUri;
134 }
135
136 @SuppressWarnings("null")
137 @Override
138 public String getMessage() {
139 return getCause().getLocalizedMessage();
140 }
141
142 @NonNull
143 @Override
144 public ValidationException getCause() {
145 return exception;
146 }
147 }
148
149 private static class JsonValidationResult implements IValidationResult {
150 @NonNull
151 private final List<JsonValidationFinding> findings;
152
153 @SuppressWarnings("null")
154 public JsonValidationResult(@NonNull List<JsonValidationFinding> findings) {
155 this.findings = Collections.unmodifiableList(Objects.requireNonNull(findings, "findings"));
156 }
157
158 @Override
159 public IConstraint.Level getHighestSeverity() {
160 return findings.isEmpty() ? IConstraint.Level.INFORMATIONAL : IConstraint.Level.ERROR;
161 }
162
163 @Override
164 public List<? extends IValidationFinding> getFindings() {
165 return findings;
166 }
167
168 }
169 }