Skip to content

Commit 23ccf44

Browse files
committed
finished reworking ObjectSchema to new ValidationException handling (might need further improvements)
1 parent 27a0a87 commit 23ccf44

File tree

6 files changed

+215
-80
lines changed

6 files changed

+215
-80
lines changed

core/src/main/java/org/everit/json/schema/ObjectSchema.java

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -306,78 +306,94 @@ private List<ValidationException> testPatternProperties(final JSONObject subject
306306
for (String propName : propNames) {
307307
if (entry.getKey().matcher(propName).find()) {
308308
ifFails(entry.getValue(), subject.get(propName))
309-
.map(exc -> exc.prepend(propName, exc.getViolatedSchema()))
309+
.map(exc -> exc.prepend(propName))
310310
.ifPresent(rval::add);
311311
}
312312
}
313313
}
314314
return rval;
315315
}
316316

317-
private void testProperties(final JSONObject subject) {
317+
private List<ValidationException> testProperties(final JSONObject subject) {
318318
if (propertySchemas != null) {
319+
List<ValidationException> rval = new ArrayList<>();
319320
for (Entry<String, Schema> entry : propertySchemas.entrySet()) {
320321
String key = entry.getKey();
321322
if (subject.has(key)) {
322-
entry.getValue().validate(subject.get(key));
323+
ifFails(entry.getValue(), subject.get(key))
324+
.map(exc -> exc.prepend(key))
325+
.ifPresent(rval::add);
323326
}
324327
}
328+
return rval;
325329
}
330+
return Collections.emptyList();
326331
}
327332

328-
private void testPropertyDependencies(final JSONObject subject) {
329-
propertyDependencies.keySet().stream()
330-
.filter(subject::has)
331-
.flatMap(ifPresent -> propertyDependencies.get(ifPresent).stream())
332-
.filter(mustBePresent -> !subject.has(mustBePresent))
333-
.findFirst()
334-
.ifPresent(missing -> failure("property [%s] is required", missing));
333+
private List<ValidationException> testPropertyDependencies(final JSONObject subject) {
334+
return propertyDependencies.keySet().stream()
335+
.filter(subject::has)
336+
.flatMap(ifPresent -> propertyDependencies.get(ifPresent).stream())
337+
.filter(mustBePresent -> !subject.has(mustBePresent))
338+
.map(missingKey -> String.format("property [%s] is required", missingKey))
339+
.map(excMessage -> new ValidationException(this, excMessage))
340+
.collect(Collectors.toList());
335341
}
336342

337-
private void testRequiredProperties(final JSONObject subject) {
338-
requiredProperties.stream()
339-
.filter(key -> !subject.has(key))
340-
.findFirst()
341-
.ifPresent(missing -> failure("required key [%s] not found", missing));
343+
private List<ValidationException> testRequiredProperties(final JSONObject subject) {
344+
return requiredProperties.stream()
345+
.filter(key -> !subject.has(key))
346+
.map(missingKey -> String.format("required key [%s] not found", missingKey))
347+
.map(excMessage -> new ValidationException(this, excMessage))
348+
.collect(Collectors.toList());
342349
}
343350

344-
private void testSchemaDependencies(final JSONObject subject) {
345-
schemaDependencies.keySet().stream()
346-
.filter(subject::has)
347-
.map(schemaDependencies::get)
348-
.forEach(schema -> schema.validate(subject));
351+
private List<ValidationException> testSchemaDependencies(final JSONObject subject) {
352+
List<ValidationException> rval = new ArrayList<>();
353+
for (Map.Entry<String, Schema> schemaDep : schemaDependencies.entrySet()) {
354+
String propName = schemaDep.getKey();
355+
if (subject.has(propName)) {
356+
ifFails(schemaDep.getValue(), subject).ifPresent(rval::add);
357+
}
358+
}
359+
return rval;
349360
}
350361

351-
private void testSize(final JSONObject subject) {
362+
private List<ValidationException> testSize(final JSONObject subject) {
352363
int actualSize = subject.length();
353364
if (minProperties != null && actualSize < minProperties.intValue()) {
354-
throw new ValidationException(this, String.format("minimum size: [%d], found: [%d]",
355-
minProperties, actualSize));
365+
return Arrays
366+
.asList(new ValidationException(this, String.format("minimum size: [%d], found: [%d]",
367+
minProperties, actualSize)));
356368
}
357369
if (maxProperties != null && actualSize > maxProperties.intValue()) {
358-
throw new ValidationException(this, String.format("maximum size: [%d], found: [%d]",
359-
maxProperties, actualSize));
370+
return Arrays
371+
.asList(new ValidationException(this, String.format("maximum size: [%d], found: [%d]",
372+
maxProperties, actualSize)));
360373
}
374+
return Collections.emptyList();
361375
}
362376

363377
@Override
364378
public void validate(final Object subject) {
365379
if (!(subject instanceof JSONObject)) {
366380
if (requiresObject) {
367-
throw new ValidationException(JSONObject.class, subject);
381+
throw new ValidationException(this, JSONObject.class, subject);
368382
}
369383
} else {
370384
List<ValidationException> failures = new ArrayList<>();
371385
JSONObject objSubject = (JSONObject) subject;
372-
testProperties(objSubject);
373-
testRequiredProperties(objSubject);
386+
failures.addAll(testProperties(objSubject));
387+
failures.addAll(testRequiredProperties(objSubject));
374388
failures.addAll(testAdditionalProperties(objSubject));
375-
testSize(objSubject);
376-
testPropertyDependencies(objSubject);
377-
testSchemaDependencies(objSubject);
389+
failures.addAll(testSize(objSubject));
390+
failures.addAll(testPropertyDependencies(objSubject));
391+
failures.addAll(testSchemaDependencies(objSubject));
378392
failures.addAll(testPatternProperties(objSubject));
379393
if (failures.size() == 1) {
380394
throw failures.get(0);
395+
} else if (failures.size() > 1) {
396+
throw ValidationException.multipleFailures(this, failures);
381397
}
382398
}
383399
}

core/src/main/java/org/everit/json/schema/ValidationException.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package org.everit.json.schema;
1717

18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
1821
import java.util.Objects;
1922

2023
/**
@@ -23,10 +26,17 @@
2326
public class ValidationException extends RuntimeException {
2427
private static final long serialVersionUID = 6192047123024651924L;
2528

29+
public static final ValidationException multipleFailures(final Schema rootFailingSchema,
30+
final List<ValidationException> causingExceptions) {
31+
return new ValidationException(rootFailingSchema, new ArrayList<>(causingExceptions));
32+
}
33+
2634
private final StringBuilder pointerToViolation;
2735

2836
private final Schema violatedSchema;
2937

38+
private final List<ValidationException> causingExceptions;
39+
3040
/**
3141
* Deprecated, use {@code ValidationException(Schema, Class<?>, Object)} instead.
3242
*
@@ -40,14 +50,30 @@ public ValidationException(final Class<?> expectedType, final Object actualValue
4050

4151
public ValidationException(final Schema violatedSchema, final Class<?> expectedType,
4252
final Object actualValue) {
43-
this(violatedSchema, "expected type: " + expectedType.getSimpleName() + ", found: "
44-
+ (actualValue == null ? "null" : actualValue.getClass().getSimpleName()));
53+
this(violatedSchema, new StringBuilder("#"),
54+
"expected type: " + expectedType.getSimpleName() + ", found: "
55+
+ (actualValue == null ? "null" : actualValue.getClass().getSimpleName()),
56+
Collections.emptyList());
57+
}
58+
59+
private ValidationException(final Schema rootFailingSchema,
60+
final List<ValidationException> causingExceptions) {
61+
this(rootFailingSchema, new StringBuilder("#"),
62+
causingExceptions.size() + " schema violations found",
63+
causingExceptions);
4564
}
4665

4766
public ValidationException(final Schema violatedSchema, final String message) {
67+
this(violatedSchema, new StringBuilder("#"), message, Collections.emptyList());
68+
}
69+
70+
public ValidationException(final Schema violatedSchema, final StringBuilder pointerToViolation,
71+
final String message,
72+
final List<ValidationException> causingExceptions) {
4873
super(message);
4974
this.violatedSchema = violatedSchema;
50-
this.pointerToViolation = new StringBuilder("#");
75+
this.pointerToViolation = pointerToViolation;
76+
this.causingExceptions = Collections.unmodifiableList(causingExceptions);
5177
}
5278

5379
/**
@@ -58,15 +84,17 @@ public ValidationException(final Schema violatedSchema, final String message) {
5884
*/
5985
@Deprecated
6086
public ValidationException(final String message) {
61-
this((Schema) null, message);
87+
this((Schema) null, new StringBuilder("#"), message, Collections.emptyList());
6288
}
6389

6490
private ValidationException(final StringBuilder pointerToViolation,
6591
final Schema violatedSchema,
6692
final ValidationException original) {
67-
super(original.getMessage());
68-
this.violatedSchema = violatedSchema;
69-
this.pointerToViolation = pointerToViolation;
93+
this(violatedSchema, pointerToViolation, original.getMessage(), original.causingExceptions);
94+
}
95+
96+
public List<ValidationException> getCausingExceptions() {
97+
return causingExceptions;
7098
}
7199

72100
public String getPointerToViolation() {
@@ -77,10 +105,14 @@ public Schema getViolatedSchema() {
77105
return violatedSchema;
78106
}
79107

80-
public ValidationException prepend(final String fragment, final Schema violatingSchema) {
108+
public ValidationException prepend(final String fragment) {
109+
return prepend(fragment, this.violatedSchema);
110+
}
111+
112+
public ValidationException prepend(final String fragment, final Schema violatedSchema) {
81113
Objects.requireNonNull(fragment, "fragment cannot be null");
82-
return new ValidationException(this.pointerToViolation.insert(1, '/').insert(2, fragment),
83-
violatingSchema, this);
114+
StringBuilder newPointer = this.pointerToViolation.insert(1, '/').insert(2, fragment);
115+
return new ValidationException(newPointer, violatedSchema, this);
84116
}
85117

86118
}

0 commit comments

Comments
 (0)