From 85246f1783c5e4a6b9091556efe2b2ec83f0ddd8 Mon Sep 17 00:00:00 2001 From: Justin Tay <49700559+justin-tay@users.noreply.github.com> Date: Tue, 11 Jun 2024 14:24:08 +0800 Subject: [PATCH] Refactor to make more fields final --- .../networknt/schema/BaseJsonValidator.java | 48 ++++-- .../networknt/schema/CollectorContext.java | 23 ++- .../networknt/schema/ContainsValidator.java | 28 ++-- .../schema/ContentEncodingValidator.java | 2 +- .../networknt/schema/JsonNodeLocation.java | 43 ------ .../java/com/networknt/schema/JsonSchema.java | 139 +++++++++++++----- .../networknt/schema/JsonSchemaException.java | 7 +- .../networknt/schema/MaxItemsValidator.java | 14 +- .../networknt/schema/MaxLengthValidator.java | 11 +- .../networknt/schema/MaximumValidator.java | 14 +- .../networknt/schema/MinimumValidator.java | 14 +- .../schema/SchemaValidatorsConfig.java | 13 +- .../schema/ValidationMessageHandler.java | 115 +++++++-------- .../networknt/schema/ValidationResult.java | 7 +- .../schema/result/JsonNodeResults.java | 2 +- .../walk/DefaultItemWalkListenerRunner.java | 2 +- .../DefaultKeywordWalkListenerRunner.java | 2 +- .../DefaultPropertyWalkListenerRunner.java | 2 +- .../com/networknt/schema/walk/WalkFlow.java | 4 +- .../schema/CollectorContextTest.java | 19 +++ 20 files changed, 316 insertions(+), 193 deletions(-) delete mode 100644 src/main/java/com/networknt/schema/JsonNodeLocation.java diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java index 9576e6ec5..87e9311a7 100644 --- a/src/main/java/com/networknt/schema/BaseJsonValidator.java +++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.networknt.schema.annotation.JsonNodeAnnotation; import com.networknt.schema.i18n.DefaultMessageSource; +import com.networknt.schema.i18n.MessageSource; import org.slf4j.Logger; @@ -29,12 +30,15 @@ import java.util.Set; import java.util.function.Consumer; +/** + * Base {@link JsonValidator}. + */ public abstract class BaseJsonValidator extends ValidationMessageHandler implements JsonValidator { protected final boolean suppressSubSchemaRetrieval; protected final JsonNode schemaNode; - protected ValidationContext validationContext; + protected final ValidationContext validationContext; public BaseJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidatorTypeCode validatorType, ValidationContext validationContext) { @@ -60,15 +64,41 @@ public BaseJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationP } /** - * Copy constructor. - * - * @param copy to copy from + * Constructor to create a copy using fields. + * + * @param suppressSubSchemaRetrieval to suppress sub schema retrieval + * @param schemaNode the schema node + * @param validationContext the validation context + * @param errorMessageType the error message type + * @param customErrorMessagesEnabled whether custom error msessages are enabled + * @param messageSource the message source + * @param keyword the keyword + * @param parentSchema the parent schema + * @param schemaLocation the schema location + * @param evaluationPath the evaluation path + * @param evaluationParentSchema the evaluation parent schema + * @param errorMessage the error message */ - protected BaseJsonValidator(BaseJsonValidator copy) { - super(copy); - this.suppressSubSchemaRetrieval = copy.suppressSubSchemaRetrieval; - this.schemaNode = copy.schemaNode; - this.validationContext = copy.validationContext; + protected BaseJsonValidator( + /* Below from BaseJsonValidator */ + boolean suppressSubSchemaRetrieval, + JsonNode schemaNode, + ValidationContext validationContext, + /* Below from ValidationMessageHandler */ + ErrorMessageType errorMessageType, + boolean customErrorMessagesEnabled, + MessageSource messageSource, + Keyword keyword, + JsonSchema parentSchema, + SchemaLocation schemaLocation, + JsonNodePath evaluationPath, + JsonSchema evaluationParentSchema, + Map errorMessage) { + super(errorMessageType, customErrorMessagesEnabled, messageSource, keyword, + parentSchema, schemaLocation, evaluationPath, evaluationParentSchema, errorMessage); + this.suppressSubSchemaRetrieval = suppressSubSchemaRetrieval; + this.schemaNode = schemaNode; + this.validationContext = validationContext; } private static JsonSchema obtainSubSchemaNode(final JsonNode schemaNode, final ValidationContext validationContext) { diff --git a/src/main/java/com/networknt/schema/CollectorContext.java b/src/main/java/com/networknt/schema/CollectorContext.java index 2469fceda..21b1d6a4e 100644 --- a/src/main/java/com/networknt/schema/CollectorContext.java +++ b/src/main/java/com/networknt/schema/CollectorContext.java @@ -28,16 +28,35 @@ public class CollectorContext { /** * Map for holding the name and {@link Collector} or a simple Object. */ - private Map collectorMap = new HashMap<>(); + private final Map collectorMap; /** * Map for holding the name and {@link Collector} class collect method output. */ - private Map collectorLoadMap = new HashMap<>(); + private final Map collectorLoadMap; + /** + * Default constructor will use an unsynchronized HashMap to store data. This is + * suitable if the collector context is not shared with multiple threads. + */ public CollectorContext() { + this(new HashMap<>(), new HashMap<>()); } + /** + * Constructor that creates the context using the specified instances to store + * data. + *

+ * If for instance the collector context needs to be shared with multiple + * threads a ConcurrentHashMap can be used. + * + * @param collectorMap the collector map + * @param collectorLoadMap the collector load map + */ + public CollectorContext(Map collectorMap, Map collectorLoadMap) { + this.collectorMap = collectorMap; + this.collectorLoadMap = collectorLoadMap; + } /** * Adds a collector with give name. Preserving this method for backward diff --git a/src/main/java/com/networknt/schema/ContainsValidator.java b/src/main/java/com/networknt/schema/ContainsValidator.java index adcdcde10..ebfde5564 100644 --- a/src/main/java/com/networknt/schema/ContainsValidator.java +++ b/src/main/java/com/networknt/schema/ContainsValidator.java @@ -42,9 +42,9 @@ public class ContainsValidator extends BaseJsonValidator { private final JsonSchema schema; private final boolean isMinV201909; - private Integer min = null; - private Integer max = null; - + private final Integer min; + private final Integer max; + private Boolean hasUnevaluatedItemsValidator = null; public ContainsValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { @@ -55,19 +55,29 @@ public ContainsValidator(SchemaLocation schemaLocation, JsonNodePath evaluationP // slightly. this.isMinV201909 = MinV201909.getVersions().contains(this.validationContext.getMetaSchema().getSpecification()); + Integer currentMax = null; + Integer currentMin = null; if (schemaNode.isObject() || schemaNode.isBoolean()) { this.schema = validationContext.newSchema(schemaLocation, evaluationPath, schemaNode, parentSchema); JsonNode parentSchemaNode = parentSchema.getSchemaNode(); - Optional.ofNullable(parentSchemaNode.get(ValidatorTypeCode.MAX_CONTAINS.getValue())) - .filter(JsonNode::canConvertToExactIntegral) - .ifPresent(node -> this.max = node.intValue()); + Optional maxNode = Optional + .ofNullable(parentSchemaNode.get(ValidatorTypeCode.MAX_CONTAINS.getValue())) + .filter(JsonNode::canConvertToExactIntegral); + if (maxNode.isPresent()) { + currentMax = maxNode.get().intValue(); + } - Optional.ofNullable(parentSchemaNode.get(ValidatorTypeCode.MIN_CONTAINS.getValue())) - .filter(JsonNode::canConvertToExactIntegral) - .ifPresent(node -> this.min = node.intValue()); + Optional minNode = Optional + .ofNullable(parentSchemaNode.get(ValidatorTypeCode.MIN_CONTAINS.getValue())) + .filter(JsonNode::canConvertToExactIntegral); + if (minNode.isPresent()) { + currentMin = minNode.get().intValue(); + } } else { this.schema = null; } + this.max = currentMax; + this.min = currentMin; } @Override diff --git a/src/main/java/com/networknt/schema/ContentEncodingValidator.java b/src/main/java/com/networknt/schema/ContentEncodingValidator.java index 6f2bbbb68..5c9e29608 100644 --- a/src/main/java/com/networknt/schema/ContentEncodingValidator.java +++ b/src/main/java/com/networknt/schema/ContentEncodingValidator.java @@ -32,7 +32,7 @@ */ public class ContentEncodingValidator extends BaseJsonValidator { private static final Logger logger = LoggerFactory.getLogger(ContentEncodingValidator.class); - private String contentEncoding; + private final String contentEncoding; /** * Constructor. diff --git a/src/main/java/com/networknt/schema/JsonNodeLocation.java b/src/main/java/com/networknt/schema/JsonNodeLocation.java deleted file mode 100644 index 7f888c135..000000000 --- a/src/main/java/com/networknt/schema/JsonNodeLocation.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2024 the original author or authors. - * - * 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; - -/** - * JsonNode location information. - */ -public class JsonNodeLocation { - private final int line; - private final int column; - - public JsonNodeLocation(int line, int column) { - super(); - this.line = line; - this.column = column; - } - - public int getLine() { - return line; - } - - public int getColumn() { - return column; - } - - @Override - public String toString() { - return "JsonNodeLocation [line=" + line + ", column=" + column + "]"; - } -} diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java index 7257681d7..b110fe636 100644 --- a/src/main/java/com/networknt/schema/JsonSchema.java +++ b/src/main/java/com/networknt/schema/JsonSchema.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.networknt.schema.SpecVersion.VersionFlag; +import com.networknt.schema.i18n.MessageSource; import com.networknt.schema.utils.JsonNodes; import com.networknt.schema.utils.SetView; @@ -31,6 +32,7 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; @@ -50,17 +52,15 @@ */ public class JsonSchema extends BaseJsonValidator { private static final long V201909_VALUE = VersionFlag.V201909.getVersionFlagValue(); + private final String id; /** * The validators sorted and indexed by evaluation path. */ - private List validators; + private List validators = null; private boolean validatorsLoaded = false; private boolean recursiveAnchor = false; - - private TypeValidator typeValidator; - - private final String id; + private TypeValidator typeValidator = null; static JsonSchema from(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) { return new JsonSchema(validationContext, schemaLocation, evaluationPath, schemaNode, parent, suppressSubSchemaRetrieval); @@ -149,19 +149,56 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc } getValidators(); } - + /** - * Copy constructor. + * Constructor to create a copy using fields. * - * @param copy to copy from + * @param validators the validators + * @param validatorsLoaded whether the validators are preloaded + * @param recursiveAnchor whether this is has a recursive anchor + * @param typeValidator the type validator + * @param id the id + * @param suppressSubSchemaRetrieval to suppress sub schema retrieval + * @param schemaNode the schema node + * @param validationContext the validation context + * @param errorMessageType the error message type + * @param customErrorMessagesEnabled whether custom error msessages are enabled + * @param messageSource the message source + * @param keyword the keyword + * @param parentSchema the parent schema + * @param schemaLocation the schema location + * @param evaluationPath the evaluation path + * @param evaluationParentSchema the evaluation parent schema + * @param errorMessage the error message */ - protected JsonSchema(JsonSchema copy) { - super(copy); - this.validators = copy.validators; - this.validatorsLoaded = copy.validatorsLoaded; - this.recursiveAnchor = copy.recursiveAnchor; - this.typeValidator = copy.typeValidator; - this.id = copy.id; + protected JsonSchema( + /* Below from JsonSchema */ + List validators, + boolean validatorsLoaded, + boolean recursiveAnchor, + TypeValidator typeValidator, + String id, + /* Below from BaseJsonValidator */ + boolean suppressSubSchemaRetrieval, + JsonNode schemaNode, + ValidationContext validationContext, + /* Below from ValidationMessageHandler */ + ErrorMessageType errorMessageType, + boolean customErrorMessagesEnabled, + MessageSource messageSource, + Keyword keyword, + JsonSchema parentSchema, + SchemaLocation schemaLocation, + JsonNodePath evaluationPath, + JsonSchema evaluationParentSchema, + Map errorMessage) { + super(suppressSubSchemaRetrieval, schemaNode, validationContext, errorMessageType, customErrorMessagesEnabled, messageSource, keyword, + parentSchema, schemaLocation, evaluationPath, evaluationParentSchema, errorMessage); + this.validators = validators; + this.validatorsLoaded = validatorsLoaded; + this.recursiveAnchor = recursiveAnchor; + this.typeValidator = typeValidator; + this.id = id; } /** @@ -176,34 +213,68 @@ protected JsonSchema(JsonSchema copy) { * @return the schema */ public JsonSchema fromRef(JsonSchema refEvaluationParentSchema, JsonNodePath refEvaluationPath) { - JsonSchema copy = new JsonSchema(this); - copy.validationContext = new ValidationContext(copy.getValidationContext().getMetaSchema(), - copy.getValidationContext().getJsonSchemaFactory(), + ValidationContext validationContext = new ValidationContext(this.getValidationContext().getMetaSchema(), + this.getValidationContext().getJsonSchemaFactory(), refEvaluationParentSchema.validationContext.getConfig(), refEvaluationParentSchema.getValidationContext().getSchemaReferences(), refEvaluationParentSchema.getValidationContext().getSchemaResources(), refEvaluationParentSchema.getValidationContext().getDynamicAnchors()); - copy.evaluationPath = refEvaluationPath; - copy.evaluationParentSchema = refEvaluationParentSchema; + JsonNodePath evaluationPath = refEvaluationPath; + JsonSchema evaluationParentSchema = refEvaluationParentSchema; // Validator state is reset due to the changes in evaluation path - copy.validatorsLoaded = false; - copy.typeValidator = null; - copy.validators = null; - return copy; + boolean validatorsLoaded = false; + TypeValidator typeValidator = null; + List validators = null; + + return new JsonSchema( + /* Below from JsonSchema */ + validators, + validatorsLoaded, + recursiveAnchor, + typeValidator, + id, + /* Below from BaseJsonValidator */ + suppressSubSchemaRetrieval, + schemaNode, + validationContext, + /* Below from ValidationMessageHandler */ + errorMessageType, customErrorMessagesEnabled, messageSource, + keyword, parentSchema, schemaLocation, evaluationPath, + evaluationParentSchema, errorMessage); } public JsonSchema withConfig(SchemaValidatorsConfig config) { if (!this.getValidationContext().getConfig().equals(config)) { - JsonSchema copy = new JsonSchema(this); - copy.validationContext = new ValidationContext(copy.getValidationContext().getMetaSchema(), - copy.getValidationContext().getJsonSchemaFactory(), config, - copy.getValidationContext().getSchemaReferences(), - copy.getValidationContext().getSchemaResources(), - copy.getValidationContext().getDynamicAnchors()); - copy.validatorsLoaded = false; - copy.typeValidator = null; - copy.validators = null; - return copy; + ValidationContext validationContext = new ValidationContext(this.getValidationContext().getMetaSchema(), + this.getValidationContext().getJsonSchemaFactory(), config, + this.getValidationContext().getSchemaReferences(), + this.getValidationContext().getSchemaResources(), + this.getValidationContext().getDynamicAnchors()); + boolean validatorsLoaded = false; + TypeValidator typeValidator = null; + List validators = null; + return new JsonSchema( + /* Below from JsonSchema */ + validators, + validatorsLoaded, + recursiveAnchor, + typeValidator, + id, + /* Below from BaseJsonValidator */ + suppressSubSchemaRetrieval, + schemaNode, + validationContext, + /* Below from ValidationMessageHandler */ + errorMessageType, + customErrorMessagesEnabled, + messageSource, + keyword, + parentSchema, + schemaLocation, + evaluationPath, + evaluationParentSchema, + errorMessage); + } return this; } diff --git a/src/main/java/com/networknt/schema/JsonSchemaException.java b/src/main/java/com/networknt/schema/JsonSchemaException.java index 2113d2e47..d667e8701 100644 --- a/src/main/java/com/networknt/schema/JsonSchemaException.java +++ b/src/main/java/com/networknt/schema/JsonSchemaException.java @@ -19,9 +19,12 @@ import java.util.Collections; import java.util.Set; +/** + * Represents an error when processing the JsonSchema. + */ public class JsonSchemaException extends RuntimeException { private static final long serialVersionUID = -7805792737596582110L; - private ValidationMessage validationMessage; + private final ValidationMessage validationMessage; public JsonSchemaException(ValidationMessage validationMessage) { this.validationMessage = validationMessage; @@ -29,10 +32,12 @@ public JsonSchemaException(ValidationMessage validationMessage) { public JsonSchemaException(String message) { super(message); + this.validationMessage = null; } public JsonSchemaException(Throwable throwable) { super(throwable); + this.validationMessage = null; } @Override diff --git a/src/main/java/com/networknt/schema/MaxItemsValidator.java b/src/main/java/com/networknt/schema/MaxItemsValidator.java index 57b0c0237..1f7748bf7 100644 --- a/src/main/java/com/networknt/schema/MaxItemsValidator.java +++ b/src/main/java/com/networknt/schema/MaxItemsValidator.java @@ -30,12 +30,14 @@ public class MaxItemsValidator extends BaseJsonValidator implements JsonValidato private static final Logger logger = LoggerFactory.getLogger(MaxItemsValidator.class); - private int max = 0; + private final int max; public MaxItemsValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { super(schemaLocation, evaluationPath, schemaNode, parentSchema, ValidatorTypeCode.MAX_ITEMS, validationContext); if (schemaNode.canConvertToExactIntegral()) { - max = schemaNode.intValue(); + this.max = schemaNode.intValue(); + } else { + this.max = 0; } } @@ -43,16 +45,16 @@ public Set validate(ExecutionContext executionContext, JsonNo debug(logger, executionContext, node, rootNode, instanceLocation); if (node.isArray()) { - if (node.size() > max) { + if (node.size() > this.max) { return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation) .locale(executionContext.getExecutionConfig().getLocale()) - .failFast(executionContext.isFailFast()).arguments(max, node.size()).build()); + .failFast(executionContext.isFailFast()).arguments(this.max, node.size()).build()); } } else if (this.validationContext.getConfig().isTypeLoose()) { - if (1 > max) { + if (1 > this.max) { return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation) .locale(executionContext.getExecutionConfig().getLocale()) - .failFast(executionContext.isFailFast()).arguments(max, 1).build()); + .failFast(executionContext.isFailFast()).arguments(this.max, 1).build()); } } diff --git a/src/main/java/com/networknt/schema/MaxLengthValidator.java b/src/main/java/com/networknt/schema/MaxLengthValidator.java index e304478c3..ec587fdef 100644 --- a/src/main/java/com/networknt/schema/MaxLengthValidator.java +++ b/src/main/java/com/networknt/schema/MaxLengthValidator.java @@ -29,13 +29,14 @@ public class MaxLengthValidator extends BaseJsonValidator implements JsonValidator { private static final Logger logger = LoggerFactory.getLogger(MaxLengthValidator.class); - private int maxLength; + private final int maxLength; public MaxLengthValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { super(schemaLocation, evaluationPath, schemaNode, parentSchema, ValidatorTypeCode.MAX_LENGTH, validationContext); - maxLength = Integer.MAX_VALUE; if (schemaNode != null && schemaNode.canConvertToExactIntegral()) { - maxLength = schemaNode.intValue(); + this.maxLength = schemaNode.intValue(); + } else { + this.maxLength = Integer.MAX_VALUE; } } @@ -47,10 +48,10 @@ public Set validate(ExecutionContext executionContext, JsonNo // ignore no-string typs return Collections.emptySet(); } - if (node.textValue().codePointCount(0, node.textValue().length()) > maxLength) { + if (node.textValue().codePointCount(0, node.textValue().length()) > this.maxLength) { return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation) .locale(executionContext.getExecutionConfig().getLocale()) - .failFast(executionContext.isFailFast()).arguments(maxLength).build()); + .failFast(executionContext.isFailFast()).arguments(this.maxLength).build()); } return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/MaximumValidator.java b/src/main/java/com/networknt/schema/MaximumValidator.java index 261fa4717..1bbef9e7a 100644 --- a/src/main/java/com/networknt/schema/MaximumValidator.java +++ b/src/main/java/com/networknt/schema/MaximumValidator.java @@ -34,7 +34,7 @@ public class MaximumValidator extends BaseJsonValidator { private static final Logger logger = LoggerFactory.getLogger(MaximumValidator.class); private static final String PROPERTY_EXCLUSIVE_MAXIMUM = "exclusiveMaximum"; - private boolean excludeEqual = false; + private final boolean excludeEqual; private final ThresholdMixin typedMaximum; @@ -47,14 +47,16 @@ public MaximumValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPa JsonNode exclusiveMaximumNode = getParentSchema().getSchemaNode().get(PROPERTY_EXCLUSIVE_MAXIMUM); if (exclusiveMaximumNode != null && exclusiveMaximumNode.isBoolean()) { - excludeEqual = exclusiveMaximumNode.booleanValue(); + this.excludeEqual = exclusiveMaximumNode.booleanValue(); + } else { + this.excludeEqual = false; } final String maximumText = schemaNode.asText(); if ((schemaNode.isLong() || schemaNode.isInt()) && (JsonType.INTEGER.toString().equals(getNodeFieldType()))) { // "integer", and within long range final long lm = schemaNode.asLong(); - typedMaximum = new ThresholdMixin() { + this.typedMaximum = new ThresholdMixin() { @Override public boolean crossesThreshold(JsonNode node) { if (node.isBigInteger()) { @@ -78,7 +80,7 @@ public String thresholdValue() { } }; } else { - typedMaximum = new ThresholdMixin() { + this.typedMaximum = new ThresholdMixin() { @Override public boolean crossesThreshold(JsonNode node) { if (schemaNode.isDouble() && schemaNode.doubleValue() == Double.POSITIVE_INFINITY) { @@ -115,11 +117,11 @@ public Set validate(ExecutionContext executionContext, JsonNo return Collections.emptySet(); } - if (typedMaximum.crossesThreshold(node)) { + if (this.typedMaximum.crossesThreshold(node)) { return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation) .locale(executionContext.getExecutionConfig().getLocale()) .failFast(executionContext.isFailFast()) - .arguments(typedMaximum.thresholdValue()).build()); + .arguments(this.typedMaximum.thresholdValue()).build()); } return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/MinimumValidator.java b/src/main/java/com/networknt/schema/MinimumValidator.java index 3f359482c..dc557a185 100644 --- a/src/main/java/com/networknt/schema/MinimumValidator.java +++ b/src/main/java/com/networknt/schema/MinimumValidator.java @@ -34,7 +34,7 @@ public class MinimumValidator extends BaseJsonValidator { private static final Logger logger = LoggerFactory.getLogger(MinimumValidator.class); private static final String PROPERTY_EXCLUSIVE_MINIMUM = "exclusiveMinimum"; - private boolean excludeEqual = false; + private final boolean excludeEqual; /** * In order to limit number of `if` statements in `validate` method, all the @@ -51,14 +51,16 @@ public MinimumValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPa JsonNode exclusiveMinimumNode = getParentSchema().getSchemaNode().get(PROPERTY_EXCLUSIVE_MINIMUM); if (exclusiveMinimumNode != null && exclusiveMinimumNode.isBoolean()) { - excludeEqual = exclusiveMinimumNode.booleanValue(); + this.excludeEqual = exclusiveMinimumNode.booleanValue(); + } else { + this.excludeEqual = false; } final String minimumText = schemaNode.asText(); if ((schemaNode.isLong() || schemaNode.isInt()) && JsonType.INTEGER.toString().equals(getNodeFieldType())) { // "integer", and within long range final long lmin = schemaNode.asLong(); - typedMinimum = new ThresholdMixin() { + this.typedMinimum = new ThresholdMixin() { @Override public boolean crossesThreshold(JsonNode node) { if (node.isBigInteger()) { @@ -84,7 +86,7 @@ public String thresholdValue() { }; } else { - typedMinimum = new ThresholdMixin() { + this.typedMinimum = new ThresholdMixin() { @Override public boolean crossesThreshold(JsonNode node) { // jackson's BIG_DECIMAL parsing is limited. see https://github.com/FasterXML/jackson-databind/issues/1770 @@ -122,11 +124,11 @@ public Set validate(ExecutionContext executionContext, JsonNo return Collections.emptySet(); } - if (typedMinimum.crossesThreshold(node)) { + if (this.typedMinimum.crossesThreshold(node)) { return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation) .locale(executionContext.getExecutionConfig().getLocale()) .failFast(executionContext.isFailFast()) - .arguments(typedMinimum.thresholdValue()).build()); + .arguments(this.typedMinimum.thresholdValue()).build()); } return Collections.emptySet(); } diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java index 1240da3ec..8787685eb 100644 --- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java +++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java @@ -32,6 +32,9 @@ import java.util.Map; import java.util.Objects; +/** + * Configuration for validators. + */ public class SchemaValidatorsConfig { public static final int DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH = 40; @@ -78,7 +81,7 @@ public class SchemaValidatorsConfig { * When set to true, "messages" provided in schema are used for forming validation errors * else default messages are used */ - private boolean isCustomMessageSupported = true; + private boolean customMessageSupported = true; /** * When set to true, support for discriminators is enabled for validations of @@ -285,11 +288,11 @@ public void setJavaSemantics(boolean javaSemantics) { } public boolean isCustomMessageSupported() { - return isCustomMessageSupported; + return customMessageSupported; } public void setCustomMessageSupported(boolean customMessageSupported) { - this.isCustomMessageSupported = customMessageSupported; + this.customMessageSupported = customMessageSupported; } public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) { @@ -358,13 +361,13 @@ WalkListenerRunner getItemWalkListenerRunner() { return this.itemWalkListenerRunner; } - private WalkListenerRunner keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap()); + private final WalkListenerRunner keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap()); WalkListenerRunner getKeywordWalkListenerRunner() { return this.keywordWalkListenerRunner; } - private WalkListenerRunner propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners()); + private final WalkListenerRunner propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners()); WalkListenerRunner getPropertyWalkListenerRunner() { return this.propertyWalkListenerRunner; diff --git a/src/main/java/com/networknt/schema/ValidationMessageHandler.java b/src/main/java/com/networknt/schema/ValidationMessageHandler.java index 1bacaffdb..1183f4ada 100644 --- a/src/main/java/com/networknt/schema/ValidationMessageHandler.java +++ b/src/main/java/com/networknt/schema/ValidationMessageHandler.java @@ -9,48 +9,78 @@ import java.util.Map; import java.util.Objects; +/** + * Validation message handler. + */ public abstract class ValidationMessageHandler { + protected final ErrorMessageType errorMessageType; + protected final boolean customErrorMessagesEnabled; protected final MessageSource messageSource; - protected ErrorMessageType errorMessageType; - - protected SchemaLocation schemaLocation; - protected JsonNodePath evaluationPath; - protected JsonSchema evaluationParentSchema; - - protected JsonSchema parentSchema; - - protected boolean customErrorMessagesEnabled; - protected Map errorMessage; - - protected Keyword keyword; + protected final Keyword keyword; + protected final JsonSchema parentSchema; + protected final SchemaLocation schemaLocation; + protected final JsonNodePath evaluationPath; + protected final JsonSchema evaluationParentSchema; + protected final Map errorMessage; protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean customErrorMessagesEnabled, MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, SchemaLocation schemaLocation, JsonNodePath evaluationPath) { - this.errorMessageType = errorMessageType; + ErrorMessageType currentErrorMessageType = errorMessageType; this.messageSource = messageSource; this.schemaLocation = Objects.requireNonNull(schemaLocation); this.evaluationPath = Objects.requireNonNull(evaluationPath); this.parentSchema = parentSchema; + this.evaluationParentSchema = null; this.customErrorMessagesEnabled = customErrorMessagesEnabled; - updateKeyword(keyword); + this.keyword = keyword; + + Map currentErrorMessage = null; + + if (this.keyword != null) { + if (this.customErrorMessagesEnabled && keyword != null && parentSchema != null) { + currentErrorMessage = getErrorMessage(parentSchema.getSchemaNode(), keyword.getValue()); + } + String errorCodeKey = getErrorCodeKey(keyword.getValue()); + if (errorCodeKey != null && this.parentSchema != null) { + JsonNode errorCodeNode = this.parentSchema.getSchemaNode().get(errorCodeKey); + if (errorCodeNode != null && errorCodeNode.isTextual()) { + String errorCodeText = errorCodeNode.asText(); + if (StringUtils.isNotBlank(errorCodeText)) { + currentErrorMessageType = CustomErrorMessageType.of(errorCodeText); + } + } + } + } + this.errorMessageType = currentErrorMessageType; + this.errorMessage = currentErrorMessage; } /** - * Copy constructor. - * - * @param copy to copy from + * Constructor to create a copy using fields. + * + * @param errorMessageType the error message type + * @param customErrorMessagesEnabled whether custom error msessages are enabled + * @param messageSource the message source + * @param keyword the keyword + * @param parentSchema the parent schema + * @param schemaLocation the schema location + * @param evaluationPath the evaluation path + * @param evaluationParentSchema the evaluation parent schema + * @param errorMessage the error message */ - protected ValidationMessageHandler(ValidationMessageHandler copy) { - this.messageSource = copy.messageSource; - this.errorMessageType = copy.errorMessageType; - this.schemaLocation = copy.schemaLocation; - this.evaluationPath = copy.evaluationPath; - this.parentSchema = copy.parentSchema; - this.evaluationParentSchema = copy.evaluationParentSchema; - this.customErrorMessagesEnabled = copy.customErrorMessagesEnabled; - this.errorMessage = copy.errorMessage; - this.keyword = copy.keyword; + protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean customErrorMessagesEnabled, + MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, SchemaLocation schemaLocation, + JsonNodePath evaluationPath, JsonSchema evaluationParentSchema, Map errorMessage) { + this.errorMessageType = errorMessageType; + this.customErrorMessagesEnabled = customErrorMessagesEnabled; + this.messageSource = messageSource; + this.keyword = keyword; + this.parentSchema = parentSchema; + this.schemaLocation = schemaLocation; + this.evaluationPath = evaluationPath; + this.evaluationParentSchema = evaluationParentSchema; + this.errorMessage = errorMessage; } protected MessageSourceValidationMessage.Builder message() { @@ -67,37 +97,6 @@ protected ErrorMessageType getErrorMessageType() { return this.errorMessageType; } - protected void parseErrorCode(String errorCodeKey) { - if (errorCodeKey != null && this.parentSchema != null) { - JsonNode errorCodeNode = this.parentSchema.getSchemaNode().get(errorCodeKey); - if (errorCodeNode != null && errorCodeNode.isTextual()) { - String errorCodeText = errorCodeNode.asText(); - if (StringUtils.isNotBlank(errorCodeText)) { - this.errorMessageType = CustomErrorMessageType.of(errorCodeText); - } - } - } - } - - protected void updateValidatorType(ValidatorTypeCode validatorTypeCode) { - updateKeyword(validatorTypeCode); - updateErrorMessageType(validatorTypeCode); - } - - protected void updateErrorMessageType(ErrorMessageType errorMessageType) { - this.errorMessageType = errorMessageType; - } - - protected void updateKeyword(Keyword keyword) { - this.keyword = keyword; - if (this.keyword != null) { - if (this.customErrorMessagesEnabled && keyword != null && parentSchema != null) { - this.errorMessage = getErrorMessage(parentSchema.getSchemaNode(), keyword.getValue()); - } - parseErrorCode(getErrorCodeKey(keyword.getValue())); - } - } - /** * Gets the custom error message to use. * diff --git a/src/main/java/com/networknt/schema/ValidationResult.java b/src/main/java/com/networknt/schema/ValidationResult.java index 7c04196ad..74cd7ca20 100644 --- a/src/main/java/com/networknt/schema/ValidationResult.java +++ b/src/main/java/com/networknt/schema/ValidationResult.java @@ -17,11 +17,14 @@ import java.util.Set; +/** + * Represents a validation result. + */ public class ValidationResult { - private Set validationMessages; + private final Set validationMessages; - private ExecutionContext executionContext; + private final ExecutionContext executionContext; public ValidationResult(Set validationMessages, ExecutionContext executionContext) { super(); diff --git a/src/main/java/com/networknt/schema/result/JsonNodeResults.java b/src/main/java/com/networknt/schema/result/JsonNodeResults.java index 78b51f3bd..0b3f5e893 100644 --- a/src/main/java/com/networknt/schema/result/JsonNodeResults.java +++ b/src/main/java/com/networknt/schema/result/JsonNodeResults.java @@ -31,7 +31,7 @@ public class JsonNodeResults { /** * Stores the invalid results. */ - private Map> values = new HashMap<>(); + private final Map> values = new HashMap<>(); public void setResult(JsonNodePath instanceLocation, SchemaLocation schemaLocation, JsonNodePath evaluationPath, boolean valid) { diff --git a/src/main/java/com/networknt/schema/walk/DefaultItemWalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/DefaultItemWalkListenerRunner.java index eba7f054b..fbd5c36c6 100644 --- a/src/main/java/com/networknt/schema/walk/DefaultItemWalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/DefaultItemWalkListenerRunner.java @@ -12,7 +12,7 @@ public class DefaultItemWalkListenerRunner extends AbstractWalkListenerRunner { - private List itemWalkListeners; + private final List itemWalkListeners; public DefaultItemWalkListenerRunner(List itemWalkListeners) { this.itemWalkListeners = itemWalkListeners; diff --git a/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java index 0435f5323..fd75835f8 100644 --- a/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/DefaultKeywordWalkListenerRunner.java @@ -9,7 +9,7 @@ public class DefaultKeywordWalkListenerRunner extends AbstractWalkListenerRunner { - private Map> keywordWalkListenersMap; + private final Map> keywordWalkListenersMap; public DefaultKeywordWalkListenerRunner(Map> keywordWalkListenersMap) { this.keywordWalkListenersMap = keywordWalkListenersMap; diff --git a/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java b/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java index e10253f97..f0c101b72 100644 --- a/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java +++ b/src/main/java/com/networknt/schema/walk/DefaultPropertyWalkListenerRunner.java @@ -12,7 +12,7 @@ public class DefaultPropertyWalkListenerRunner extends AbstractWalkListenerRunner { - private List propertyWalkListeners; + private final List propertyWalkListeners; public DefaultPropertyWalkListenerRunner(List propertyWalkListeners) { this.propertyWalkListeners = propertyWalkListeners; diff --git a/src/main/java/com/networknt/schema/walk/WalkFlow.java b/src/main/java/com/networknt/schema/walk/WalkFlow.java index af2da7b5c..e4bc43fd6 100644 --- a/src/main/java/com/networknt/schema/walk/WalkFlow.java +++ b/src/main/java/com/networknt/schema/walk/WalkFlow.java @@ -8,9 +8,9 @@ public enum WalkFlow { CONTINUE("ContinueToWalk", "continue to invoke the walk method and other listeners"); - private String name; + private final String name; - private String description; + private final String description; WalkFlow(String name, String description) { this.name = name; diff --git a/src/test/java/com/networknt/schema/CollectorContextTest.java b/src/test/java/com/networknt/schema/CollectorContextTest.java index ea281e7f8..4d4b1e97d 100644 --- a/src/test/java/com/networknt/schema/CollectorContextTest.java +++ b/src/test/java/com/networknt/schema/CollectorContextTest.java @@ -24,8 +24,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.io.IOException; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class CollectorContextTest { @@ -380,4 +384,19 @@ private Map getDatasourceMap() { map.put("sample3", "actual_value_added_to_context3"); return map; } + + @Test + void constructor() { + CollectorContext context = new CollectorContext(); + assertTrue(context.getCollectorMap().isEmpty()); + assertTrue(context.getAll().isEmpty()); + } + + @Test + void constructorWithMap() { + ConcurrentHashMap collectorMap = new ConcurrentHashMap<>(); + ConcurrentHashMap collectorLoadMap = new ConcurrentHashMap<>(); + CollectorContext context = new CollectorContext(collectorMap, collectorLoadMap); + assertSame(collectorMap, context.getCollectorMap()); + } }