Skip to content

Commit 2512e21

Browse files
committed
Refactor path type
1 parent 1e1957f commit 2512e21

File tree

4 files changed

+81
-31
lines changed

4 files changed

+81
-31
lines changed

src/main/java/com/networknt/schema/path/NodePath.java

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

18+
import java.util.ArrayDeque;
1819
import java.util.Objects;
1920

2021
/**
@@ -202,14 +203,40 @@ public boolean contains(String segment) {
202203
@Override
203204
public String toString() {
204205
if (this.value == null) {
205-
String parentValue = this.parent == null ? type.getRoot() : this.parent.toString();
206+
if (pathSegmentIndex == -1 && this.pathSegment == null) {
207+
this.value = type.getRoot();
208+
return this.value;
209+
}
210+
211+
ArrayDeque<Object> pathSegments = new ArrayDeque<Object>();
206212
if (pathSegmentIndex != -1) {
207-
this.value = this.type.append(parentValue, pathSegmentIndex);
208-
} else if (pathSegment != null) {
209-
this.value = this.type.append(parentValue, pathSegment);
210-
} else {
211-
this.value = parentValue;
213+
pathSegments.push(this.pathSegmentIndex);
214+
} else if (this.pathSegment != null) {
215+
pathSegments.push(this.pathSegment);
216+
}
217+
NodePath parent = getParent();
218+
while (parent != null) {
219+
if (parent.pathSegmentIndex != -1) {
220+
pathSegments.push(parent.pathSegmentIndex);
221+
} else if (parent.pathSegment != null) {
222+
pathSegments.push(parent.pathSegment);
223+
}
224+
parent = parent.getParent();
225+
}
226+
227+
StringBuilder builder = new StringBuilder();
228+
String root = type.getRoot();
229+
if (root != null) {
230+
builder.append(root);
231+
}
232+
for (Object pathSegment : pathSegments) {
233+
if (pathSegment instanceof Number) {
234+
type.append(builder, ((Number) pathSegment).intValue());
235+
} else {
236+
type.append(builder, pathSegment.toString());
237+
}
212238
}
239+
this.value = builder.toString();
213240
}
214241
return this.value;
215242
}

src/main/java/com/networknt/schema/path/PathType.java

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.networknt.schema.path;
22

3-
import java.util.function.BiFunction;
3+
import java.util.function.BiConsumer;
44
import java.util.function.IntPredicate;
55

66
/**
@@ -11,18 +11,19 @@ public enum PathType {
1111
/**
1212
* The legacy approach, loosely based on JSONPath (but not guaranteed to give valid JSONPath expressions).
1313
*/
14-
LEGACY("$", (currentPath, token) -> currentPath + "." + replaceCommonSpecialCharactersIfPresent(token),
15-
(currentPath, index) -> currentPath + "[" + index + "]"),
14+
LEGACY("$", (currentPath, token) -> {
15+
currentPath.append(".").append(replaceCommonSpecialCharactersIfPresent(token));
16+
}, (currentPath, index) -> {
17+
currentPath.append("[").append(index).append("]");
18+
}),
1619

1720
/**
1821
* Paths as JSONPath expressions.
1922
*/
2023
JSON_PATH("$", (currentPath, token) -> {
21-
2224
if (token.isEmpty()) {
2325
throw new IllegalArgumentException("A JSONPath selector cannot be empty");
2426
}
25-
2627
/*
2728
* Accepted characters for shorthand paths:
2829
* - 'a' through 'z'
@@ -32,16 +33,18 @@ public enum PathType {
3233
* - any non-ASCII Unicode character
3334
*/
3435
if (JSONPath.isShorthand(token)) {
35-
return currentPath + "." + token;
36+
currentPath.append(".").append(token);
37+
return;
3638
}
3739

3840
// Replace single quote (used to wrap property names when not shorthand form).
3941
if (token.indexOf('\'') != -1) token = token.replace("'", "\\'");
4042
// Replace other special characters.
4143
token = replaceCommonSpecialCharactersIfPresent(token);
42-
43-
return currentPath + "['" + token + "']";
44-
}, (currentPath, index) -> currentPath + "[" + index + "]"),
44+
currentPath.append("['").append(token).append("']");
45+
}, (currentPath, index) -> {
46+
currentPath.append("[").append(index).append("]");
47+
}),
4548

4649
/**
4750
* Paths as JSONPointer expressions.
@@ -54,21 +57,31 @@ public enum PathType {
5457
if (token.indexOf('/') != -1) token = token.replace("/", "~1");
5558
// Replace other special characters.
5659
token = replaceCommonSpecialCharactersIfPresent(token);
57-
return currentPath + "/" + token;
58-
}, (currentPath, index) -> currentPath + "/" + index),
60+
currentPath.append("/").append(token);
61+
}, (currentPath, index) -> {
62+
currentPath.append("/").append(index);
63+
}),
5964

6065
/**
6166
* Paths as a URI reference.
6267
*/
63-
URI_REFERENCE("", (currentPath, token) -> !currentPath.isEmpty() ? currentPath + "/" + token : token, (currentPath, index) -> currentPath + "/" + index);
68+
URI_REFERENCE("", (currentPath, token) -> {
69+
if (!(currentPath.length() == 0)) {
70+
currentPath.append("/").append(token);
71+
} else {
72+
currentPath.append(token);
73+
}
74+
}, (currentPath, index) -> {
75+
currentPath.append("/").append(index);
76+
});
6477

6578
/**
6679
* The default path generation approach to use.
6780
*/
6881
public static final PathType DEFAULT = LEGACY;
6982
private final String rootToken;
70-
private final BiFunction<String, String, String> appendTokenFn;
71-
private final BiFunction<String, Integer, String> appendIndexFn;
83+
private final BiConsumer<StringBuilder, String> appendTokenFn;
84+
private final BiConsumer<StringBuilder, Integer> appendIndexFn;
7285

7386
/**
7487
* Constructor.
@@ -77,7 +90,7 @@ public enum PathType {
7790
* @param appendTokenFn A function used to define the path fragment used to append a token (e.g. property) to an existing path.
7891
* @param appendIndexFn A function used to append an index (for arrays) to an existing path.
7992
*/
80-
PathType(String rootToken, BiFunction<String, String, String> appendTokenFn, BiFunction<String, Integer, String> appendIndexFn) {
93+
PathType(String rootToken, BiConsumer<StringBuilder, String> appendTokenFn, BiConsumer<StringBuilder, Integer> appendIndexFn) {
8194
this.rootToken = rootToken;
8295
this.appendTokenFn = appendTokenFn;
8396
this.appendIndexFn = appendIndexFn;
@@ -103,21 +116,19 @@ private static String replaceCommonSpecialCharactersIfPresent(String token) {
103116
*
104117
* @param currentPath The path to append to.
105118
* @param child The child token.
106-
* @return The resulting complete path.
107119
*/
108-
public String append(String currentPath, String child) {
109-
return this.appendTokenFn.apply(currentPath, child);
120+
public void append(StringBuilder currentPath, String child) {
121+
this.appendTokenFn.accept(currentPath, child);
110122
}
111123

112124
/**
113125
* Append the given index to the provided current path.
114126
*
115127
* @param currentPath The path to append to.
116128
* @param index The index to append.
117-
* @return The resulting complete path.
118129
*/
119-
public String append(String currentPath, int index) {
120-
return this.appendIndexFn.apply(currentPath, index);
130+
public void append(StringBuilder currentPath, int index) {
131+
this.appendIndexFn.accept(currentPath, index);
121132
}
122133

123134
/**

src/main/java/com/networknt/schema/utils/JsonNodeUtil.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,21 @@ private static void visitNode(PathType pathType, String root, JsonNode node, Col
3737
private static void visitArray(PathType pathType, String root, JsonNode node, Collection<String> collector) {
3838
int size = node.size();
3939
for (int i = 0; i < size; ++i) {
40-
String path = pathType.append(root, i);
40+
StringBuilder builder = new StringBuilder();
41+
builder.append(root);
42+
pathType.append(builder, i);
43+
String path = builder.toString();
4144
collector.add(path);
4245
visitNode(pathType, path, node.get(i), collector);
4346
}
4447
}
4548

4649
private static void visitObject(PathType pathType, String root, JsonNode node, Collection<String> collector) {
4750
node.fields().forEachRemaining(entry -> {
48-
String path = pathType.append(root, entry.getKey());
51+
StringBuilder builder = new StringBuilder();
52+
builder.append(root);
53+
pathType.append(builder, entry.getKey());
54+
String path = builder.toString();
4955
collector.add(path);
5056
visitNode(pathType, path, entry.getValue(), collector);
5157
});

src/test/java/com/networknt/schema/Issue687Test.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,19 @@ static Stream<Arguments> errors() {
7070
@ParameterizedTest
7171
@MethodSource("appendTokens")
7272
void testAppendToken(PathType pathType, String currentPath, String token, String expected) {
73-
assertEquals(expected, pathType.append(currentPath, token));
73+
StringBuilder builder = new StringBuilder();
74+
builder.append(currentPath);
75+
pathType.append(builder, token);
76+
assertEquals(expected, builder.toString());
7477
}
7578

7679
@ParameterizedTest
7780
@MethodSource("appendIndexes")
7881
void testAppendIndex(PathType pathType, String currentPath, Integer index, String expected) {
79-
assertEquals(expected, pathType.append(currentPath, index));
82+
StringBuilder builder = new StringBuilder();
83+
builder.append(currentPath);
84+
pathType.append(builder, index);
85+
assertEquals(expected, builder.toString());
8086
}
8187

8288
@ParameterizedTest

0 commit comments

Comments
 (0)