Skip to content

Commit 6a5ef34

Browse files
committed
Refactor evaluation context out from validator state
1 parent 6315c27 commit 6a5ef34

17 files changed

+430
-82
lines changed

src/main/java/com/networknt/schema/ExecutionContext.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.networknt.schema;
1818

19+
import java.util.ArrayDeque;
1920
import java.util.ArrayList;
2021
import java.util.HashMap;
2122
import java.util.List;
@@ -40,8 +41,19 @@ public class ExecutionContext {
4041
private InstanceResults instanceResults = null;
4142
private List<Error> errors = new ArrayList<>();
4243

43-
private Map<NodePath, DiscriminatorState> discriminatorMapping = new HashMap<>();
44+
private final Map<NodePath, DiscriminatorState> discriminatorMapping = new HashMap<>();
4445

46+
final ArrayDeque<Object> evaluationPath = new ArrayDeque<>(64);
47+
final ArrayDeque<Schema> evaluationSchema = new ArrayDeque<>(64);
48+
49+
public ArrayDeque<Object> getEvaluationPath() {
50+
return evaluationPath;
51+
}
52+
53+
public ArrayDeque<Schema> getEvaluationSchema() {
54+
return evaluationSchema;
55+
}
56+
4557
public Map<NodePath, DiscriminatorState> getDiscriminatorMapping() {
4658
return discriminatorMapping;
4759
}

src/main/java/com/networknt/schema/Schema.java

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.fasterxml.jackson.databind.JsonNode;
3636
import com.networknt.schema.keyword.KeywordValidator;
3737
import com.networknt.schema.keyword.TypeValidator;
38+
import com.networknt.schema.path.EvaluationPath;
3839
import com.networknt.schema.path.NodePath;
3940
import com.networknt.schema.path.PathType;
4041
import com.networknt.schema.resource.ClasspathResourceLoader;
@@ -147,6 +148,14 @@ public static NodePath getInstance() {
147148
}
148149

149150
public void validate(ExecutionContext executionContext, JsonNode node) {
151+
/*
152+
* Sets the evaluationPath to point to the schema location fragment if it isn't
153+
* pointing to the document
154+
*/
155+
int count = this.schemaLocation.getFragment().getNameCount();
156+
for (int x = 0; x < count; x++) {
157+
executionContext.evaluationPath.addLast(this.schemaLocation.getFragment().getElement(x));
158+
}
150159
validate(executionContext, node, node, atRoot());
151160
}
152161

@@ -702,14 +711,32 @@ private List<KeywordValidator> read(JsonNode schemaNode) {
702711

703712
@Override
704713
public void validate(ExecutionContext executionContext, JsonNode jsonNode, JsonNode rootNode, NodePath instanceLocation) {
705-
int currentErrors = executionContext.getErrors().size();
706-
for (KeywordValidator v : getValidators()) {
707-
v.validate(executionContext, jsonNode, rootNode, instanceLocation);
708-
}
709-
if (executionContext.getErrors().size() > currentErrors) {
710-
// Failed with assertion set result and drop all annotations from this schema
711-
// and all subschemas
712-
executionContext.getInstanceResults().setResult(instanceLocation, getSchemaLocation(), getEvaluationPath(), false);
714+
// String newEvaluationPath = new EvaluationPath(executionContext.getEvaluationPath()).toString();
715+
// String oldEvaluationPath = getEvaluationPath().toString();
716+
// if (!oldEvaluationPath.equals(newEvaluationPath)) {
717+
// System.out.println("-----------------");
718+
// System.out.println("MISMATCH OLD: " + oldEvaluationPath);
719+
// System.out.println("MISMATCH NEW: " + newEvaluationPath);
720+
// System.out.println("-----------------");
721+
// }
722+
executionContext.evaluationSchema.addLast(this);
723+
try {
724+
int currentErrors = executionContext.getErrors().size();
725+
for (KeywordValidator v : getValidators()) {
726+
executionContext.evaluationPath.addLast(v.getKeyword());
727+
try {
728+
v.validate(executionContext, jsonNode, rootNode, instanceLocation);
729+
} finally {
730+
executionContext.evaluationPath.removeLast();
731+
}
732+
}
733+
if (executionContext.getErrors().size() > currentErrors) {
734+
// Failed with assertion set result and drop all annotations from this schema
735+
// and all subschemas
736+
executionContext.getInstanceResults().setResult(instanceLocation, getSchemaLocation(), getEvaluationPath(), false);
737+
}
738+
} finally {
739+
executionContext.evaluationSchema.removeLast();
713740
}
714741
}
715742

@@ -1585,7 +1612,12 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
15851612
if (executionContext.getWalkConfig().getKeywordWalkListenerRunner().runPreWalkListeners(executionContext,
15861613
evaluationPathWithKeyword.getName(-1), node, rootNode, instanceLocation,
15871614
this, validator)) {
1588-
validator.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
1615+
executionContext.evaluationPath.addLast(validator.getKeyword());
1616+
try {
1617+
validator.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
1618+
} finally {
1619+
executionContext.evaluationPath.removeLast();
1620+
}
15891621
}
15901622
} finally {
15911623
// Call all the post-walk listeners.

src/main/java/com/networknt/schema/keyword/AllOfValidator.java

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,19 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
5959

6060
protected void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
6161
NodePath instanceLocation, boolean walk) {
62+
int schemaIndex = 0;
6263
for (Schema schema : this.schemas) {
63-
if (!walk) {
64-
schema.validate(executionContext, node, rootNode, instanceLocation);
65-
} else {
66-
schema.walk(executionContext, node, rootNode, instanceLocation, true);
64+
executionContext.getEvaluationPath().addLast(schemaIndex);
65+
try {
66+
if (!walk) {
67+
schema.validate(executionContext, node, rootNode, instanceLocation);
68+
} else {
69+
schema.walk(executionContext, node, rootNode, instanceLocation, true);
70+
}
71+
} finally {
72+
executionContext.getEvaluationPath().removeLast();
6773
}
74+
schemaIndex++;
6875
}
6976
}
7077

@@ -75,9 +82,16 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
7582
validate(executionContext, node, rootNode, instanceLocation, true);
7683
return;
7784
}
85+
int schemaIndex = 0;
7886
for (Schema schema : this.schemas) {
7987
// Walk through the schema
80-
schema.walk(executionContext, node, rootNode, instanceLocation, false);
88+
executionContext.getEvaluationPath().addLast(schemaIndex);
89+
try {
90+
schema.walk(executionContext, node, rootNode, instanceLocation, false);
91+
} finally {
92+
executionContext.getEvaluationPath().removeLast();
93+
}
94+
schemaIndex++;
8195
}
8296
}
8397

src/main/java/com/networknt/schema/keyword/AnyOfValidator.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
7373
// Save flag as nested schema evaluation shouldn't trigger fail fast
7474
boolean failFast = executionContext.isFailFast();
7575
try {
76+
int schemaIndex = 0;
7677
executionContext.setFailFast(false);
7778
for (Schema schema : this.schemas) {
7879
subSchemaErrors.clear(); // Reuse and clear for each run
@@ -87,14 +88,21 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
8788
allErrors = new ArrayList<>();
8889
}
8990
allErrors.addAll(subSchemaErrors);
91+
schemaIndex++;
9092
continue;
9193
}
9294
}
93-
if (!walk) {
94-
schema.validate(executionContext, node, rootNode, instanceLocation);
95-
} else {
96-
schema.walk(executionContext, node, rootNode, instanceLocation, true);
95+
executionContext.getEvaluationPath().addLast(schemaIndex);
96+
try {
97+
if (!walk) {
98+
schema.validate(executionContext, node, rootNode, instanceLocation);
99+
} else {
100+
schema.walk(executionContext, node, rootNode, instanceLocation, true);
101+
}
102+
} finally {
103+
executionContext.getEvaluationPath().removeLast();
97104
}
105+
schemaIndex++;
98106

99107
// check if any validation errors have occurred
100108
if (subSchemaErrors.isEmpty()) {
@@ -202,7 +210,14 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
202210
return;
203211
}
204212
for (Schema schema : this.schemas) {
205-
schema.walk(executionContext, node, rootNode, instanceLocation, false);
213+
int schemaIndex = 0;
214+
executionContext.getEvaluationPath().addLast(schemaIndex);
215+
try {
216+
schema.walk(executionContext, node, rootNode, instanceLocation, false);
217+
} finally {
218+
executionContext.getEvaluationPath().removeLast();
219+
}
220+
schemaIndex++;
206221
}
207222
}
208223

src/main/java/com/networknt/schema/keyword/DependenciesValidator.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
7979
}
8080
Schema schema = schemaDeps.get(pname);
8181
if (schema != null) {
82-
schema.validate(executionContext, node, rootNode, instanceLocation);
82+
executionContext.getEvaluationPath().addLast(pname);
83+
try {
84+
schema.validate(executionContext, node, rootNode, instanceLocation);
85+
} finally {
86+
executionContext.getEvaluationPath().removeLast();
87+
}
8388
}
8489
}
8590
}

src/main/java/com/networknt/schema/keyword/DependentSchemas.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,10 @@
3131
public class DependentSchemas extends BaseKeywordValidator {
3232
private final Map<String, Schema> schemaDependencies = new HashMap<>();
3333

34-
public DependentSchemas(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode, Schema parentSchema, SchemaContext schemaContext) {
35-
34+
public DependentSchemas(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
35+
Schema parentSchema, SchemaContext schemaContext) {
3636
super(KeywordType.DEPENDENT_SCHEMAS, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
37-
38-
for (Iterator<String> it = schemaNode.fieldNames(); it.hasNext(); ) {
37+
for (Iterator<String> it = schemaNode.fieldNames(); it.hasNext();) {
3938
String pname = it.next();
4039
JsonNode pvalue = schemaNode.get(pname);
4140
if (pvalue.isObject() || pvalue.isBoolean()) {
@@ -57,10 +56,15 @@ protected void validate(ExecutionContext executionContext, JsonNode node, JsonNo
5756
String pname = it.next();
5857
Schema schema = this.schemaDependencies.get(pname);
5958
if (schema != null) {
60-
if(!walk) {
61-
schema.validate(executionContext, node, rootNode, instanceLocation);
62-
} else {
63-
schema.walk(executionContext, node, rootNode, instanceLocation, true);
59+
executionContext.getEvaluationPath().addLast(pname);
60+
try {
61+
if(!walk) {
62+
schema.validate(executionContext, node, rootNode, instanceLocation);
63+
} else {
64+
schema.walk(executionContext, node, rootNode, instanceLocation, true);
65+
}
66+
} finally {
67+
executionContext.getEvaluationPath().removeLast();
6468
}
6569
}
6670
}

src/main/java/com/networknt/schema/keyword/IfValidator.java

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ public IfValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonN
6666

6767
@Override
6868
public void validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation) {
69-
70-
7169
boolean ifConditionPassed = false;
7270

7371
// Save flag as nested schema evaluation shouldn't trigger fail fast
@@ -86,9 +84,25 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
8684
}
8785

8886
if (ifConditionPassed && this.thenSchema != null) {
89-
this.thenSchema.validate(executionContext, node, rootNode, instanceLocation);
87+
// The "if" keyword is a bit unusual as it actually handles multiple keywords
88+
// This removes the "if" in the evaluation path so the rest of the evaluation paths will be correct
89+
executionContext.getEvaluationPath().removeLast();
90+
executionContext.getEvaluationPath().addLast("then");
91+
try {
92+
this.thenSchema.validate(executionContext, node, rootNode, instanceLocation);
93+
} finally {
94+
executionContext.getEvaluationPath().removeLast();
95+
executionContext.getEvaluationPath().addLast("if");
96+
}
9097
} else if (!ifConditionPassed && this.elseSchema != null) {
91-
this.elseSchema.validate(executionContext, node, rootNode, instanceLocation);
98+
executionContext.getEvaluationPath().removeLast();
99+
executionContext.getEvaluationPath().addLast("else");
100+
try {
101+
this.elseSchema.validate(executionContext, node, rootNode, instanceLocation);
102+
} finally {
103+
executionContext.getEvaluationPath().removeLast();
104+
executionContext.getEvaluationPath().addLast("if");
105+
}
92106
}
93107
}
94108

@@ -107,6 +121,9 @@ public void preloadSchema() {
107121

108122
@Override
109123
public void walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, NodePath instanceLocation, boolean shouldValidateSchema) {
124+
// The "if" keyword is a bit unusual as it actually handles multiple keywords
125+
// This removes the "if" in the evaluation path so the rest of the evaluation paths will be correct
126+
110127
boolean checkCondition = node != null && shouldValidateSchema;
111128
boolean ifConditionPassed = false;
112129

@@ -126,17 +143,44 @@ public void walk(ExecutionContext executionContext, JsonNode node, JsonNode root
126143
}
127144
if (!checkCondition) {
128145
if (this.thenSchema != null) {
129-
this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
146+
executionContext.getEvaluationPath().removeLast();
147+
executionContext.getEvaluationPath().addLast("then");
148+
try {
149+
this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
150+
} finally {
151+
executionContext.getEvaluationPath().removeLast();
152+
executionContext.getEvaluationPath().addLast("if");
153+
}
130154
}
131155
if (this.elseSchema != null) {
132-
this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
156+
executionContext.getEvaluationPath().removeLast();
157+
executionContext.getEvaluationPath().addLast("else");
158+
try {
159+
this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
160+
} finally {
161+
executionContext.getEvaluationPath().removeLast();
162+
executionContext.getEvaluationPath().addLast("if");
163+
}
133164
}
134165
} else {
135166
if (this.thenSchema != null && ifConditionPassed) {
136-
this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
137-
}
138-
else if (this.elseSchema != null && !ifConditionPassed) {
139-
this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
167+
executionContext.getEvaluationPath().removeLast();
168+
executionContext.getEvaluationPath().addLast("then");
169+
try {
170+
this.thenSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
171+
} finally {
172+
executionContext.getEvaluationPath().removeLast();
173+
executionContext.getEvaluationPath().addLast("if");
174+
}
175+
} else if (this.elseSchema != null && !ifConditionPassed) {
176+
executionContext.getEvaluationPath().removeLast();
177+
executionContext.getEvaluationPath().addLast("else");
178+
try {
179+
this.elseSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
180+
} finally {
181+
executionContext.getEvaluationPath().removeLast();
182+
executionContext.getEvaluationPath().addLast("if");
183+
}
140184
}
141185
}
142186
}

0 commit comments

Comments
 (0)