Skip to content

Commit d4d699e

Browse files
authored
Merge pull request #248 from erosb/VisitorListener
Improved ValidationListener
2 parents 802e65c + e450a3f commit d4d699e

38 files changed

+1036
-43
lines changed

core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@
191191
<dependency>
192192
<groupId>nl.jqno.equalsverifier</groupId>
193193
<artifactId>equalsverifier</artifactId>
194-
<version>2.3.1</version>
194+
<version>3.0.3</version>
195195
<scope>test</scope>
196196
</dependency>
197197
<dependency>

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.Optional;
44

5+
import org.everit.json.schema.internal.JSONPrinter;
6+
57
/**
68
* Validator for {@code if}, {@code then}, {@code else} schemas.
79
*/
@@ -68,4 +70,18 @@ void accept(Visitor visitor) {
6870
visitor.visitConditionalSchema(this);
6971
}
7072

73+
@Override void describePropertiesTo(JSONPrinter writer) {
74+
if (ifSchema != null) {
75+
writer.key("if");
76+
ifSchema.describeTo(writer);
77+
}
78+
if (thenSchema != null) {
79+
writer.key("then");
80+
thenSchema.describeTo(writer);
81+
}
82+
if (elseSchema != null) {
83+
writer.key("else");
84+
elseSchema.describeTo(writer);
85+
}
86+
}
7187
}

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

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package org.everit.json.schema;
22

3-
4-
import java.util.Arrays;
5-
3+
import static java.util.Arrays.asList;
64
import static java.util.Objects.requireNonNull;
5+
import static org.everit.json.schema.event.ConditionalSchemaValidationEvent.Keyword.ELSE;
6+
import static org.everit.json.schema.event.ConditionalSchemaValidationEvent.Keyword.IF;
7+
import static org.everit.json.schema.event.ConditionalSchemaValidationEvent.Keyword.THEN;
8+
9+
import org.everit.json.schema.event.ConditionalSchemaMatchEvent;
10+
import org.everit.json.schema.event.ConditionalSchemaMismatchEvent;
11+
import org.everit.json.schema.event.ConditionalSchemaValidationEvent;
712

813
class ConditionalSchemaValidatingVisitor extends Visitor {
914

@@ -15,7 +20,7 @@ class ConditionalSchemaValidatingVisitor extends Visitor {
1520

1621
private ValidationException ifSchemaException;
1722

18-
public ConditionalSchemaValidatingVisitor(Object subject, ValidatingVisitor owner) {
23+
ConditionalSchemaValidatingVisitor(Object subject, ValidatingVisitor owner) {
1924
this.subject = subject;
2025
this.owner = requireNonNull(owner, "owner cannot be null");
2126
}
@@ -34,6 +39,11 @@ void visitConditionalSchema(ConditionalSchema conditionalSchema) {
3439
void visitIfSchema(Schema ifSchema) {
3540
if (conditionalSchema.getIfSchema().isPresent()) {
3641
ifSchemaException = owner.getFailureOfSchema(ifSchema, subject);
42+
if (ifSchemaException == null) {
43+
owner.validationListener.ifSchemaMatch(createMatchEvent(IF));
44+
} else {
45+
owner.validationListener.ifSchemaMismatch(createMismatchEvent(IF, ifSchemaException));
46+
}
3747
}
3848
}
3949

@@ -42,12 +52,17 @@ void visitThenSchema(Schema thenSchema) {
4252
if (ifSchemaException == null) {
4353
ValidationException thenSchemaException = owner.getFailureOfSchema(thenSchema, subject);
4454
if (thenSchemaException != null) {
45-
owner.failure(new ValidationException(conditionalSchema,
55+
ValidationException failure = new ValidationException(conditionalSchema,
4656
new StringBuilder(new StringBuilder("#")),
4757
"input is invalid against the \"then\" schema",
48-
Arrays.asList(thenSchemaException),
58+
asList(thenSchemaException),
4959
"then",
50-
conditionalSchema.getSchemaLocation()));
60+
conditionalSchema.getSchemaLocation());
61+
62+
owner.validationListener.thenSchemaMismatch(createMismatchEvent(THEN, thenSchemaException));
63+
owner.failure(failure);
64+
} else {
65+
owner.validationListener.thenSchemaMatch(createMatchEvent(THEN));
5166
}
5267
}
5368
}
@@ -57,14 +72,27 @@ void visitElseSchema(Schema elseSchema) {
5772
if (ifSchemaException != null) {
5873
ValidationException elseSchemaException = owner.getFailureOfSchema(elseSchema, subject);
5974
if (elseSchemaException != null) {
60-
owner.failure(new ValidationException(conditionalSchema,
75+
ValidationException failure = new ValidationException(conditionalSchema,
6176
new StringBuilder(new StringBuilder("#")),
6277
"input is invalid against both the \"if\" and \"else\" schema",
63-
Arrays.asList(ifSchemaException, elseSchemaException),
78+
asList(ifSchemaException, elseSchemaException),
6479
"else",
65-
conditionalSchema.getSchemaLocation()));
80+
conditionalSchema.getSchemaLocation());
81+
owner.validationListener.elseSchemaMismatch(createMismatchEvent(ELSE, elseSchemaException));
82+
owner.failure(failure);
83+
} else {
84+
owner.validationListener.elseSchemaMatch(createMatchEvent(ELSE));
6685
}
6786
}
6887
}
6988

89+
private ConditionalSchemaMatchEvent createMatchEvent(ConditionalSchemaValidationEvent.Keyword keyword) {
90+
return new ConditionalSchemaMatchEvent(conditionalSchema, subject, keyword);
91+
}
92+
93+
private ConditionalSchemaMismatchEvent createMismatchEvent(ConditionalSchemaValidationEvent.Keyword keyword,
94+
ValidationException failure) {
95+
return new ConditionalSchemaMismatchEvent(conditionalSchema, subject, keyword, failure);
96+
}
97+
7098
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public static Builder builder() {
1818
return new Builder();
1919
}
2020

21+
public static final FalseSchema INSTANCE = FalseSchema.builder().build();
22+
2123
/**
2224
* Constructor.
2325
*

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ public void describeTo(JSONPrinter writer) {
3131
public String toString() {
3232
return "true";
3333
}
34+
35+
@Override public boolean equals(Object o) {
36+
return o instanceof TrueSchema && super.equals(o);
37+
}
3438
}

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
import java.util.Collection;
1111
import java.util.List;
1212

13+
import org.everit.json.schema.event.CombinedSchemaMatchEvent;
14+
import org.everit.json.schema.event.CombinedSchemaMismatchEvent;
15+
import org.everit.json.schema.event.SchemaReferencedEvent;
16+
import org.everit.json.schema.event.ValidationListener;
1317
import org.json.JSONArray;
1418
import org.json.JSONObject;
1519

@@ -33,6 +37,8 @@ private static boolean isNull(Object obj) {
3337

3438
protected Object subject;
3539

40+
final ValidationListener validationListener;
41+
3642
private ValidationFailureReporter failureReporter;
3743

3844
private final ReadWriteValidator readWriteValidator;
@@ -46,13 +52,15 @@ void visit(Schema schema) {
4652
super.visit(schema);
4753
}
4854

49-
ValidatingVisitor(Object subject, ValidationFailureReporter failureReporter, ReadWriteValidator readWriteValidator) {
55+
ValidatingVisitor(Object subject, ValidationFailureReporter failureReporter, ReadWriteValidator readWriteValidator,
56+
ValidationListener validationListener) {
5057
if (subject != null && !VALIDATED_TYPES.stream().anyMatch(type -> type.isAssignableFrom(subject.getClass()))) {
5158
throw new IllegalArgumentException(format(TYPE_FAILURE_MSG, subject.getClass().getSimpleName()));
5259
}
5360
this.subject = subject;
5461
this.failureReporter = failureReporter;
5562
this.readWriteValidator = readWriteValidator;
63+
this.validationListener = validationListener;
5664
}
5765

5866
@Override
@@ -125,6 +133,9 @@ void visitReferenceSchema(ReferenceSchema referenceSchema) {
125133
if (failure != null) {
126134
failureReporter.failure(failure);
127135
}
136+
if (validationListener != null) {
137+
validationListener.schemaReferenced(new SchemaReferencedEvent(referenceSchema, subject, referredSchema));
138+
}
128139
}
129140

130141
@Override
@@ -147,6 +158,7 @@ void visitCombinedSchema(CombinedSchema combinedSchema) {
147158
if (null != exception) {
148159
failures.add(exception);
149160
}
161+
reportSchemaMatchEvent(combinedSchema, subschema, exception);
150162
}
151163
int matchingCount = subschemas.size() - failures.size();
152164
try {
@@ -166,6 +178,14 @@ void visitConditionalSchema(ConditionalSchema conditionalSchema) {
166178
conditionalSchema.accept(new ConditionalSchemaValidatingVisitor(subject, this));
167179
}
168180

181+
private void reportSchemaMatchEvent(CombinedSchema schema, Schema subschema, ValidationException failure) {
182+
if (failure == null) {
183+
validationListener.combinedSchemaMatch(new CombinedSchemaMatchEvent(schema, subschema, subject));
184+
} else {
185+
validationListener.combinedSchemaMismatch(new CombinedSchemaMismatchEvent(schema, subschema, subject, failure));
186+
}
187+
}
188+
169189
ValidationException getFailureOfSchema(Schema schema, Object input) {
170190
Object origSubject = this.subject;
171191
this.subject = input;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ public String getSchemaLocation() {
469469
return false;
470470
if (!causingExceptions.equals(that.causingExceptions))
471471
return false;
472-
return Objects.equals(keyword, that.keyword);
472+
return Objects.equals(keyword, that.keyword) && Objects.equals(getMessage(), that.getMessage());
473473
}
474474

475475
@Override public int hashCode() {

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.function.BiFunction;
44

5+
import org.everit.json.schema.event.ValidationListener;
6+
57
public interface Validator {
68

79
class ValidatorBuilder {
@@ -10,6 +12,8 @@ class ValidatorBuilder {
1012

1113
private ReadWriteContext readWriteContext;
1214

15+
private ValidationListener validationListener = ValidationListener.NOOP;
16+
1317
public ValidatorBuilder failEarly() {
1418
this.failEarly = true;
1519
return this;
@@ -20,8 +24,13 @@ public ValidatorBuilder readWriteContext(ReadWriteContext readWriteContext) {
2024
return this;
2125
}
2226

27+
public ValidatorBuilder withListener(ValidationListener validationListener) {
28+
this.validationListener = validationListener;
29+
return this;
30+
}
31+
2332
public Validator build() {
24-
return new DefaultValidator(failEarly, readWriteContext);
33+
return new DefaultValidator(failEarly, readWriteContext, validationListener);
2534
}
2635

2736
}
@@ -41,15 +50,22 @@ class DefaultValidator implements Validator {
4150

4251
private final ReadWriteContext readWriteContext;
4352

53+
private final ValidationListener validationListener;
54+
4455
DefaultValidator(boolean failEarly, ReadWriteContext readWriteContext) {
56+
this(failEarly, readWriteContext, null);
57+
}
58+
59+
DefaultValidator(boolean failEarly, ReadWriteContext readWriteContext, ValidationListener validationListener) {
4560
this.failEarly = failEarly;
4661
this.readWriteContext = readWriteContext;
62+
this.validationListener = validationListener;
4763
}
4864

4965
@Override public void performValidation(Schema schema, Object input) {
5066
ValidationFailureReporter failureReporter = createFailureReporter(schema);
5167
ReadWriteValidator readWriteValidator = ReadWriteValidator.createForContext(readWriteContext, failureReporter);
52-
ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter, readWriteValidator);
68+
ValidatingVisitor visitor = new ValidatingVisitor(input, failureReporter, readWriteValidator, validationListener);
5369
visitor.visit(schema);
5470
visitor.failIfErrorFound();
5571
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.everit.json.schema.event;
2+
3+
import org.everit.json.schema.CombinedSchema;
4+
import org.everit.json.schema.Schema;
5+
import org.json.JSONObject;
6+
7+
public class CombinedSchemaMatchEvent extends CombinedSchemaValidationEvent {
8+
9+
public CombinedSchemaMatchEvent(CombinedSchema schema, Schema subSchema,
10+
Object instance) {
11+
super(schema, subSchema, instance);
12+
}
13+
14+
@Override void describeTo(JSONObject obj) {
15+
obj.put("type", "match");
16+
obj.put("keyword", schema.getCriterion().toString());
17+
}
18+
19+
@Override public boolean equals(Object o) {
20+
return o instanceof CombinedSchemaMatchEvent && super.equals(o);
21+
}
22+
23+
@Override boolean canEqual(Object o) {
24+
return o instanceof CombinedSchemaMatchEvent;
25+
}
26+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.everit.json.schema.event;
2+
3+
import java.util.Objects;
4+
5+
import org.everit.json.schema.CombinedSchema;
6+
import org.everit.json.schema.Schema;
7+
import org.everit.json.schema.ValidationException;
8+
import org.json.JSONObject;
9+
import org.json.JSONTokener;
10+
11+
public class CombinedSchemaMismatchEvent extends CombinedSchemaValidationEvent implements MismatchEvent {
12+
13+
private final ValidationException failure;
14+
15+
public CombinedSchemaMismatchEvent(CombinedSchema schema, Schema subSchema, Object instance, ValidationException failure) {
16+
super(schema, subSchema, instance);
17+
this.failure = failure;
18+
}
19+
20+
@Override public ValidationException getFailure() {
21+
return failure;
22+
}
23+
24+
@Override void describeTo(JSONObject obj) {
25+
obj.put("type", "mismatch");
26+
obj.put("keyword", schema.getCriterion().toString());
27+
obj.put("subSchema", new JSONTokener(subSchema.toString()).nextValue());
28+
obj.put("failure", failure.toJSON());
29+
}
30+
31+
@Override public boolean equals(Object o) {
32+
if (this == o)
33+
return true;
34+
if (!(o instanceof CombinedSchemaMismatchEvent))
35+
return false;
36+
if (!super.equals(o))
37+
return false;
38+
CombinedSchemaMismatchEvent that = (CombinedSchemaMismatchEvent) o;
39+
return failure.equals(that.failure);
40+
}
41+
42+
@Override public int hashCode() {
43+
return Objects.hash(super.hashCode(), failure);
44+
}
45+
46+
@Override boolean canEqual(Object o) {
47+
return o instanceof CombinedSchemaMismatchEvent;
48+
}
49+
}

0 commit comments

Comments
 (0)