Skip to content

Support GraalVM and refactor #972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/main/java/com/networknt/schema/AbstractJsonValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public abstract class AbstractJsonValidator implements JsonValidator {
private final SchemaLocation schemaLocation;
private final JsonNode schemaNode;
private final JsonNodePath evaluationPath;
private final Keyword keyword;
private final String keyword;

/**
* Constructor.
Expand All @@ -41,7 +41,7 @@ public abstract class AbstractJsonValidator implements JsonValidator {
public AbstractJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, Keyword keyword, JsonNode schemaNode) {
this.schemaLocation = schemaLocation;
this.evaluationPath = evaluationPath;
this.keyword = keyword;
this.keyword = keyword.getValue();
this.schemaNode = schemaNode;
}

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

@Override
public String getKeyword() {
return keyword.getValue();
return keyword;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

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

private Boolean hasUnevaluatedPropertiesValidator;

Expand Down Expand Up @@ -64,9 +71,12 @@ public AdditionalPropertiesValidator(SchemaLocation schemaLocation, JsonNodePath

JsonNode patternPropertiesNode = parentSchema.getSchemaNode().get(PatternPropertiesValidator.PROPERTY);
if (patternPropertiesNode != null) {
this.patternProperties = new ArrayList<>();
for (Iterator<String> it = patternPropertiesNode.fieldNames(); it.hasNext(); ) {
patternProperties.add(RegularExpression.compile(it.next(), validationContext));
}
} else {
this.patternProperties = Collections.emptyList();
}
}

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

Set<ValidationMessage> errors = null;

for (Iterator<String> it = node.fieldNames(); it.hasNext(); ) {
String pname = it.next();
for (Iterator<Entry<String, JsonNode>> it = node.fields(); it.hasNext(); ) {
Entry<String, JsonNode> entry = it.next();
String pname = entry.getKey();
// skip the context items
if (pname.startsWith("#")) {
continue;
Expand All @@ -120,15 +131,18 @@ public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNo
if (additionalPropertiesSchema != null) {
ValidatorState state = executionContext.getValidatorState();
if (state != null && state.isWalkEnabled()) {
Set<ValidationMessage> results = additionalPropertiesSchema.walk(executionContext, node.get(pname), rootNode, instanceLocation.append(pname), state.isValidationEnabled());
Set<ValidationMessage> results = additionalPropertiesSchema.walk(executionContext,
entry.getValue(), rootNode, instanceLocation.append(pname),
state.isValidationEnabled());
if (!results.isEmpty()) {
if (errors == null) {
errors = new LinkedHashSet<>();
}
errors.addAll(results);
}
} else {
Set<ValidationMessage> results = additionalPropertiesSchema.validate(executionContext, node.get(pname), rootNode, instanceLocation.append(pname));
Set<ValidationMessage> results = additionalPropertiesSchema.validate(executionContext,
entry.getValue(), rootNode, instanceLocation.append(pname));
if (!results.isEmpty()) {
if (errors == null) {
errors = new LinkedHashSet<>();
Expand Down
68 changes: 68 additions & 0 deletions src/main/java/com/networknt/schema/AnnotationKeyword.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2016 Network New Technologies Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.networknt.schema;

import com.fasterxml.jackson.databind.JsonNode;

import java.util.Collections;
import java.util.Set;

/**
* Used for Keywords that have no validation aspect, but are part of the metaschema, where annotations may need to be collected.
*/
public class AnnotationKeyword extends AbstractKeyword {

private static final class Validator extends AbstractJsonValidator {
public Validator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
JsonSchema parentSchema, ValidationContext validationContext, Keyword keyword) {
super(schemaLocation, evaluationPath, keyword, schemaNode);
}

@Override
public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
if (collectAnnotations(executionContext)) {
Object value = getAnnotationValue(getSchemaNode());
if (value != null) {
putAnnotation(executionContext,
annotation -> annotation.instanceLocation(instanceLocation).value(value));
}
}
return Collections.emptySet();
}

protected Object getAnnotationValue(JsonNode schemaNode) {
if (schemaNode.isTextual()) {
return schemaNode.textValue();
} else if (schemaNode.isNumber()) {
return schemaNode.numberValue();
} else if (schemaNode.isObject()) {
return schemaNode;
}
return null;
}
}

public AnnotationKeyword(String keyword) {
super(keyword);
}

@Override
public JsonValidator newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception {
return new Validator(schemaLocation, evaluationPath, schemaNode, parentSchema, validationContext, this);
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/networknt/schema/BaseJsonValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,40 @@ protected void preloadJsonSchemas(final Collection<JsonSchema> schemas) {
}
}

public static class JsonNodePathLegacy {
private static final JsonNodePath INSTANCE = new JsonNodePath(PathType.LEGACY);
public static JsonNodePath getInstance() {
return INSTANCE;
}
}

public static class JsonNodePathJsonPointer {
private static final JsonNodePath INSTANCE = new JsonNodePath(PathType.JSON_POINTER);
public static JsonNodePath getInstance() {
return INSTANCE;
}
}

public static class JsonNodePathJsonPath {
private static final JsonNodePath INSTANCE = new JsonNodePath(PathType.JSON_PATH);
public static JsonNodePath getInstance() {
return INSTANCE;
}
}

/**
* Get the root path.
*
* @return The path.
*/
protected JsonNodePath atRoot() {
if (this.validationContext.getConfig().getPathType().equals(PathType.JSON_POINTER)) {
return JsonNodePathJsonPointer.getInstance();
} else if (this.validationContext.getConfig().getPathType().equals(PathType.LEGACY)) {
return JsonNodePathLegacy.getInstance();
} else if (this.validationContext.getConfig().getPathType().equals(PathType.JSON_PATH)) {
return JsonNodePathJsonPath.getInstance();
}
return new JsonNodePath(this.validationContext.getConfig().getPathType());
}

Expand Down
30 changes: 23 additions & 7 deletions src/main/java/com/networknt/schema/ExecutionContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
*/
public class ExecutionContext {
private ExecutionConfig executionConfig;
private CollectorContext collectorContext;
private CollectorContext collectorContext = null;
private ValidatorState validatorState = null;
private Stack<DiscriminatorContext> discriminatorContexts = new Stack<>();
private JsonNodeAnnotations annotations = new JsonNodeAnnotations();
private JsonNodeResults results = new JsonNodeResults();
private Stack<DiscriminatorContext> discriminatorContexts = null;
private JsonNodeAnnotations annotations = null;
private JsonNodeResults results = null;

/**
* This is used during the execution to determine if the validator should fail fast.
Expand All @@ -43,7 +43,7 @@ public class ExecutionContext {
* Creates an execution context.
*/
public ExecutionContext() {
this(new CollectorContext());
this(new ExecutionConfig(), null);
}

/**
Expand All @@ -61,7 +61,7 @@ public ExecutionContext(CollectorContext collectorContext) {
* @param executionConfig the execution configuration
*/
public ExecutionContext(ExecutionConfig executionConfig) {
this(executionConfig, new CollectorContext());
this(executionConfig, null);
}

/**
Expand All @@ -81,7 +81,10 @@ public ExecutionContext(ExecutionConfig executionConfig, CollectorContext collec
* @return the collector context
*/
public CollectorContext getCollectorContext() {
return collectorContext;
if (this.collectorContext == null) {
this.collectorContext = new CollectorContext();
}
return this.collectorContext;
}

/**
Expand Down Expand Up @@ -112,10 +115,16 @@ public void setExecutionConfig(ExecutionConfig executionConfig) {
}

public JsonNodeAnnotations getAnnotations() {
if (this.annotations == null) {
this.annotations = new JsonNodeAnnotations();
}
return annotations;
}

public JsonNodeResults getResults() {
if (this.results == null) {
this.results = new JsonNodeResults();
}
return results;
}

Expand Down Expand Up @@ -163,13 +172,20 @@ public void setValidatorState(ValidatorState validatorState) {
}

public DiscriminatorContext getCurrentDiscriminatorContext() {
if (this.discriminatorContexts == null) {
return null;
}

if (!this.discriminatorContexts.empty()) {
return this.discriminatorContexts.peek();
}
return null; // this is the case when we get on a schema that has a discriminator, but it's not used in anyOf
}

public void enterDiscriminatorContext(final DiscriminatorContext ctx, @SuppressWarnings("unused") JsonNodePath instanceLocation) {
if (this.discriminatorContexts == null) {
this.discriminatorContexts = new Stack<>();
}
this.discriminatorContexts.push(ctx);
}

Expand Down
28 changes: 17 additions & 11 deletions src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,15 @@
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

/**
Expand Down Expand Up @@ -451,10 +459,11 @@ private List<JsonValidator> read(JsonNode schemaNode) {
} else {
JsonValidator refValidator = null;

Iterator<String> pnames = schemaNode.fieldNames();
while (pnames.hasNext()) {
String pname = pnames.next();
JsonNode nodeToUse = schemaNode.get(pname);
Iterator<Entry<String, JsonNode>> iterator = schemaNode.fields();
while (iterator.hasNext()) {
Entry<String, JsonNode> entry = iterator.next();
String pname = entry.getKey();
JsonNode nodeToUse = entry.getValue();

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

@Override
public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode jsonNode, JsonNode rootNode, JsonNodePath instanceLocation) {
if (validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
ObjectNode discriminator = (ObjectNode) schemaNode.get("discriminator");
if (null != discriminator && null != executionContext.getCurrentDiscriminatorContext()) {
executionContext.getCurrentDiscriminatorContext().registerDiscriminator(schemaLocation,
discriminator);
}
}

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

if (config.isOpenAPI3StyleDiscriminators()) {
if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) {
ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator");
if (null != discriminator) {
final DiscriminatorContext discriminatorContext = executionContext
Expand Down Expand Up @@ -1137,15 +1145,13 @@ public boolean isRecursiveAnchor() {
*/
public ExecutionContext createExecutionContext() {
SchemaValidatorsConfig config = validationContext.getConfig();
CollectorContext collectorContext = new CollectorContext();

// Copy execution config defaults from validation config
ExecutionConfig executionConfig = new ExecutionConfig();
executionConfig.setLocale(config.getLocale());
executionConfig.setFormatAssertionsEnabled(config.getFormatAssertionsEnabled());
executionConfig.setFailFast(config.isFailFast());

ExecutionContext executionContext = new ExecutionContext(executionConfig, collectorContext);
ExecutionContext executionContext = new ExecutionContext(executionConfig);
if(config.getExecutionContextCustomizer() != null) {
config.getExecutionContextCustomizer().customize(executionContext, validationContext);
}
Expand Down
Loading