1
2
3
4
5
6 package dev.metaschema.core.model.validation;
7
8 import org.everit.json.schema.Schema;
9 import org.everit.json.schema.ValidationException;
10 import org.everit.json.schema.loader.SchemaLoader;
11 import org.json.JSONException;
12 import org.json.JSONObject;
13 import org.json.JSONTokener;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.Reader;
18 import java.net.URI;
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.stream.Collectors;
23 import java.util.stream.Stream;
24
25 import dev.metaschema.core.model.IResourceLocation;
26 import dev.metaschema.core.model.constraint.IConstraint;
27 import dev.metaschema.core.util.ObjectUtils;
28 import edu.umd.cs.findbugs.annotations.NonNull;
29
30
31
32
33 public class JsonSchemaContentValidator
34 extends AbstractContentValidator {
35 @NonNull
36 private final Schema schema;
37
38
39
40
41
42
43
44
45 public JsonSchemaContentValidator(@NonNull Reader reader) {
46 this(new JSONTokener(reader));
47 }
48
49
50
51
52
53
54
55
56 public JsonSchemaContentValidator(@NonNull InputStream is) {
57 this(new JSONTokener(is));
58 }
59
60
61
62
63
64
65
66
67 public JsonSchemaContentValidator(@NonNull JSONObject jsonSchema) {
68 this(ObjectUtils.notNull(SchemaLoader.load(jsonSchema)));
69 }
70
71
72
73
74
75
76
77
78 protected JsonSchemaContentValidator(@NonNull JSONTokener tokenizer) {
79 this(new JSONObject(tokenizer));
80 }
81
82
83
84
85
86
87
88 protected JsonSchemaContentValidator(@NonNull Schema schema) {
89 this.schema = ObjectUtils.requireNonNull(schema, "schema");
90 }
91
92 @Override
93 public IValidationResult validate(InputStream is, URI resourceUri) throws IOException {
94 JSONObject json;
95 try {
96 json = new JSONObject(new JSONTokener(is));
97 } catch (JSONException ex) {
98 throw new IOException(String.format("Unable to parse JSON from '%s'", resourceUri), ex);
99 }
100 return validate(json, resourceUri);
101 }
102
103
104
105
106
107
108
109
110
111
112 @SuppressWarnings("null")
113 @NonNull
114 public IValidationResult validate(@NonNull JSONObject json, @NonNull URI resourceUri) {
115 IValidationResult retval;
116 try {
117 schema.validate(json);
118 retval = IValidationResult.PASSING_RESULT;
119 } catch (ValidationException ex) {
120 retval = new JsonValidationResult(handleValidationException(ex, resourceUri).collect(Collectors.toList()));
121 }
122
123 return retval;
124 }
125
126
127
128
129
130
131
132
133
134
135
136 @SuppressWarnings("null")
137 @NonNull
138 protected Stream<JsonValidationFinding> handleValidationException(
139 @NonNull ValidationException exception,
140 @NonNull URI resourceUri) {
141 JsonValidationFinding finding = new JsonValidationFinding(exception, resourceUri);
142 Stream<JsonValidationFinding> childFindings = exception.getCausingExceptions().stream()
143 .flatMap(ex -> handleValidationException(ex, resourceUri));
144 return Stream.concat(Stream.of(finding), childFindings);
145 }
146
147
148
149
150
151 public static class JsonValidationFinding implements IValidationFinding {
152 @NonNull
153 private final ValidationException exception;
154 @NonNull
155 private final URI documentUri;
156
157
158
159
160
161
162
163
164
165
166
167 public JsonValidationFinding(
168 @NonNull ValidationException exception,
169 @NonNull URI resourceUri) {
170 this.exception = ObjectUtils.requireNonNull(exception, "exception");
171 this.documentUri = ObjectUtils.requireNonNull(resourceUri, "documentUri");
172 }
173
174 @Override
175 public String getIdentifier() {
176
177 return null;
178 }
179
180 @Override
181 public Kind getKind() {
182 return IValidationFinding.Kind.FAIL;
183 }
184
185 @Override
186 public IConstraint.Level getSeverity() {
187 return IConstraint.Level.CRITICAL;
188 }
189
190 @Override
191 public URI getDocumentUri() {
192 return documentUri;
193 }
194
195 @Override
196 public IResourceLocation getLocation() {
197
198 return null;
199 }
200
201 @Override
202 public String getPathKind() {
203 return "JSON-pointer";
204 }
205
206 @Override
207 public String getPath() {
208 return getCause().getPointerToViolation();
209 }
210
211 @Override
212 public String getMessage() {
213 return getCause().getLocalizedMessage();
214 }
215
216 @NonNull
217 @Override
218 public ValidationException getCause() {
219 return exception;
220 }
221 }
222
223 private static class JsonValidationResult implements IValidationResult {
224 @NonNull
225 private final List<JsonValidationFinding> findings;
226
227 @SuppressWarnings("null")
228 public JsonValidationResult(@NonNull List<JsonValidationFinding> findings) {
229 this.findings = Collections.unmodifiableList(Objects.requireNonNull(findings, "findings"));
230 }
231
232 @Override
233 public IConstraint.Level getHighestSeverity() {
234 return findings.isEmpty() ? IConstraint.Level.INFORMATIONAL : IConstraint.Level.ERROR;
235 }
236
237 @Override
238 public List<? extends IValidationFinding> getFindings() {
239 return findings;
240 }
241
242 }
243 }