Skip to content

Commit 5a94df7

Browse files
authored
Refactor of paths (#915)
* Refactor path * Refactor * Fix paths in validation messages * Fix paths in validation messages * Reduce repeated calls * Refactor schema path * Refactor validation message * Refactor validation message * Refactor validation message * Fix * Fix test expectations * Add evaluationPath to walk event * Rename * Refactor * Fix walk * Rename schemaPath to schemaLocation * Fix items tuple schema location * Rename at to instanceLocation * Rename keyWordName to keyword * Use factory method instead of reflection * change getValidators to use evaluation path and fix paths * Refactor message building * Refactor to use message builder * Refactor parse error code * Fix evaluationPath for IfValidator * Fix paths for additionalItems * Refactor to make use of ValidatorTypeCode optional to create a BaseJsonValidator * Refactor to remove customMessage and errorCodeKey from the ValidatorTypeCode * Update javadocs * Add keyword to JsonValidator * Add javadoc * Support customized formats * Add configuration for annotation allow lists * Optimise * Optimise required property evaluation * Set schema location * Validator sort * Fix inconsistent validation for anyOf * Fix incorrect test expectation * Add schema location * Use schema location * Refactor * Rename resolve to append * Use schema retrieval uri if $id not present * Refactor schema location * Optimise performance * Update walkers doc
1 parent 5dd0be3 commit 5a94df7

File tree

118 files changed

+3778
-1366
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+3778
-1366
lines changed

doc/walkers.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,20 @@ public interface JsonSchemaWalker {
1717
* cutting concerns like logging or instrumentation. This method also performs
1818
* the validation if {@code shouldValidateSchema} is set to true. <br>
1919
* <br>
20-
* {@link BaseJsonValidator#walk(JsonNode, JsonNode, String, boolean)} provides
21-
* a default implementation of this method. However keywords that parse
20+
* {@link BaseJsonValidator#walk(ExecutionContext, JsonNode, JsonNode, JsonNodePath, boolean)} provides
21+
* a default implementation of this method. However validators that parse
2222
* sub-schemas should override this method to call walk method on those
23-
* subschemas.
23+
* sub-schemas.
2424
*
25+
* @param executionContext ExecutionContext
2526
* @param node JsonNode
2627
* @param rootNode JsonNode
27-
* @param at String
28+
* @param instanceLocation JsonNodePath
2829
* @param shouldValidateSchema boolean
2930
* @return a set of validation messages if shouldValidateSchema is true.
3031
*/
31-
Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema);
32+
Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
33+
JsonNodePath instanceLocation, boolean shouldValidateSchema);
3234
}
3335

3436
```
@@ -41,10 +43,11 @@ The JSONValidator interface extends this new interface thus allowing all the val
4143
* validate method if shouldValidateSchema is enabled.
4244
*/
4345
@Override
44-
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
46+
public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode,
47+
JsonNodePath instanceLocation, boolean shouldValidateSchema);
4548
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
4649
if (shouldValidateSchema) {
47-
validationMessages = validate(node, rootNode, at);
50+
validationMessages = validate(executionContext, node, rootNode, instanceLocation);
4851
}
4952
return validationMessages;
5053
}
@@ -54,7 +57,7 @@ The JSONValidator interface extends this new interface thus allowing all the val
5457
A new walk method added to the JSONSchema class allows us to walk through the JSONSchema.
5558

5659
```java
57-
public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
60+
public ValidationResult walk(JsonNode node, boolean shouldValidateSchema) {
5861
// Create the collector context object.
5962
CollectorContext collectorContext = new CollectorContext();
6063
// Set the collector context in thread info, this is unique for every thread.
@@ -66,7 +69,7 @@ A new walk method added to the JSONSchema class allows us to walk through the JS
6669
ValidationResult validationResult = new ValidationResult(errors, collectorContext);
6770
return validationResult;
6871
}
69-
72+
7073
@Override
7174
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
7275
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
@@ -174,14 +177,15 @@ Following snippet shows the details captured by WalkEvent instance.
174177
```java
175178
public class WalkEvent {
176179

177-
private String schemaPath;
180+
private ExecutionContext executionContext;
181+
private SchemaLocation schemaLocation;
182+
private JsonNodePath evaluationPath;
178183
private JsonNode schemaNode;
179184
private JsonSchema parentSchema;
180-
private String keyWordName;
185+
private String keyword;
181186
private JsonNode node;
182187
private JsonNode rootNode;
183-
private String at;
184-
188+
private JsonNodePath instanceLocation;
185189
```
186190

187191
### Sample Flow
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright (c) 2023 the original author or authors.
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+
package com.networknt.schema;
17+
18+
import java.util.Objects;
19+
20+
/**
21+
* The absolute IRI is an IRI without the fragment.
22+
* <p>
23+
* absolute-IRI = scheme ":" ihier-part [ "?" iquery ]
24+
* <p>
25+
* This does not attempt to validate whether the value really conforms to an
26+
* absolute IRI format as in earlier drafts the IDs are not defined as such.
27+
*/
28+
public class AbsoluteIri {
29+
private final String value;
30+
31+
/**
32+
* Constructs a new IRI given the value.
33+
*
34+
* @param value
35+
*/
36+
public AbsoluteIri(String value) {
37+
this.value = value;
38+
}
39+
40+
/**
41+
* Constructs a new IRI given the value.
42+
*
43+
* @param iri the value
44+
* @return the new absolute IRI
45+
*/
46+
public static AbsoluteIri of(String iri) {
47+
return new AbsoluteIri(iri);
48+
}
49+
50+
/**
51+
* Constructs a new IRI by parsing the given string and then resolving it
52+
* against this IRI.
53+
*
54+
* @param iri to resolve
55+
* @return the new absolute IRI
56+
*/
57+
public AbsoluteIri resolve(String iri) {
58+
return new AbsoluteIri(resolve(this.value, iri));
59+
}
60+
61+
/**
62+
* Gets the scheme of the IRI.
63+
*
64+
* @return the scheme
65+
*/
66+
public String getScheme() {
67+
return getScheme(this.value);
68+
}
69+
70+
/**
71+
* Returns the scheme and authority components of the IRI.
72+
*
73+
* @return the scheme and authority components
74+
*/
75+
protected String getSchemeAuthority() {
76+
return getSchemeAuthority(this.value);
77+
}
78+
79+
/**
80+
* Constructs a new IRI by parsing the given string and then resolving it
81+
* against this IRI.
82+
*
83+
* @param parent the parent absolute IRI
84+
* @param iri to resolve
85+
* @return the new absolute IRI
86+
*/
87+
public static String resolve(String parent, String iri) {
88+
if (iri.contains(":")) {
89+
// IRI is absolute
90+
return iri;
91+
} else {
92+
// IRI is relative to this
93+
if (parent == null) {
94+
return iri;
95+
}
96+
if (iri.startsWith("/")) {
97+
// IRI is relative to this root
98+
return getSchemeAuthority(parent) + iri;
99+
} else {
100+
// IRI is relative to this path
101+
String base = parent;
102+
int scheme = parent.indexOf("://");
103+
if (scheme == -1) {
104+
scheme = 0;
105+
} else {
106+
scheme = scheme + 3;
107+
}
108+
int slash = parent.lastIndexOf('/');
109+
if (slash != -1 && slash > scheme) {
110+
base = parent.substring(0, slash);
111+
}
112+
return base + "/" + iri;
113+
}
114+
}
115+
}
116+
117+
/**
118+
* Returns the scheme and authority components of the IRI.
119+
*
120+
* @param iri to parse
121+
* @return the scheme and authority components
122+
*/
123+
protected static String getSchemeAuthority(String iri) {
124+
if (iri == null) {
125+
return "";
126+
}
127+
// iri refers to root
128+
int start = iri.indexOf("://");
129+
if (start == -1) {
130+
start = 0;
131+
} else {
132+
start = start + 3;
133+
}
134+
int end = iri.indexOf('/', start);
135+
return end != -1 ? iri.substring(0, end) : iri;
136+
}
137+
138+
/**
139+
* Returns the scheme of the IRI.
140+
*
141+
* @param iri to parse
142+
* @return the scheme
143+
*/
144+
public static String getScheme(String iri) {
145+
if (iri == null) {
146+
return "";
147+
}
148+
// iri refers to root
149+
int start = iri.indexOf(":");
150+
if (start == -1) {
151+
return "";
152+
} else {
153+
return iri.substring(0, start);
154+
}
155+
}
156+
157+
@Override
158+
public String toString() {
159+
return this.value;
160+
}
161+
162+
@Override
163+
public int hashCode() {
164+
return Objects.hash(this.value);
165+
}
166+
167+
@Override
168+
public boolean equals(Object obj) {
169+
if (this == obj)
170+
return true;
171+
if (obj == null)
172+
return false;
173+
if (getClass() != obj.getClass())
174+
return false;
175+
AbsoluteIri other = (AbsoluteIri) obj;
176+
return Objects.equals(this.value, other.value);
177+
}
178+
179+
}

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

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,34 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
19+
public abstract class AbstractJsonValidator implements JsonValidator {
20+
private final SchemaLocation schemaLocation;
21+
private final JsonNodePath evaluationPath;
22+
private final Keyword keyword;
2023

21-
import java.util.Collections;
22-
import java.util.Set;
24+
public AbstractJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, Keyword keyword) {
25+
this.schemaLocation = schemaLocation;
26+
this.evaluationPath = evaluationPath;
27+
this.keyword = keyword;
28+
}
2329

24-
public abstract class AbstractJsonValidator implements JsonValidator {
30+
@Override
31+
public SchemaLocation getSchemaLocation() {
32+
return schemaLocation;
33+
}
2534

26-
public Set<ValidationMessage> validate(ExecutionContext executionContext, JsonNode node) {
27-
return validate(executionContext, node, node, PathType.LEGACY.getRoot());
35+
@Override
36+
public JsonNodePath getEvaluationPath() {
37+
return evaluationPath;
2838
}
2939

3040
@Override
31-
public Set<ValidationMessage> walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
32-
Set<ValidationMessage> validationMessages = Collections.emptySet();
33-
if (shouldValidateSchema) {
34-
validationMessages = validate(executionContext, node, rootNode, at);
35-
}
36-
return validationMessages;
37-
}
41+
public String getKeyword() {
42+
return keyword.getValue();
43+
}
44+
45+
@Override
46+
public String toString() {
47+
return getEvaluationPath().getName(-1);
48+
}
3849
}

0 commit comments

Comments
 (0)