1919import com .jayway .jsonpath .internal .EvaluationContext ;
2020import com .jayway .jsonpath .internal .Path ;
2121import com .jayway .jsonpath .internal .PathRef ;
22+ import com .jayway .jsonpath .internal .function .ParamType ;
23+ import com .jayway .jsonpath .internal .function .Parameter ;
2224import org .slf4j .Logger ;
2325import org .slf4j .LoggerFactory ;
2426
27+ import java .util .Arrays ;
28+
2529public 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 ()) {
0 commit comments