Skip to content

Commit a2936eb

Browse files
author
Greenwood
committed
inverting the relationship between root scanner and function such that the function takes a parameter which is the scanner expression
1 parent 525da15 commit a2936eb

File tree

5 files changed

+66
-2
lines changed

5 files changed

+66
-2
lines changed

json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@
1919
import com.jayway.jsonpath.internal.EvaluationContext;
2020
import com.jayway.jsonpath.internal.Path;
2121
import com.jayway.jsonpath.internal.PathRef;
22+
import com.jayway.jsonpath.internal.function.ParamType;
23+
import com.jayway.jsonpath.internal.function.Parameter;
2224
import org.slf4j.Logger;
2325
import org.slf4j.LoggerFactory;
2426

27+
import java.util.Arrays;
28+
2529
public class CompiledPath implements Path {
2630

2731
private static final Logger logger = LoggerFactory.getLogger(CompiledPath.class);
@@ -32,7 +36,7 @@ public class CompiledPath implements Path {
3236

3337

3438
public CompiledPath(RootPathToken root, boolean isRootPath) {
35-
this.root = root;
39+
this.root = invertScannerFunctionRelationship(root);
3640
this.isRootPath = isRootPath;
3741
}
3842

@@ -41,6 +45,48 @@ public boolean isRootPath() {
4145
return isRootPath;
4246
}
4347

48+
49+
50+
/**
51+
* In the event the writer of the path referenced a function at the tail end of a scanner, augment the query such
52+
* that the root node is the function and the parameter to the function is the scanner. This way we maintain
53+
* relative sanity in the path expression, functions either evaluate scalar values or arrays, they're
54+
* not re-entrant nor should they maintain state, they do however take parameters.
55+
*
56+
* @param path
57+
* this is our old root path which will become a parameter (assuming there's a scanner terminated by a function
58+
*
59+
* @return
60+
* A function with the scanner as input, or if this situation doesn't exist just the input path
61+
*/
62+
private RootPathToken invertScannerFunctionRelationship(final RootPathToken path) {
63+
if (path.isFunctionPath() && path.next() instanceof ScanPathToken) {
64+
PathToken token = path;
65+
PathToken prior = null;
66+
while (null != (token = token.next()) && !(token instanceof FunctionPathToken)) {
67+
prior = token;
68+
}
69+
// Invert the relationship $..path.function() to $.function($..path)
70+
if (token instanceof FunctionPathToken) {
71+
prior.setNext(null);
72+
path.setTail(prior);
73+
74+
// Now generate a new parameter from our path
75+
Parameter parameter = new Parameter();
76+
parameter.setPath(new CompiledPath(path, true));
77+
parameter.setType(ParamType.PATH);
78+
((FunctionPathToken)token).setParameters(Arrays.asList(parameter));
79+
RootPathToken functionRoot = new RootPathToken('$');
80+
functionRoot.setTail(token);
81+
functionRoot.setNext(token);
82+
83+
// Define the function as the root
84+
return functionRoot;
85+
}
86+
}
87+
return path;
88+
}
89+
4490
@Override
4591
public EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration, boolean forUpdate) {
4692
if (logger.isDebugEnabled()) {

json-path/src/main/java/com/jayway/jsonpath/internal/path/FunctionPathToken.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public class FunctionPathToken extends PathToken {
2020

2121
private final String functionName;
2222
private final String pathFragment;
23-
private final List<Parameter> functionParams;
23+
private List<Parameter> functionParams;
2424

2525
public FunctionPathToken(String pathFragment, List<Parameter> parameters) {
2626
this.pathFragment = pathFragment + ((parameters != null && parameters.size() > 0) ? "(...)" : "()");
@@ -81,4 +81,7 @@ public String getPathFragment() {
8181
}
8282

8383

84+
public void setParameters(List<Parameter> parameters) {
85+
this.functionParams = parameters;
86+
}
8487
}

json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,7 @@ public void invoke(PathFunction pathFunction, String currentPath, PathRef parent
215215

216216
protected abstract String getPathFragment();
217217

218+
public void setNext(final PathToken next) {
219+
this.next = next;
220+
}
218221
}

json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,8 @@ public boolean isTokenDefinite() {
7676
public boolean isFunctionPath() {
7777
return (tail instanceof FunctionPathToken);
7878
}
79+
80+
public void setTail(PathToken token) {
81+
this.tail = token;
82+
}
7983
}

json-path/src/test/java/com/jayway/jsonpath/internal/function/Issue191.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ public void testResultSetNumericComputation() {
2727
Long.valueOf(35679716813L), value);
2828
}
2929

30+
@Test
31+
public void testResultSetNumericComputationTail() {
32+
InputStream stream = ClassLoader.getSystemResourceAsStream("issue_191.json");
33+
Long value = JsonPath.parse(stream).read("$..timestamp.sum()", Long.class);
34+
assertEquals("Expected the max function to consume the aggregation parameters and calculate the max over the result set",
35+
Long.valueOf(35679716813L), value);
36+
}
37+
3038
@Test
3139
public void testMultipleResultSetSums() {
3240
InputStream stream = ClassLoader.getSystemResourceAsStream("issue_191.json");

0 commit comments

Comments
 (0)