Skip to content

Commit 53a3402

Browse files
authored
Support GraalVM and refactor (#972)
* Add native-image properties * Add meta schema mapper * Only create objects if needed * Defer some processing * Refactor * Refactor * Refactor * Refactor * Refactor
1 parent bd085ed commit 53a3402

21 files changed

+381
-349
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public abstract class AbstractJsonValidator implements JsonValidator {
2828
private final SchemaLocation schemaLocation;
2929
private final JsonNode schemaNode;
3030
private final JsonNodePath evaluationPath;
31-
private final Keyword keyword;
31+
private final String keyword;
3232

3333
/**
3434
* Constructor.
@@ -41,7 +41,7 @@ public abstract class AbstractJsonValidator implements JsonValidator {
4141
public AbstractJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, Keyword keyword, JsonNode schemaNode) {
4242
this.schemaLocation = schemaLocation;
4343
this.evaluationPath = evaluationPath;
44-
this.keyword = keyword;
44+
this.keyword = keyword.getValue();
4545
this.schemaNode = schemaNode;
4646
}
4747

@@ -57,7 +57,7 @@ public JsonNodePath getEvaluationPath() {
5757

5858
@Override
5959
public String getKeyword() {
60-
return keyword.getValue();
60+
return keyword;
6161
}
6262

6363
/**

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

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

26-
import java.util.*;
26+
import java.util.ArrayList;
27+
import java.util.Collections;
28+
import java.util.HashSet;
29+
import java.util.Iterator;
30+
import java.util.LinkedHashSet;
31+
import java.util.List;
32+
import java.util.Map.Entry;
33+
import java.util.Set;
2734

2835
/**
2936
* {@link JsonValidator} for additionalProperties.
@@ -34,7 +41,7 @@ public class AdditionalPropertiesValidator extends BaseJsonValidator {
3441
private final boolean allowAdditionalProperties;
3542
private final JsonSchema additionalPropertiesSchema;
3643
private final Set<String> allowedProperties;
37-
private final List<RegularExpression> patternProperties = new ArrayList<>();
44+
private final List<RegularExpression> patternProperties;
3845

3946
private Boolean hasUnevaluatedPropertiesValidator;
4047

@@ -64,9 +71,12 @@ public AdditionalPropertiesValidator(SchemaLocation schemaLocation, JsonNodePath
6471

6572
JsonNode patternPropertiesNode = parentSchema.getSchemaNode().get(PatternPropertiesValidator.PROPERTY);
6673
if (patternPropertiesNode != null) {
74+
this.patternProperties = new ArrayList<>();
6775
for (Iterator<String> it = patternPropertiesNode.fieldNames(); it.hasNext(); ) {
6876
patternProperties.add(RegularExpression.compile(it.next(), validationContext));
6977
}
78+
} else {
79+
this.patternProperties = Collections.emptyList();
7080
}
7181
}
7282

@@ -93,8 +103,9 @@ public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNo
93103

94104
Set<ValidationMessage> errors = null;
95105

96-
for (Iterator<String> it = node.fieldNames(); it.hasNext(); ) {
97-
String pname = it.next();
106+
for (Iterator<Entry<String, JsonNode>> it = node.fields(); it.hasNext(); ) {
107+
Entry<String, JsonNode> entry = it.next();
108+
String pname = entry.getKey();
98109
// skip the context items
99110
if (pname.startsWith("#")) {
100111
continue;
@@ -120,15 +131,18 @@ public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNo
120131
if (additionalPropertiesSchema != null) {
121132
ValidatorState state = executionContext.getValidatorState();
122133
if (state != null && state.isWalkEnabled()) {
123-
Set<ValidationMessage> results = additionalPropertiesSchema.walk(executionContext, node.get(pname), rootNode, instanceLocation.append(pname), state.isValidationEnabled());
134+
Set<ValidationMessage> results = additionalPropertiesSchema.walk(executionContext,
135+
entry.getValue(), rootNode, instanceLocation.append(pname),
136+
state.isValidationEnabled());
124137
if (!results.isEmpty()) {
125138
if (errors == null) {
126139
errors = new LinkedHashSet<>();
127140
}
128141
errors.addAll(results);
129142
}
130143
} else {
131-
Set<ValidationMessage> results = additionalPropertiesSchema.validate(executionContext, node.get(pname), rootNode, instanceLocation.append(pname));
144+
Set<ValidationMessage> results = additionalPropertiesSchema.validate(executionContext,
145+
entry.getValue(), rootNode, instanceLocation.append(pname));
132146
if (!results.isEmpty()) {
133147
if (errors == null) {
134148
errors = new LinkedHashSet<>();
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2016 Network New Technologies Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.networknt.schema;
18+
19+
import com.fasterxml.jackson.databind.JsonNode;
20+
21+
import java.util.Collections;
22+
import java.util.Set;
23+
24+
/**
25+
* Used for Keywords that have no validation aspect, but are part of the metaschema, where annotations may need to be collected.
26+
*/
27+
public class AnnotationKeyword extends AbstractKeyword {
28+
29+
private static final class Validator extends AbstractJsonValidator {
30+
public Validator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
31+
JsonSchema parentSchema, ValidationContext validationContext, Keyword keyword) {
32+
super(schemaLocation, evaluationPath, keyword, schemaNode);
33+
}
34+
35+
@Override
36+
public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
37+
if (collectAnnotations(executionContext)) {
38+
Object value = getAnnotationValue(getSchemaNode());
39+
if (value != null) {
40+
putAnnotation(executionContext,
41+
annotation -> annotation.instanceLocation(instanceLocation).value(value));
42+
}
43+
}
44+
return Collections.emptySet();
45+
}
46+
47+
protected Object getAnnotationValue(JsonNode schemaNode) {
48+
if (schemaNode.isTextual()) {
49+
return schemaNode.textValue();
50+
} else if (schemaNode.isNumber()) {
51+
return schemaNode.numberValue();
52+
} else if (schemaNode.isObject()) {
53+
return schemaNode;
54+
}
55+
return null;
56+
}
57+
}
58+
59+
public AnnotationKeyword(String keyword) {
60+
super(keyword);
61+
}
62+
63+
@Override
64+
public JsonValidator newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
65+
JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception {
66+
return new Validator(schemaLocation, evaluationPath, schemaNode, parentSchema, validationContext, this);
67+
}
68+
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,40 @@ protected void preloadJsonSchemas(final Collection<JsonSchema> schemas) {
290290
}
291291
}
292292

293+
public static class JsonNodePathLegacy {
294+
private static final JsonNodePath INSTANCE = new JsonNodePath(PathType.LEGACY);
295+
public static JsonNodePath getInstance() {
296+
return INSTANCE;
297+
}
298+
}
299+
300+
public static class JsonNodePathJsonPointer {
301+
private static final JsonNodePath INSTANCE = new JsonNodePath(PathType.JSON_POINTER);
302+
public static JsonNodePath getInstance() {
303+
return INSTANCE;
304+
}
305+
}
306+
307+
public static class JsonNodePathJsonPath {
308+
private static final JsonNodePath INSTANCE = new JsonNodePath(PathType.JSON_PATH);
309+
public static JsonNodePath getInstance() {
310+
return INSTANCE;
311+
}
312+
}
313+
293314
/**
294315
* Get the root path.
295316
*
296317
* @return The path.
297318
*/
298319
protected JsonNodePath atRoot() {
320+
if (this.validationContext.getConfig().getPathType().equals(PathType.JSON_POINTER)) {
321+
return JsonNodePathJsonPointer.getInstance();
322+
} else if (this.validationContext.getConfig().getPathType().equals(PathType.LEGACY)) {
323+
return JsonNodePathLegacy.getInstance();
324+
} else if (this.validationContext.getConfig().getPathType().equals(PathType.JSON_PATH)) {
325+
return JsonNodePathJsonPath.getInstance();
326+
}
299327
return new JsonNodePath(this.validationContext.getConfig().getPathType());
300328
}
301329

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

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626
*/
2727
public class ExecutionContext {
2828
private ExecutionConfig executionConfig;
29-
private CollectorContext collectorContext;
29+
private CollectorContext collectorContext = null;
3030
private ValidatorState validatorState = null;
31-
private Stack<DiscriminatorContext> discriminatorContexts = new Stack<>();
32-
private JsonNodeAnnotations annotations = new JsonNodeAnnotations();
33-
private JsonNodeResults results = new JsonNodeResults();
31+
private Stack<DiscriminatorContext> discriminatorContexts = null;
32+
private JsonNodeAnnotations annotations = null;
33+
private JsonNodeResults results = null;
3434

3535
/**
3636
* This is used during the execution to determine if the validator should fail fast.
@@ -43,7 +43,7 @@ public class ExecutionContext {
4343
* Creates an execution context.
4444
*/
4545
public ExecutionContext() {
46-
this(new CollectorContext());
46+
this(new ExecutionConfig(), null);
4747
}
4848

4949
/**
@@ -61,7 +61,7 @@ public ExecutionContext(CollectorContext collectorContext) {
6161
* @param executionConfig the execution configuration
6262
*/
6363
public ExecutionContext(ExecutionConfig executionConfig) {
64-
this(executionConfig, new CollectorContext());
64+
this(executionConfig, null);
6565
}
6666

6767
/**
@@ -81,7 +81,10 @@ public ExecutionContext(ExecutionConfig executionConfig, CollectorContext collec
8181
* @return the collector context
8282
*/
8383
public CollectorContext getCollectorContext() {
84-
return collectorContext;
84+
if (this.collectorContext == null) {
85+
this.collectorContext = new CollectorContext();
86+
}
87+
return this.collectorContext;
8588
}
8689

8790
/**
@@ -112,10 +115,16 @@ public void setExecutionConfig(ExecutionConfig executionConfig) {
112115
}
113116

114117
public JsonNodeAnnotations getAnnotations() {
118+
if (this.annotations == null) {
119+
this.annotations = new JsonNodeAnnotations();
120+
}
115121
return annotations;
116122
}
117123

118124
public JsonNodeResults getResults() {
125+
if (this.results == null) {
126+
this.results = new JsonNodeResults();
127+
}
119128
return results;
120129
}
121130

@@ -163,13 +172,20 @@ public void setValidatorState(ValidatorState validatorState) {
163172
}
164173

165174
public DiscriminatorContext getCurrentDiscriminatorContext() {
175+
if (this.discriminatorContexts == null) {
176+
return null;
177+
}
178+
166179
if (!this.discriminatorContexts.empty()) {
167180
return this.discriminatorContexts.peek();
168181
}
169182
return null; // this is the case when we get on a schema that has a discriminator, but it's not used in anyOf
170183
}
171184

172185
public void enterDiscriminatorContext(final DiscriminatorContext ctx, @SuppressWarnings("unused") JsonNodePath instanceLocation) {
186+
if (this.discriminatorContexts == null) {
187+
this.discriminatorContexts = new Stack<>();
188+
}
173189
this.discriminatorContexts.push(ctx);
174190
}
175191

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

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,15 @@
2727
import java.io.UnsupportedEncodingException;
2828
import java.net.URLDecoder;
2929
import java.nio.charset.StandardCharsets;
30-
import java.util.*;
30+
import java.util.ArrayList;
31+
import java.util.Collections;
32+
import java.util.Comparator;
33+
import java.util.Iterator;
34+
import java.util.LinkedHashSet;
35+
import java.util.List;
36+
import java.util.Map.Entry;
37+
import java.util.Objects;
38+
import java.util.Set;
3139
import java.util.function.Consumer;
3240

3341
/**
@@ -451,10 +459,11 @@ private List<JsonValidator> read(JsonNode schemaNode) {
451459
} else {
452460
JsonValidator refValidator = null;
453461

454-
Iterator<String> pnames = schemaNode.fieldNames();
455-
while (pnames.hasNext()) {
456-
String pname = pnames.next();
457-
JsonNode nodeToUse = schemaNode.get(pname);
462+
Iterator<Entry<String, JsonNode>> iterator = schemaNode.fields();
463+
while (iterator.hasNext()) {
464+
Entry<String, JsonNode> entry = iterator.next();
465+
String pname = entry.getKey();
466+
JsonNode nodeToUse = entry.getValue();
458467

459468
JsonNodePath path = getEvaluationPath().append(pname);
460469
SchemaLocation schemaPath = getSchemaLocation().append(pname);
@@ -536,15 +545,14 @@ private long activeDialect() {
536545

537546
@Override
538547
public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode jsonNode, JsonNode rootNode, JsonNodePath instanceLocation) {
539-
if (validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
548+
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
540549
ObjectNode discriminator = (ObjectNode) schemaNode.get("discriminator");
541550
if (null != discriminator && null != executionContext.getCurrentDiscriminatorContext()) {
542551
executionContext.getCurrentDiscriminatorContext().registerDiscriminator(schemaLocation,
543552
discriminator);
544553
}
545554
}
546555

547-
SchemaValidatorsConfig config = this.validationContext.getConfig();
548556
SetView<ValidationMessage> errors = null;
549557
// Set the walkEnabled and isValidationEnabled flag in internal validator state.
550558
setValidatorState(executionContext, false, true);
@@ -566,7 +574,7 @@ public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNo
566574
}
567575
}
568576

569-
if (config.isOpenAPI3StyleDiscriminators()) {
577+
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
570578
ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator");
571579
if (null != discriminator) {
572580
final DiscriminatorContext discriminatorContext = executionContext
@@ -1137,15 +1145,13 @@ public boolean isRecursiveAnchor() {
11371145
*/
11381146
public ExecutionContext createExecutionContext() {
11391147
SchemaValidatorsConfig config = validationContext.getConfig();
1140-
CollectorContext collectorContext = new CollectorContext();
1141-
11421148
// Copy execution config defaults from validation config
11431149
ExecutionConfig executionConfig = new ExecutionConfig();
11441150
executionConfig.setLocale(config.getLocale());
11451151
executionConfig.setFormatAssertionsEnabled(config.getFormatAssertionsEnabled());
11461152
executionConfig.setFailFast(config.isFailFast());
11471153

1148-
ExecutionContext executionContext = new ExecutionContext(executionConfig, collectorContext);
1154+
ExecutionContext executionContext = new ExecutionContext(executionConfig);
11491155
if(config.getExecutionContextCustomizer() != null) {
11501156
config.getExecutionContextCustomizer().customize(executionContext, validationContext);
11511157
}

0 commit comments

Comments
 (0)