Skip to content

Commit 58b0ddf

Browse files
fduttonFaron Dutton
andauthored
Adds explicit support for tracking evaluated properties (#714)
Resolves #713 Co-authored-by: Faron Dutton <[email protected]>
1 parent a6b63f9 commit 58b0ddf

13 files changed

+107
-139
lines changed

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import java.util.regex.Matcher;
2525
import java.util.regex.Pattern;
2626

27-
public class AdditionalPropertiesValidator extends BaseJsonValidator implements JsonValidator {
27+
public class AdditionalPropertiesValidator extends BaseJsonValidator {
2828
private static final Logger logger = LoggerFactory.getLogger(AdditionalPropertiesValidator.class);
2929

3030
private final boolean allowAdditionalProperties;
@@ -65,15 +65,7 @@ public AdditionalPropertiesValidator(String schemaPath, JsonNode schemaNode, Jso
6565
}
6666

6767
private void addToEvaluatedProperties(String propertyPath) {
68-
Object evaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
69-
List<String> evaluatedPropertiesList = null;
70-
if (evaluatedProperties == null) {
71-
evaluatedPropertiesList = new ArrayList<>();
72-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, evaluatedPropertiesList);
73-
} else {
74-
evaluatedPropertiesList = (List<String>) evaluatedProperties;
75-
}
76-
evaluatedPropertiesList.add(propertyPath);
68+
CollectorContext.getInstance().getEvaluatedProperties().add(propertyPath);
7769
}
7870

7971
public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {

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

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.slf4j.Logger;
2424
import org.slf4j.LoggerFactory;
2525

26-
public class AllOfValidator extends BaseJsonValidator implements JsonValidator {
26+
public class AllOfValidator extends BaseJsonValidator {
2727
private static final Logger logger = LoggerFactory.getLogger(AllOfValidator.class);
2828

2929
private final List<JsonSchema> schemas = new ArrayList<JsonSchema>();
@@ -49,17 +49,17 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
4949

5050
Set<ValidationMessage> childSchemaErrors = new LinkedHashSet<ValidationMessage>();
5151

52-
// As AllOf might contain multiple schemas take a backup of evaluatedProperties.
53-
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
54-
55-
List<String> totalEvaluatedProperties = new ArrayList<>();
52+
Set<String> newEvaluatedProperties = Collections.emptySet();
5653

5754
for (JsonSchema schema : schemas) {
55+
// As AllOf might contain multiple schemas take a backup of evaluatedProperties.
56+
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
57+
58+
Set<ValidationMessage> localErrors = new HashSet<>();
59+
5860
try {
5961
// Make the evaluatedProperties list empty.
60-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
61-
62-
Set<ValidationMessage> localErrors = new HashSet<>();
62+
CollectorContext.getInstance().getEvaluatedProperties().clear();
6363

6464
if (!state.isWalkEnabled()) {
6565
localErrors = schema.validate(node, rootNode, at);
@@ -71,7 +71,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
7171

7272
// Keep Collecting total evaluated properties.
7373
if (localErrors.isEmpty()) {
74-
totalEvaluatedProperties.addAll((List<String>) CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES));
74+
newEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
7575
}
7676

7777
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
@@ -106,13 +106,11 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
106106
}
107107
}
108108
} finally {
109-
if (childSchemaErrors.isEmpty()) {
110-
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
111-
backupEvaluatedPropertiesList.addAll(totalEvaluatedProperties);
112-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
113-
} else {
114-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
109+
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
110+
if (localErrors.isEmpty()) {
111+
CollectorContext.getInstance().getEvaluatedProperties().addAll(newEvaluatedProperties);
115112
}
113+
newEvaluatedProperties = Collections.emptySet();
116114
}
117115
}
118116

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.*;
2424
import java.util.stream.Collectors;
2525

26-
public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
26+
public class AnyOfValidator extends BaseJsonValidator {
2727
private static final Logger logger = LoggerFactory.getLogger(RequiredValidator.class);
2828
private static final String DISCRIMINATOR_REMARK = "and the discriminator-selected candidate schema didn't pass validation";
2929

@@ -64,10 +64,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
6464
Set<ValidationMessage> allErrors = new LinkedHashSet<>();
6565

6666
// As anyOf might contain multiple schemas take a backup of evaluatedProperties.
67-
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
67+
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
6868

6969
// Make the evaluatedProperties list empty.
70-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
70+
CollectorContext.getInstance().getEvaluatedProperties().clear();
7171

7272
try {
7373
int numberOfValidSubSchemas = 0;
@@ -139,22 +139,15 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
139139
validationContext.leaveDiscriminatorContextImmediately(at);
140140
}
141141
if (allErrors.isEmpty()) {
142-
addEvaluatedProperties(backupEvaluatedProperties);
143142
state.setMatchedNode(true);
144143
} else {
145-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
144+
CollectorContext.getInstance().getEvaluatedProperties().clear();
146145
}
146+
CollectorContext.getInstance().getEvaluatedProperties().addAll(backupEvaluatedProperties);
147147
}
148148
return Collections.unmodifiableSet(allErrors);
149149
}
150150

151-
private void addEvaluatedProperties(Object backupEvaluatedProperties) {
152-
// Add all the evaluated properties.
153-
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
154-
backupEvaluatedPropertiesList.addAll((List<String>) CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES));
155-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
156-
}
157-
158151
@Override
159152
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
160153
if (shouldValidateSchema) {

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
package com.networknt.schema;
1717

18+
import java.util.Collection;
1819
import java.util.HashMap;
20+
import java.util.LinkedHashSet;
1921
import java.util.Map;
2022
import java.util.Map.Entry;
2123
import java.util.Set;
@@ -45,6 +47,39 @@ public static CollectorContext getInstance() {
4547
*/
4648
private Map<String, Object> collectorLoadMap = new HashMap<String, Object>();
4749

50+
/**
51+
* Used to track which properties have been evaluated.
52+
*/
53+
private final Set<String> evaluatedProperties = new LinkedHashSet<>();
54+
55+
/**
56+
* Identifies which properties have been evaluated.
57+
*
58+
* @return the set of evaluated properties (never null)
59+
*/
60+
public Set<String> getEvaluatedProperties() {
61+
return evaluatedProperties;
62+
}
63+
64+
/**
65+
* Clones the properties that have been evaluated.
66+
*
67+
* @return the set of evaluated properties (never null)
68+
*/
69+
public Set<String> copyEvaluatedProperties() {
70+
return new LinkedHashSet<>(evaluatedProperties);
71+
}
72+
73+
/**
74+
* Replaces the properties that have been evaluated.
75+
*/
76+
public void replaceEvaluatedProperties(Collection<String> paths) {
77+
this.evaluatedProperties.clear();
78+
if (null != paths) {
79+
this.evaluatedProperties.addAll(paths);
80+
}
81+
}
82+
4883
/**
4984
* Adds a collector with give name. Preserving this method for backward
5085
* compatibility.
@@ -121,6 +156,7 @@ public void combineWithCollector(String name, Object data) {
121156
public void reset() {
122157
this.collectorMap = new HashMap<String, Object>();
123158
this.collectorLoadMap = new HashMap<String, Object>();
159+
this.evaluatedProperties.clear();
124160
}
125161

126162
/**

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

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
import java.util.*;
2424

25-
public class IfValidator extends BaseJsonValidator implements JsonValidator {
25+
public class IfValidator extends BaseJsonValidator {
2626
private static final Logger logger = LoggerFactory.getLogger(IfValidator.class);
2727

2828
private static final ArrayList<String> KEYWORDS = new ArrayList<String>(Arrays.asList("if", "then", "else"));
@@ -59,16 +59,16 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5959
debug(logger, node, rootNode, at);
6060

6161
// As if-then-else might contain multiple schemas take a backup of evaluatedProperties.
62-
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
62+
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
6363

64-
Object ifEvaluatedProperties = null;
64+
Set<String> ifEvaluatedProperties = Collections.emptySet();
6565

66-
Object thenEvaluatedProperties = null;
66+
Set<String> thenEvaluatedProperties = Collections.emptySet();
6767

68-
Object elseEvaluatedProperties = null;
68+
Set<String> elseEvaluatedProperties = Collections.emptySet();
6969

7070
// Make the evaluatedProperties list empty.
71-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
71+
CollectorContext.getInstance().getEvaluatedProperties().clear();
7272

7373
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
7474

@@ -82,49 +82,38 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
8282
ifConditionPassed = false;
8383
}
8484
// Evaluated Properties from if.
85-
ifEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
85+
ifEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
8686

8787
if (ifConditionPassed && thenSchema != null) {
8888

8989
// Make the evaluatedProperties list empty.
90-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
90+
CollectorContext.getInstance().getEvaluatedProperties().clear();
9191

9292
errors.addAll(thenSchema.validate(node, rootNode, at));
9393

9494
// Collect the then evaluated properties.
95-
thenEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
95+
thenEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
9696

9797
} else if (!ifConditionPassed && elseSchema != null) {
9898

9999
// Make the evaluatedProperties list empty.
100-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
100+
CollectorContext.getInstance().getEvaluatedProperties().clear();
101101

102102
errors.addAll(elseSchema.validate(node, rootNode, at));
103103

104104
// Collect the else evaluated properties.
105-
elseEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
105+
elseEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
106106
}
107107

108108
} finally {
109+
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
109110
if (errors.isEmpty()) {
110-
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
111-
112111
// If the "if" keyword condition is passed then only add if properties as evaluated.
113-
if (ifEvaluatedProperties != null && ifConditionPassed) {
114-
backupEvaluatedPropertiesList.addAll((List<String>) ifEvaluatedProperties);
115-
}
116-
117-
if (thenEvaluatedProperties != null) {
118-
backupEvaluatedPropertiesList.addAll((List<String>) thenEvaluatedProperties);
112+
if (ifConditionPassed) {
113+
CollectorContext.getInstance().getEvaluatedProperties().addAll(ifEvaluatedProperties);
119114
}
120-
121-
if (elseEvaluatedProperties != null) {
122-
backupEvaluatedPropertiesList.addAll((List<String>) elseEvaluatedProperties);
123-
}
124-
125-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
126-
} else {
127-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
115+
CollectorContext.getInstance().getEvaluatedProperties().addAll(thenEvaluatedProperties);
116+
CollectorContext.getInstance().getEvaluatedProperties().addAll(elseEvaluatedProperties);
128117
}
129118
}
130119

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
import java.util.*;
2424

25-
public class NotValidator extends BaseJsonValidator implements JsonValidator {
25+
public class NotValidator extends BaseJsonValidator {
2626
private static final Logger logger = LoggerFactory.getLogger(RequiredValidator.class);
2727

2828
private final JsonSchema schema;
@@ -38,10 +38,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
3838
Set<ValidationMessage> errors = new HashSet<>();
3939

4040
//As not will contain a schema take a backup of evaluatedProperties.
41-
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
41+
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
4242

4343
// Make the evaluatedProperties list empty.
44-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
44+
CollectorContext.getInstance().getEvaluatedProperties().clear();
4545

4646
try {
4747
debug(logger, node, rootNode, at);
@@ -52,11 +52,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
5252
return Collections.emptySet();
5353
} finally {
5454
if (errors.isEmpty()) {
55-
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
56-
backupEvaluatedPropertiesList.addAll((List<String>) CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES));
57-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
55+
CollectorContext.getInstance().getEvaluatedProperties().addAll(backupEvaluatedProperties);
5856
} else {
59-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
57+
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
6058
}
6159
}
6260
}

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.*;
2424
import java.util.stream.Collectors;
2525

26-
public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
26+
public class OneOfValidator extends BaseJsonValidator {
2727
private static final Logger logger = LoggerFactory.getLogger(OneOfValidator.class);
2828

2929
private final List<ShortcutValidator> schemas = new ArrayList<ShortcutValidator>();
@@ -129,10 +129,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
129129
Set<ValidationMessage> errors = new LinkedHashSet<ValidationMessage>();
130130

131131
// As oneOf might contain multiple schemas take a backup of evaluatedProperties.
132-
Object backupEvaluatedProperties = CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES);
132+
Set<String> backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties();
133133

134134
// Make the evaluatedProperties list empty.
135-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
135+
CollectorContext.getInstance().getEvaluatedProperties().clear();
136136

137137
try {
138138
debug(logger, node, rootNode, at);
@@ -182,7 +182,7 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
182182

183183
// If the number of valid schema is greater than one, just reset the evaluated properties and break.
184184
if (numberOfValidSchema > 1) {
185-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, new ArrayList<>());
185+
CollectorContext.getInstance().getEvaluatedProperties().clear();
186186
break;
187187
}
188188

@@ -231,11 +231,9 @@ else if (numberOfValidSchema < 1) {
231231
return Collections.unmodifiableSet(errors);
232232
} finally {
233233
if (errors.isEmpty()) {
234-
List<String> backupEvaluatedPropertiesList = (backupEvaluatedProperties == null ? new ArrayList<>() : (List<String>) backupEvaluatedProperties);
235-
backupEvaluatedPropertiesList.addAll((List<String>) CollectorContext.getInstance().get(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES));
236-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedPropertiesList);
234+
CollectorContext.getInstance().getEvaluatedProperties().addAll(backupEvaluatedProperties);
237235
} else {
238-
CollectorContext.getInstance().add(UnEvaluatedPropertiesValidator.EVALUATED_PROPERTIES, backupEvaluatedProperties);
236+
CollectorContext.getInstance().replaceEvaluatedProperties(backupEvaluatedProperties);
239237
}
240238
}
241239
}

0 commit comments

Comments
 (0)