Browse Source

Decouple the Path representation from its evaluation

Extracted all of the PathToken::evaluate methods out to *Evaluator
classes, now called by a (configurable) DefaultEvaluator.  If you
want to write your own custom evaluator, you now need to implement
the EvaluatorDispatcher interface (one function) and then traverse
the PathToken nodes as fits your need.  This presumes, of course,
that you want to use the `EvaluationContext` as-is.  Otherwise, get
the root node and do your own thing.
pull/169/head
Rick Osborne 9 years ago
parent
commit
b5f68f2830
  1. 189
      json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java
  2. 18
      json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java
  3. 8
      json-path/src/main/java/com/jayway/jsonpath/internal/path/EvaluatorDispatcher.java
  4. 3
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java
  5. 217
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathToken.java
  6. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenAppender.java
  7. 7
      json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java
  8. 206
      json-path/src/main/java/com/jayway/jsonpath/internal/path/ScanPathToken.java
  9. 60
      json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java
  10. 52
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/DefaultEvaluator.java
  11. 21
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/FunctionTokenEvaluator.java
  12. 9
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/ITokenEvaluator.java
  13. 36
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/IndexArrayTokenEvaluator.java
  14. 43
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/PredicateTokenEvaluator.java
  15. 50
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/PropertyTokenEvaluator.java
  16. 22
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/RootTokenEvaluator.java
  17. 95
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/ScanTokenEvaluator.java
  18. 99
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/SliceArrayTokenEvaluator.java
  19. 156
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/TokenEvaluator.java
  20. 35
      json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/WildcardTokenEvaluator.java
  21. 2
      json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/ArrayIndexOperation.java
  22. 28
      json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/ArraySliceOperation.java
  23. 12
      json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/SliceBetweenOperation.java
  24. 12
      json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/SliceFromOperation.java
  25. 12
      json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/SliceToOperation.java
  26. 16
      json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/ArrayPathTokenPredicate.java
  27. 20
      json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/FilterPathTokenPredicate.java
  28. 5
      json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/PathTokenPredicate.java
  29. 52
      json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/PropertyPathTokenPredicate.java
  30. 8
      json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/WildcardPathTokenPredicate.java
  31. 22
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/ArrayPathToken.java
  32. 15
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/FunctionPathToken.java
  33. 27
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/IndexArrayPathToken.java
  34. 110
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/PathToken.java
  35. 40
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/PredicatePathToken.java
  36. 41
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/PropertyPathToken.java
  37. 16
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/RootPathToken.java
  38. 35
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/ScanPathToken.java
  39. 27
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/SliceArrayPathToken.java
  40. 34
      json-path/src/main/java/com/jayway/jsonpath/internal/path/token/WildcardPathToken.java
  41. 4
      json-path/src/test/java/com/jayway/jsonpath/internal/path/PathTokenTest.java

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

@ -1,189 +0,0 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.String.format;
/**
*
*/
public class ArrayPathToken extends PathToken {
private static final Logger logger = LoggerFactory.getLogger(ArrayPathToken.class);
private final ArraySliceOperation arraySliceOperation;
private final ArrayIndexOperation arrayIndexOperation;
ArrayPathToken(final ArraySliceOperation arraySliceOperation) {
this.arraySliceOperation = arraySliceOperation;
this.arrayIndexOperation = null;
}
ArrayPathToken(final ArrayIndexOperation arrayIndexOperation) {
this.arrayIndexOperation = arrayIndexOperation;
this.arraySliceOperation = null;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(currentPath, model, ctx))
return;
if(arraySliceOperation != null){
evaluateSliceOperation(currentPath, parent, model, ctx);
} else {
evaluateIndexOperation(currentPath, parent, model, ctx);
}
}
public void evaluateIndexOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(currentPath, model, ctx))
return;
if(arrayIndexOperation.isSingleIndexOperation()){
handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx);
} else {
for (Integer index : arrayIndexOperation.indexes()) {
handleArrayIndex(index, currentPath, model, ctx);
}
}
}
public void evaluateSliceOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(currentPath, model, ctx))
return;
switch (arraySliceOperation.operation()) {
case SLICE_FROM:
sliceFrom(arraySliceOperation, currentPath, parent, model, ctx);
break;
case SLICE_BETWEEN:
sliceBetween(arraySliceOperation, currentPath, parent, model, ctx);
break;
case SLICE_TO:
sliceTo(arraySliceOperation, currentPath, parent, model, ctx);
break;
}
}
public void sliceFrom(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
if (from < 0) {
//calculate slice start from array length
from = length + from;
}
from = Math.max(0, from);
logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString());
if (length == 0 || from >= length) {
return;
}
for (int i = from; i < length; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
public void sliceBetween(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = operation.from();
int to = operation.to();
to = Math.min(length, to);
if (from >= to || length == 0) {
return;
}
logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString());
for (int i = from; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
public void sliceTo(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
if (length == 0) {
return;
}
int to = operation.to();
if (to < 0) {
//calculate slice end from array length
to = length + to;
}
to = Math.min(length, to);
logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString());
for (int i = 0; i < to; i++) {
handleArrayIndex(i, currentPath, model, ctx);
}
}
@Override
public String getPathFragment() {
if(arrayIndexOperation != null){
return arrayIndexOperation.toString();
} else {
return arraySliceOperation.toString();
}
}
@Override
public boolean isTokenDefinite() {
if(arrayIndexOperation != null){
return arrayIndexOperation.isSingleIndexOperation();
} else {
return false;
}
}
/**
* Check if model is non-null and array.
* @param currentPath
* @param model
* @param ctx
* @return false if current evaluation call must be skipped, true otherwise
* @throws PathNotFoundException if model is null and evaluation must be interrupted
* @throws InvalidPathException if model is not an array and evaluation must be interrupted
*/
protected boolean checkArrayModel(String currentPath, Object model, EvaluationContextImpl ctx) {
if (model == null){
if (! isUpstreamDefinite()) {
return false;
} else {
throw new PathNotFoundException("The path " + currentPath + " is null");
}
}
if (!ctx.jsonProvider().isArray(model)) {
if (! isUpstreamDefinite()) {
return false;
} else {
throw new PathNotFoundException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model));
}
}
return true;
}
}

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

@ -19,6 +19,8 @@ import com.jayway.jsonpath.internal.EvaluationAbortException;
import com.jayway.jsonpath.internal.EvaluationContext;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.eval.DefaultEvaluator;
import com.jayway.jsonpath.internal.path.token.RootPathToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -30,6 +32,7 @@ public class CompiledPath implements Path {
private final boolean isRootPath;
protected EvaluatorDispatcher evaluator;
public CompiledPath(RootPathToken root, boolean isRootPath) {
this.root = root;
@ -41,6 +44,15 @@ public class CompiledPath implements Path {
return isRootPath;
}
public EvaluatorDispatcher getEvaluator() {
if (evaluator == null) evaluator = new DefaultEvaluator();
return evaluator;
}
public void setEvaluator(final EvaluatorDispatcher evaluator) {
this.evaluator = evaluator;
}
@Override
public EvaluationContext evaluate(Object document, Object rootDocument, Configuration configuration, boolean forUpdate) {
if (logger.isDebugEnabled()) {
@ -50,7 +62,7 @@ public class CompiledPath implements Path {
EvaluationContextImpl ctx = new EvaluationContextImpl(this, rootDocument, configuration, forUpdate);
try {
PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP;
root.evaluate("", op, document, ctx);
getEvaluator().evaluate(root, "", op, document, ctx);
} catch (EvaluationAbortException abort){};
return ctx;
@ -75,4 +87,8 @@ public class CompiledPath implements Path {
public String toString() {
return root.toString();
}
public RootPathToken getRoot() {
return root;
}
}

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

@ -0,0 +1,8 @@
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.token.PathToken;
public interface EvaluatorDispatcher {
void evaluate(PathToken token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx);
}

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

@ -5,6 +5,9 @@ import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.CharacterIndex;
import com.jayway.jsonpath.internal.Path;
import com.jayway.jsonpath.internal.filter.FilterCompiler;
import com.jayway.jsonpath.internal.path.operation.ArrayIndexOperation;
import com.jayway.jsonpath.internal.path.operation.ArraySliceOperation;
import com.jayway.jsonpath.internal.path.token.RootPathToken;
import java.util.ArrayList;
import java.util.Collection;

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

@ -1,217 +0,0 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.List;
public abstract class PathToken {
private PathToken prev;
private PathToken next;
private Boolean definite = null;
private Boolean upstreamDefinite = null;
PathToken appendTailToken(PathToken next) {
this.next = next;
this.next.prev = this;
return next;
}
void handleObjectProperty(String currentPath, Object model, EvaluationContextImpl ctx, List<String> properties) {
if(properties.size() == 1) {
String property = properties.get(0);
String evalPath = Utils.concat(currentPath, "['", property, "']");
Object propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
// Conditions below heavily depend on current token type (and its logic) and are not "universal",
// so this code is quite dangerous (I'd rather rewrite it & move to PropertyPathToken and implemented
// WildcardPathToken as a dynamic multi prop case of PropertyPathToken).
// Better safe than sorry.
assert this instanceof PropertyPathToken : "only PropertyPathToken is supported";
if(isLeaf()) {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
} else {
if(ctx.options().contains(Option.SUPPRESS_EXCEPTIONS) ||
!ctx.options().contains(Option.REQUIRE_PROPERTIES)){
return;
} else {
throw new PathNotFoundException("No results for path: " + evalPath);
}
}
} else {
if (! (isUpstreamDefinite() && isTokenDefinite()) &&
!ctx.options().contains(Option.REQUIRE_PROPERTIES) ||
ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)){
// If there is some indefiniteness in the path and properties are not required - we'll ignore
// absent property. And also in case of exception suppression - so that other path evaluation
// branches could be examined.
return;
} else {
throw new PathNotFoundException("Missing property in path " + evalPath);
}
}
}
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP;
if (isLeaf()) {
ctx.addResult(evalPath, pathRef, propertyVal);
}
else {
next().evaluate(evalPath, pathRef, propertyVal, ctx);
}
} else {
String evalPath = currentPath + "[" + Utils.join(", ", "'", properties) + "]";
assert isLeaf() : "non-leaf multi props handled elsewhere";
Object merged = ctx.jsonProvider().createMap();
for (String property : properties) {
Object propertyVal;
if(hasProperty(property, model, ctx)) {
propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) {
propertyVal = null;
} else {
continue;
}
}
} else {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
} else if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
throw new PathNotFoundException("Missing property in path " + evalPath);
} else {
continue;
}
}
ctx.jsonProvider().setProperty(merged, property, propertyVal);
}
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, properties) : PathRef.NO_OP;
ctx.addResult(evalPath, pathRef, merged);
}
}
private static boolean hasProperty(String property, Object model, EvaluationContextImpl ctx) {
return ctx.jsonProvider().getPropertyKeys(model).contains(property);
}
private static Object readObjectProperty(String property, Object model, EvaluationContextImpl ctx) {
return ctx.jsonProvider().getMapValue(model, property);
}
protected void handleArrayIndex(int index, String currentPath, Object model, EvaluationContextImpl ctx) {
String evalPath = Utils.concat(currentPath, "[", String.valueOf(index), "]");
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, index) : PathRef.NO_OP;
try {
Object evalHit = ctx.jsonProvider().getArrayIndex(model, index);
if (isLeaf()) {
ctx.addResult(evalPath, pathRef, evalHit);
} else {
next().evaluate(evalPath, pathRef, evalHit, ctx);
}
} catch (IndexOutOfBoundsException e) {
}
}
PathToken prev(){
return prev;
}
PathToken next() {
if (isLeaf()) {
throw new IllegalStateException("Current path token is a leaf");
}
return next;
}
boolean isLeaf() {
return next == null;
}
boolean isRoot() {
return prev == null;
}
boolean isUpstreamDefinite() {
if (upstreamDefinite == null) {
upstreamDefinite = isRoot() || prev.isTokenDefinite() && prev.isUpstreamDefinite();
}
return upstreamDefinite;
}
public int getTokenCount() {
int cnt = 1;
PathToken token = this;
while (!token.isLeaf()){
token = token.next();
cnt++;
}
return cnt;
}
public boolean isPathDefinite() {
if(definite != null){
return definite.booleanValue();
}
boolean isDefinite = isTokenDefinite();
if (isDefinite && !isLeaf()) {
isDefinite = next.isPathDefinite();
}
definite = isDefinite;
return isDefinite;
}
@Override
public String toString() {
if (isLeaf()) {
return getPathFragment();
} else {
return getPathFragment() + next().toString();
}
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
public void invoke(PathFunction pathFunction, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
ctx.addResult(currentPath, parent, pathFunction.invoke(currentPath, parent, model, ctx));
}
public abstract void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx);
public abstract boolean isTokenDefinite();
protected abstract String getPathFragment();
}

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

@ -1,5 +1,7 @@
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.internal.path.token.PathToken;
public interface PathTokenAppender {
PathTokenAppender appendPathToken(PathToken next);
}

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

@ -1,6 +1,9 @@
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.path.operation.ArrayIndexOperation;
import com.jayway.jsonpath.internal.path.operation.ArraySliceOperation;
import com.jayway.jsonpath.internal.path.token.*;
import java.util.Collection;
import java.util.List;
@ -22,11 +25,11 @@ public class PathTokenFactory {
}
public static PathToken createSliceArrayPathToken(final ArraySliceOperation arraySliceOperation) {
return new ArrayPathToken(arraySliceOperation);
return new SliceArrayPathToken(arraySliceOperation);
}
public static PathToken createIndexArrayPathToken(final ArrayIndexOperation arrayIndexOperation) {
return new ArrayPathToken(arrayIndexOperation);
return new IndexArrayPathToken(arrayIndexOperation);
}
public static PathToken createWildCardPathToken() {

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

@ -1,206 +0,0 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.Collection;
/**
*
*/
public class ScanPathToken extends PathToken {
ScanPathToken() {
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
PathToken pt = next();
walk(pt, currentPath, parent, model, ctx, createScanPredicate(pt, ctx));
}
public static void walk(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, Predicate predicate) {
if (ctx.jsonProvider().isMap(model)) {
walkObject(pt, currentPath, parent, model, ctx, predicate);
} else if (ctx.jsonProvider().isArray(model)) {
walkArray(pt, currentPath, parent, model, ctx, predicate);
}
}
public static void walkArray(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, Predicate predicate) {
if (predicate.matches(model)) {
if (pt.isLeaf()) {
pt.evaluate(currentPath, parent, model, ctx);
} else {
PathToken next = pt.next();
Iterable<?> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
next.evaluate(evalPath, parent, evalModel, ctx);
idx++;
}
}
}
Iterable<?> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
walk(pt, evalPath, PathRef.create(model, idx), evalModel, ctx, predicate);
idx++;
}
}
public static void walkObject(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, Predicate predicate) {
if (predicate.matches(model)) {
pt.evaluate(currentPath, parent, model, ctx);
}
Collection<String> properties = ctx.jsonProvider().getPropertyKeys(model);
for (String property : properties) {
String evalPath = currentPath + "['" + property + "']";
Object propertyModel = ctx.jsonProvider().getMapValue(model, property);
if (propertyModel != JsonProvider.UNDEFINED) {
walk(pt, evalPath, PathRef.create(model, property), propertyModel, ctx, predicate);
}
}
}
private static Predicate createScanPredicate(final PathToken target, final EvaluationContextImpl ctx) {
if (target instanceof PropertyPathToken) {
return new PropertyPathTokenPredicate(target, ctx);
} else if (target instanceof ArrayPathToken) {
return new ArrayPathTokenPredicate(ctx);
} else if (target instanceof WildcardPathToken) {
return new WildcardPathTokenPredicate();
} else if (target instanceof PredicatePathToken) {
return new FilterPathTokenPredicate(target, ctx);
} else {
return FALSE_PREDICATE;
}
}
@Override
public boolean isTokenDefinite() {
return false;
}
@Override
public String getPathFragment() {
return "..";
}
private interface Predicate {
boolean matches(Object model);
}
private static final Predicate FALSE_PREDICATE = new Predicate() {
@Override
public boolean matches(Object model) {
return false;
}
};
private static final class FilterPathTokenPredicate implements Predicate {
private final EvaluationContextImpl ctx;
private PredicatePathToken predicatePathToken;
private FilterPathTokenPredicate(PathToken target, EvaluationContextImpl ctx) {
this.ctx = ctx;
predicatePathToken = (PredicatePathToken) target;
}
@Override
public boolean matches(Object model) {
return predicatePathToken.accept(model, ctx.rootDocument(), ctx.configuration(), ctx);
}
}
private static final class WildcardPathTokenPredicate implements Predicate {
@Override
public boolean matches(Object model) {
return true;
}
}
private static final class ArrayPathTokenPredicate implements Predicate {
private final EvaluationContextImpl ctx;
private ArrayPathTokenPredicate(EvaluationContextImpl ctx) {
this.ctx = ctx;
}
@Override
public boolean matches(Object model) {
return ctx.jsonProvider().isArray(model);
}
}
private static final class PropertyPathTokenPredicate implements Predicate {
private final EvaluationContextImpl ctx;
private PropertyPathToken propertyPathToken;
private PropertyPathTokenPredicate(PathToken target, EvaluationContextImpl ctx) {
this.ctx = ctx;
propertyPathToken = (PropertyPathToken) target;
}
@Override
public boolean matches(Object model) {
if (! ctx.jsonProvider().isMap(model)) {
return false;
}
//
// The commented code below makes it really hard understand, use and predict the result
// of deep scanning operations. It might be correct but was decided to be
// left out until the behavior of REQUIRE_PROPERTIES is more strictly defined
// in a deep scanning scenario. For details read conversation in commit
// https://github.com/jayway/JsonPath/commit/1a72fc078deb16995e323442bfb681bd715ce45a#commitcomment-14616092
//
// if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
// // Have to require properties defined in path when an indefinite path is evaluated,
// // so have to go there and search for it.
// return true;
// }
if (! propertyPathToken.isTokenDefinite()) {
// It's responsibility of PropertyPathToken code to handle indefinite scenario of properties,
// so we'll allow it to do its job.
return true;
}
if (propertyPathToken.isLeaf() && ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) {
// In case of DEFAULT_PATH_LEAF_TO_NULL missing properties is not a problem.
return true;
}
Collection<String> keys = ctx.jsonProvider().getPropertyKeys(model);
return keys.containsAll(propertyPathToken.getProperties());
}
}
}

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

@ -1,60 +0,0 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import static java.util.Arrays.asList;
/**
*
*/
public class WildcardPathToken extends PathToken {
WildcardPathToken() {
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
for (String property : ctx.jsonProvider().getPropertyKeys(model)) {
handleObjectProperty(currentPath, model, ctx, asList(property));
}
} else if (ctx.jsonProvider().isArray(model)) {
for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) {
try {
handleArrayIndex(idx, currentPath, model, ctx);
} catch (PathNotFoundException p){
if(ctx.options().contains(Option.REQUIRE_PROPERTIES)){
throw p;
}
}
}
}
}
@Override
public boolean isTokenDefinite() {
return false;
}
@Override
public String getPathFragment() {
return "[*]";
}
}

52
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/DefaultEvaluator.java

@ -0,0 +1,52 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.*;
import java.util.LinkedHashMap;
import java.util.Map;
public class DefaultEvaluator implements EvaluatorDispatcher {
/**
* Type safe cache abstraction
*/
protected class EvaluatorCache {
protected final LinkedHashMap<Class<? extends PathToken>, ITokenEvaluator> evaluators = new LinkedHashMap<Class<? extends PathToken>, ITokenEvaluator>();
public <P extends PathToken, E extends ITokenEvaluator<P>> void put(final Class<P> tokenClass, final E evaluator) {
evaluators.put(tokenClass, evaluator);
}
public <P extends PathToken, E extends ITokenEvaluator<P>> E get(final P token) {
final Class<? extends PathToken> wantClass = token.getClass();
for (final Map.Entry<Class<? extends PathToken>, ITokenEvaluator> pair : evaluators.entrySet()) {
if (pair.getKey().isAssignableFrom(wantClass)) {
//noinspection unchecked
return (E) pair.getValue();
}
}
throw new IllegalArgumentException("Unknown PathToken type: " + wantClass.getCanonicalName());
}
}
protected final EvaluatorCache evaluators;
public DefaultEvaluator() {
evaluators = new EvaluatorCache();
evaluators.put(IndexArrayPathToken.class, new IndexArrayTokenEvaluator(this));
evaluators.put(SliceArrayPathToken.class, new SliceArrayTokenEvaluator(this));
evaluators.put(FunctionPathToken.class, new FunctionTokenEvaluator(this));
evaluators.put(PredicatePathToken.class, new PredicateTokenEvaluator(this));
evaluators.put(PropertyPathToken.class, new PropertyTokenEvaluator(this));
evaluators.put(RootPathToken.class, new RootTokenEvaluator(this));
evaluators.put(ScanPathToken.class, new ScanTokenEvaluator(this));
evaluators.put(WildcardPathToken.class, new WildcardTokenEvaluator(this));
}
@Override
public void evaluate(final PathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
evaluators.get(token).evaluate(token, currentPath, parent, model, ctx);
}
}

21
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/FunctionTokenEvaluator.java

@ -0,0 +1,21 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.internal.function.PathFunctionFactory;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.FunctionPathToken;
public class FunctionTokenEvaluator extends TokenEvaluator<FunctionPathToken> {
public FunctionTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final FunctionPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
PathFunction pathFunction = PathFunctionFactory.newFunction(token.getFunctionName());
Object result = pathFunction.invoke(currentPath, parent, model, ctx);
ctx.addResult(currentPath, parent, result);
}
}

9
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/ITokenEvaluator.java

@ -0,0 +1,9 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.token.PathToken;
public interface ITokenEvaluator<P extends PathToken> {
void evaluate(P token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx);
}

36
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/IndexArrayTokenEvaluator.java

@ -0,0 +1,36 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.operation.ArrayIndexOperation;
import com.jayway.jsonpath.internal.path.token.IndexArrayPathToken;
public class IndexArrayTokenEvaluator extends TokenEvaluator<IndexArrayPathToken> {
public IndexArrayTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final IndexArrayPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
if (! checkArrayModel(token, currentPath, model, ctx))
return;
evaluateIndexOperation(token, currentPath, parent, model, ctx);
}
public void evaluateIndexOperation(final IndexArrayPathToken token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(token, currentPath, model, ctx))
return;
final ArrayIndexOperation operation = token.operation();
if(operation.isSingleIndexOperation()){
handleArrayIndex(token, operation.indexes().get(0), currentPath, model, ctx);
} else {
for (Integer index : operation.indexes()) {
handleArrayIndex(token, index, currentPath, model, ctx);
}
}
}
}

43
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/PredicateTokenEvaluator.java

@ -0,0 +1,43 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.PredicatePathToken;
import static java.lang.String.format;
public class PredicateTokenEvaluator extends TokenEvaluator<PredicatePathToken> {
public PredicateTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final PredicatePathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
if (token.accept(model, ctx.rootDocument(), ctx.configuration(), ctx)) {
PathRef op = ctx.forUpdate() ? parent : PathRef.NO_OP;
if (token.isLeaf()) {
ctx.addResult(currentPath, op, model);
} else {
dispatcher.evaluate(token.next(), currentPath, op, model, ctx);
}
}
} else if (ctx.jsonProvider().isArray(model)){
int idx = 0;
Iterable<?> objects = ctx.jsonProvider().toIterable(model);
for (Object idxModel : objects) {
if (token.accept(idxModel, ctx.rootDocument(), ctx.configuration(), ctx)) {
handleArrayIndex(token, idx, currentPath, model, ctx);
}
idx++;
}
} else {
if (token.isUpstreamDefinite()) {
throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model));
}
}
}
}

50
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/PropertyTokenEvaluator.java

@ -0,0 +1,50 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.PropertyPathToken;
import java.util.ArrayList;
import java.util.List;
import static com.jayway.jsonpath.internal.Utils.onlyOneIsTrueNonThrow;
public class PropertyTokenEvaluator extends TokenEvaluator<PropertyPathToken> {
public PropertyTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final PropertyPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
// Can't assert it in ctor because isLeaf() could be changed later on.
assert onlyOneIsTrueNonThrow(token.singlePropertyCase(), token.multiPropertyMergeCase(), token.multiPropertyIterationCase());
if (!ctx.jsonProvider().isMap(model)) {
if (! token.isUpstreamDefinite()) {
return;
} else {
String m = model == null ? "null" : model.getClass().getName();
throw new PathNotFoundException(String.format(
"Expected to find an object with property %s in path %s but found '%s'. " +
"This is not a json object according to the JsonProvider: '%s'.",
token.getPathFragment(), currentPath, m, ctx.configuration().jsonProvider().getClass().getName()));
}
}
if (token.singlePropertyCase() || token.multiPropertyMergeCase()) {
handleObjectProperty(token, currentPath, model, ctx, token.getProperties());
return;
}
assert token.multiPropertyIterationCase();
final List<String> currentlyHandledProperty = new ArrayList<String>(1);
currentlyHandledProperty.add(null);
for (final String property : token.getProperties()) {
currentlyHandledProperty.set(0, property);
handleObjectProperty(token, currentPath, model, ctx, currentlyHandledProperty);
}
}
}

22
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/RootTokenEvaluator.java

@ -0,0 +1,22 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.RootPathToken;
public class RootTokenEvaluator extends TokenEvaluator<RootPathToken> {
public RootTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final RootPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
if (token.isLeaf()) {
PathRef op = ctx.forUpdate() ? parent : PathRef.NO_OP;
ctx.addResult(token.getPathFragment(), op, model);
} else {
dispatcher.evaluate(token.next(), token.getPathFragment(), parent, model, ctx);
}
}
}

95
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/ScanTokenEvaluator.java

@ -0,0 +1,95 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.*;
import com.jayway.jsonpath.internal.path.predicate.*;
import com.jayway.jsonpath.internal.path.token.*;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.Collection;
public class ScanTokenEvaluator extends TokenEvaluator<ScanPathToken> {
public ScanTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final ScanPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
PathToken pt = token.next();
walk(pt, currentPath, parent, model, ctx, createScanPredicate(pt, ctx));
}
public void walk(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, PathTokenPredicate predicate) {
if (ctx.jsonProvider().isMap(model)) {
walkObject(pt, currentPath, parent, model, ctx, predicate);
} else if (ctx.jsonProvider().isArray(model)) {
walkArray(pt, currentPath, parent, model, ctx, predicate);
}
}
public void walkArray(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, PathTokenPredicate predicate) {
if (predicate.matches(model)) {
if (pt.isLeaf()) {
dispatcher.evaluate(pt, currentPath, parent, model, ctx);
} else {
PathToken next = pt.next();
Iterable<?> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
dispatcher.evaluate(next, evalPath, parent, evalModel, ctx);
idx++;
}
}
}
Iterable<?> models = ctx.jsonProvider().toIterable(model);
int idx = 0;
for (Object evalModel : models) {
String evalPath = currentPath + "[" + idx + "]";
walk(pt, evalPath, PathRef.create(model, idx), evalModel, ctx, predicate);
idx++;
}
}
public void walkObject(PathToken pt, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx, PathTokenPredicate predicate) {
if (predicate.matches(model)) {
dispatcher.evaluate(pt, currentPath, parent, model, ctx);
}
Collection<String> properties = ctx.jsonProvider().getPropertyKeys(model);
for (String property : properties) {
String evalPath = currentPath + "['" + property + "']";
Object propertyModel = ctx.jsonProvider().getMapValue(model, property);
if (propertyModel != JsonProvider.UNDEFINED) {
walk(pt, evalPath, PathRef.create(model, property), propertyModel, ctx, predicate);
}
}
}
public static PathTokenPredicate createScanPredicate(final PathToken target, final EvaluationContextImpl ctx) {
if (target instanceof PropertyPathToken) {
return new PropertyPathTokenPredicate(target, ctx);
} else if (target instanceof ArrayPathToken) {
return new ArrayPathTokenPredicate(ctx);
} else if (target instanceof WildcardPathToken) {
return new WildcardPathTokenPredicate();
} else if (target instanceof PredicatePathToken) {
return new FilterPathTokenPredicate(target, ctx);
} else {
return FALSE_PREDICATE;
}
}
private static final PathTokenPredicate FALSE_PREDICATE = new PathTokenPredicate() {
@Override
public boolean matches(Object model) {
return false;
}
};
}

99
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/SliceArrayTokenEvaluator.java

@ -0,0 +1,99 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.*;
import com.jayway.jsonpath.internal.path.operation.ArraySliceOperation;
import com.jayway.jsonpath.internal.path.token.SliceArrayPathToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SliceArrayTokenEvaluator extends TokenEvaluator<SliceArrayPathToken> {
private static final Logger logger = LoggerFactory.getLogger(SliceArrayTokenEvaluator.class);
public SliceArrayTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final SliceArrayPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
if (! checkArrayModel(token, currentPath, model, ctx))
return;
evaluateSliceOperation(token, currentPath, parent, model, ctx);
}
public void evaluateSliceOperation(final SliceArrayPathToken token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
if (! checkArrayModel(token, currentPath, model, ctx))
return;
final ArraySliceOperation operation = token.operation();
switch (operation.operation()) {
case SLICE_FROM:
sliceFrom(token, currentPath, parent, model, ctx);
break;
case SLICE_BETWEEN:
sliceBetween(token, currentPath, parent, model, ctx);
break;
case SLICE_TO:
sliceTo(token, currentPath, parent, model, ctx);
break;
}
}
public void sliceFrom(final SliceArrayPathToken token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = token.operation().from();
if (from < 0) {
//calculate slice start from array length
from = length + from;
}
from = Math.max(0, from);
logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString());
if (length == 0 || from >= length) {
return;
}
for (int i = from; i < length; i++) {
handleArrayIndex(token, i, currentPath, model, ctx);
}
}
public void sliceBetween(final SliceArrayPathToken token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
int from = token.operation().from();
int to = token.operation().to();
to = Math.min(length, to);
if (from >= to || length == 0) {
return;
}
logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString());
for (int i = from; i < to; i++) {
handleArrayIndex(token, i, currentPath, model, ctx);
}
}
public void sliceTo(final SliceArrayPathToken token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
int length = ctx.jsonProvider().length(model);
if (length == 0) {
return;
}
int to = token.operation().to();
if (to < 0) {
//calculate slice end from array length
to = length + to;
}
to = Math.min(length, to);
logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString());
for (int i = 0; i < to; i++) {
handleArrayIndex(token, i, currentPath, model, ctx);
}
}
}

156
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/TokenEvaluator.java

@ -0,0 +1,156 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.ArrayPathToken;
import com.jayway.jsonpath.internal.path.token.PathToken;
import com.jayway.jsonpath.internal.path.token.PropertyPathToken;
import com.jayway.jsonpath.spi.json.JsonProvider;
import java.util.List;
import static java.lang.String.format;
public abstract class TokenEvaluator<P extends PathToken> implements ITokenEvaluator<P> {
protected final EvaluatorDispatcher dispatcher;
public TokenEvaluator(final EvaluatorDispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public abstract void evaluate(final P token, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx);
/**
* Check if model is non-null and array.
* @param currentPath
* @param model
* @param ctx
* @return false if current evaluation call must be skipped, true otherwise
* @throws PathNotFoundException if model is null and evaluation must be interrupted
* @throws InvalidPathException if model is not an array and evaluation must be interrupted
*/
public boolean checkArrayModel(final ArrayPathToken token, String currentPath, Object model, EvaluationContextImpl ctx) {
if (model == null){
if (! token.isUpstreamDefinite()) {
return false;
} else {
throw new PathNotFoundException("The path " + currentPath + " is null");
}
}
if (!ctx.jsonProvider().isArray(model)) {
if (! token.isUpstreamDefinite()) {
return false;
} else {
throw new PathNotFoundException(format("Filter: %s can only be applied to arrays. Current context is: %s", toString(), model));
}
}
return true;
}
public void handleObjectProperty(final P token, String currentPath, Object model, EvaluationContextImpl ctx, List<String> properties) {
if(properties.size() == 1) {
String property = properties.get(0);
String evalPath = Utils.concat(currentPath, "['", property, "']");
Object propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
// Conditions below heavily depend on current token type (and its logic) and are not "universal",
// so this code is quite dangerous (I'd rather rewrite it & move to PropertyPathToken and implemented
// WildcardPathToken as a dynamic multi prop case of PropertyPathToken).
// Better safe than sorry.
assert token instanceof PropertyPathToken : "only PropertyPathToken is supported";
if(token.isLeaf()) {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
} else {
if(ctx.options().contains(Option.SUPPRESS_EXCEPTIONS) ||
!ctx.options().contains(Option.REQUIRE_PROPERTIES)){
return;
} else {
throw new PathNotFoundException("No results for path: " + evalPath);
}
}
} else {
if (! (token.isUpstreamDefinite() && token.isTokenDefinite()) &&
!ctx.options().contains(Option.REQUIRE_PROPERTIES) ||
ctx.options().contains(Option.SUPPRESS_EXCEPTIONS)){
// If there is some indefiniteness in the path and properties are not required - we'll ignore
// absent property. And also in case of exception suppression - so that other path evaluation
// branches could be examined.
return;
} else {
throw new PathNotFoundException("Missing property in path " + evalPath);
}
}
}
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, property) : PathRef.NO_OP;
if (token.isLeaf()) {
ctx.addResult(evalPath, pathRef, propertyVal);
}
else {
dispatcher.evaluate(token.next(), evalPath, pathRef, propertyVal, ctx);
}
} else {
String evalPath = currentPath + "[" + Utils.join(", ", "'", properties) + "]";
assert token.isLeaf() : "non-leaf multi props handled elsewhere";
Object merged = ctx.jsonProvider().createMap();
for (String property : properties) {
Object propertyVal;
if(hasProperty(property, model, ctx)) {
propertyVal = readObjectProperty(property, model, ctx);
if(propertyVal == JsonProvider.UNDEFINED){
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) {
propertyVal = null;
} else {
continue;
}
}
} else {
if(ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)){
propertyVal = null;
} else if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
throw new PathNotFoundException("Missing property in path " + evalPath);
} else {
continue;
}
}
ctx.jsonProvider().setProperty(merged, property, propertyVal);
}
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, properties) : PathRef.NO_OP;
ctx.addResult(evalPath, pathRef, merged);
}
}
private static boolean hasProperty(String property, Object model, EvaluationContextImpl ctx) {
return ctx.jsonProvider().getPropertyKeys(model).contains(property);
}
private static Object readObjectProperty(String property, Object model, EvaluationContextImpl ctx) {
return ctx.jsonProvider().getMapValue(model, property);
}
public void handleArrayIndex(final P token, int index, String currentPath, Object model, EvaluationContextImpl ctx) {
String evalPath = Utils.concat(currentPath, "[", String.valueOf(index), "]");
PathRef pathRef = ctx.forUpdate() ? PathRef.create(model, index) : PathRef.NO_OP;
try {
Object evalHit = ctx.jsonProvider().getArrayIndex(model, index);
if (token.isLeaf()) {
ctx.addResult(evalPath, pathRef, evalHit);
} else {
dispatcher.evaluate(token.next(), evalPath, pathRef, evalHit, ctx);
}
} catch (IndexOutOfBoundsException e) {
}
}
}

35
json-path/src/main/java/com/jayway/jsonpath/internal/path/eval/WildcardTokenEvaluator.java

@ -0,0 +1,35 @@
package com.jayway.jsonpath.internal.path.eval;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.EvaluatorDispatcher;
import com.jayway.jsonpath.internal.path.token.WildcardPathToken;
import java.util.Collections;
public class WildcardTokenEvaluator extends TokenEvaluator<WildcardPathToken> {
public WildcardTokenEvaluator(final EvaluatorDispatcher dispatcher) {
super(dispatcher);
}
@Override
public void evaluate(final WildcardPathToken token, final String currentPath, final PathRef parent, final Object model, final EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
for (String property : ctx.jsonProvider().getPropertyKeys(model)) {
handleObjectProperty(token, currentPath, model, ctx, Collections.singletonList(property));
}
} else if (ctx.jsonProvider().isArray(model)) {
for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) {
try {
handleArrayIndex(token, idx, currentPath, model, ctx);
} catch (PathNotFoundException p){
if(ctx.options().contains(Option.REQUIRE_PROPERTIES)){
throw p;
}
}
}
}
}
}

2
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexOperation.java → json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/ArrayIndexOperation.java

@ -1,4 +1,4 @@
package com.jayway.jsonpath.internal.path;
package com.jayway.jsonpath.internal.path.operation;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.internal.Utils;

28
json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java → json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/ArraySliceOperation.java

@ -1,10 +1,10 @@
package com.jayway.jsonpath.internal.path;
package com.jayway.jsonpath.internal.path.operation;
import com.jayway.jsonpath.InvalidPathException;
import static java.lang.Character.isDigit;
public class ArraySliceOperation {
public abstract class ArraySliceOperation {
public enum Operation {
SLICE_FROM,
@ -14,12 +14,10 @@ public class ArraySliceOperation {
private final Integer from;
private final Integer to;
private final Operation operation;
private ArraySliceOperation(Integer from, Integer to, Operation operation) {
protected ArraySliceOperation(Integer from, Integer to) {
this.from = from;
this.to = to;
this.operation = operation;
}
public Integer from() {
@ -30,9 +28,7 @@ public class ArraySliceOperation {
return to;
}
public Operation operation() {
return operation;
}
public abstract Operation operation();
@Override
public String toString() {
@ -58,19 +54,15 @@ public class ArraySliceOperation {
Integer tempFrom = tryRead(tokens, 0);
Integer tempTo = tryRead(tokens, 1);
Operation tempOperpation;
if(tempFrom != null && tempTo == null){
tempOperpation = Operation.SLICE_FROM;
} else if(tempFrom != null && tempTo != null){
tempOperpation = Operation.SLICE_BETWEEN;
} else if(tempFrom == null && tempTo != null){
tempOperpation = Operation.SLICE_TO;
} else {
throw new InvalidPathException("Failed to parse SliceOperation: " + operation);
return new SliceFromOperation(tempFrom);
} else if(tempFrom != null){
return new SliceBetweenOperation(tempFrom, tempTo);
} else if(tempTo != null){
return new SliceToOperation(tempTo);
}
return new ArraySliceOperation(tempFrom, tempTo, tempOperpation);
throw new InvalidPathException("Failed to parse SliceOperation: " + operation);
}
private static Integer tryRead(String[] tokens, int idx){

12
json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/SliceBetweenOperation.java

@ -0,0 +1,12 @@
package com.jayway.jsonpath.internal.path.operation;
public class SliceBetweenOperation extends ArraySliceOperation {
protected SliceBetweenOperation(final Integer from, final Integer to) {
super(from, to);
}
@Override
public Operation operation() {
return Operation.SLICE_BETWEEN;
}
}

12
json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/SliceFromOperation.java

@ -0,0 +1,12 @@
package com.jayway.jsonpath.internal.path.operation;
public class SliceFromOperation extends ArraySliceOperation {
protected SliceFromOperation(Integer from) {
super(from, null);
}
@Override
public Operation operation() {
return Operation.SLICE_FROM;
}
}

12
json-path/src/main/java/com/jayway/jsonpath/internal/path/operation/SliceToOperation.java

@ -0,0 +1,12 @@
package com.jayway.jsonpath.internal.path.operation;
public class SliceToOperation extends ArraySliceOperation {
protected SliceToOperation(final Integer to) {
super(null, to);
}
@Override
public Operation operation() {
return Operation.SLICE_TO;
}
}

16
json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/ArrayPathTokenPredicate.java

@ -0,0 +1,16 @@
package com.jayway.jsonpath.internal.path.predicate;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
public final class ArrayPathTokenPredicate implements PathTokenPredicate {
private final EvaluationContextImpl ctx;
public ArrayPathTokenPredicate(EvaluationContextImpl ctx) {
this.ctx = ctx;
}
@Override
public boolean matches(Object model) {
return ctx.jsonProvider().isArray(model);
}
}

20
json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/FilterPathTokenPredicate.java

@ -0,0 +1,20 @@
package com.jayway.jsonpath.internal.path.predicate;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.token.PathToken;
import com.jayway.jsonpath.internal.path.token.PredicatePathToken;
public final class FilterPathTokenPredicate implements PathTokenPredicate {
private final EvaluationContextImpl ctx;
private PredicatePathToken predicatePathToken;
public FilterPathTokenPredicate(PathToken target, EvaluationContextImpl ctx) {
this.ctx = ctx;
predicatePathToken = (PredicatePathToken) target;
}
@Override
public boolean matches(Object model) {
return predicatePathToken.accept(model, ctx.rootDocument(), ctx.configuration(), ctx);
}
}

5
json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/PathTokenPredicate.java

@ -0,0 +1,5 @@
package com.jayway.jsonpath.internal.path.predicate;
public interface PathTokenPredicate {
boolean matches(Object model);
}

52
json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/PropertyPathTokenPredicate.java

@ -0,0 +1,52 @@
package com.jayway.jsonpath.internal.path.predicate;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.token.PathToken;
import com.jayway.jsonpath.internal.path.token.PropertyPathToken;
import java.util.Collection;
public final class PropertyPathTokenPredicate implements PathTokenPredicate {
private final EvaluationContextImpl ctx;
private PropertyPathToken propertyPathToken;
public PropertyPathTokenPredicate(PathToken target, EvaluationContextImpl ctx) {
this.ctx = ctx;
propertyPathToken = (PropertyPathToken) target;
}
@Override
public boolean matches(Object model) {
if (! ctx.jsonProvider().isMap(model)) {
return false;
}
//
// The commented code below makes it really hard understand, use and predict the result
// of deep scanning operations. It might be correct but was decided to be
// left out until the behavior of REQUIRE_PROPERTIES is more strictly defined
// in a deep scanning scenario. For details read conversation in commit
// https://github.com/jayway/JsonPath/commit/1a72fc078deb16995e323442bfb681bd715ce45a#commitcomment-14616092
//
// if (ctx.options().contains(Option.REQUIRE_PROPERTIES)) {
// // Have to require properties defined in path when an indefinite path is evaluated,
// // so have to go there and search for it.
// return true;
// }
if (! propertyPathToken.isTokenDefinite()) {
// It's responsibility of PropertyPathToken code to handle indefinite scenario of properties,
// so we'll allow it to do its job.
return true;
}
if (propertyPathToken.isLeaf() && ctx.options().contains(Option.DEFAULT_PATH_LEAF_TO_NULL)) {
// In case of DEFAULT_PATH_LEAF_TO_NULL missing properties is not a problem.
return true;
}
Collection<String> keys = ctx.jsonProvider().getPropertyKeys(model);
return keys.containsAll(propertyPathToken.getProperties());
}
}

8
json-path/src/main/java/com/jayway/jsonpath/internal/path/predicate/WildcardPathTokenPredicate.java

@ -0,0 +1,8 @@
package com.jayway.jsonpath.internal.path.predicate;
public final class WildcardPathTokenPredicate implements PathTokenPredicate {
@Override
public boolean matches(Object model) {
return true;
}
}

22
json-path/src/main/java/com/jayway/jsonpath/internal/path/token/ArrayPathToken.java

@ -0,0 +1,22 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path.token;
/**
* Placeholder common ancestor for {@link IndexArrayPathToken}
* and {@link SliceArrayPathToken}
*/
public abstract class ArrayPathToken extends PathToken {
}

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

@ -1,7 +1,5 @@
package com.jayway.jsonpath.internal.path;
package com.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.internal.function.PathFunctionFactory;
/**
@ -25,13 +23,6 @@ public class FunctionPathToken extends PathToken {
}
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
PathFunction pathFunction = PathFunctionFactory.newFunction(functionName);
Object result = pathFunction.invoke(currentPath, parent, model, ctx);
ctx.addResult(currentPath, parent, result);
}
/**
* Return the actual value by indicating true. If this return was false then we'd return the value in an array which
* isn't what is desired - true indicates the raw value is returned.
@ -48,5 +39,7 @@ public class FunctionPathToken extends PathToken {
return "." + pathFragment;
}
public String getFunctionName() {
return functionName;
}
}

27
json-path/src/main/java/com/jayway/jsonpath/internal/path/token/IndexArrayPathToken.java

@ -0,0 +1,27 @@
package com.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.internal.path.operation.ArrayIndexOperation;
public class IndexArrayPathToken extends ArrayPathToken {
protected final ArrayIndexOperation arrayIndexOperation;
public IndexArrayPathToken(final ArrayIndexOperation arrayIndexOperation) {
super();
if (arrayIndexOperation == null) throw new IllegalArgumentException();
this.arrayIndexOperation = arrayIndexOperation;
}
@Override
public String getPathFragment() {
return arrayIndexOperation.toString();
}
@Override
public boolean isTokenDefinite() {
return arrayIndexOperation.isSingleIndexOperation();
}
public ArrayIndexOperation operation() {
return arrayIndexOperation;
}
}

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

@ -0,0 +1,110 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.function.PathFunction;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
public abstract class PathToken {
private PathToken prev;
private PathToken next;
private Boolean definite = null;
private Boolean upstreamDefinite = null;
public PathToken appendTailToken(PathToken next) {
this.next = next;
this.next.prev = this;
return next;
}
public PathToken prev(){
return prev;
}
public PathToken next() {
if (isLeaf()) {
throw new IllegalStateException("Current path token is a leaf");
}
return next;
}
public boolean isLeaf() {
return next == null;
}
public boolean isRoot() {
return prev == null;
}
public boolean isUpstreamDefinite() {
if (upstreamDefinite == null) {
upstreamDefinite = isRoot() || prev.isTokenDefinite() && prev.isUpstreamDefinite();
}
return upstreamDefinite;
}
public int getTokenCount() {
int cnt = 1;
PathToken token = this;
while (!token.isLeaf()){
token = token.next();
cnt++;
}
return cnt;
}
public boolean isPathDefinite() {
if(definite != null){
return definite.booleanValue();
}
boolean isDefinite = isTokenDefinite();
if (isDefinite && !isLeaf()) {
isDefinite = next.isPathDefinite();
}
definite = isDefinite;
return isDefinite;
}
@Override
public String toString() {
if (isLeaf()) {
return getPathFragment();
} else {
return getPathFragment() + next().toString();
}
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
public void invoke(PathFunction pathFunction, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
ctx.addResult(currentPath, parent, pathFunction.invoke(currentPath, parent, model, ctx));
}
public abstract boolean isTokenDefinite();
protected abstract String getPathFragment();
}

40
json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java → json-path/src/main/java/com/jayway/jsonpath/internal/path/token/PredicatePathToken.java

@ -12,16 +12,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
package com.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.Predicate;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.EvaluationContextImpl;
import com.jayway.jsonpath.internal.path.PredicateContextImpl;
import java.util.Collection;
import static java.lang.String.format;
import static java.util.Arrays.asList;
/**
@ -29,45 +28,16 @@ import static java.util.Arrays.asList;
*/
public class PredicatePathToken extends PathToken {
private final Collection<Predicate> predicates;
PredicatePathToken(Predicate filter) {
public PredicatePathToken(Predicate filter) {
this.predicates = asList(filter);
}
PredicatePathToken(Collection<Predicate> predicates) {
public PredicatePathToken(Collection<Predicate> predicates) {
this.predicates = predicates;
}
@Override
public void evaluate(String currentPath, PathRef ref, Object model, EvaluationContextImpl ctx) {
if (ctx.jsonProvider().isMap(model)) {
if (accept(model, ctx.rootDocument(), ctx.configuration(), ctx)) {
PathRef op = ctx.forUpdate() ? ref : PathRef.NO_OP;
if (isLeaf()) {
ctx.addResult(currentPath, op, model);
} else {
next().evaluate(currentPath, op, model, ctx);
}
}
} else if (ctx.jsonProvider().isArray(model)){
int idx = 0;
Iterable<?> objects = ctx.jsonProvider().toIterable(model);
for (Object idxModel : objects) {
if (accept(idxModel, ctx.rootDocument(), ctx.configuration(), ctx)) {
handleArrayIndex(idx, currentPath, model, ctx);
}
idx++;
}
} else {
if (isUpstreamDefinite()) {
throw new InvalidPathException(format("Filter: %s can not be applied to primitives. Current context is: %s", toString(), model));
}
}
}
public boolean accept(final Object obj, final Object root, final Configuration configuration, EvaluationContextImpl evaluationContext) {
Predicate.PredicateContext ctx = new PredicateContextImpl(obj, root, configuration, evaluationContext.documentEvalCache());

41
json-path/src/main/java/com/jayway/jsonpath/internal/path/PropertyPathToken.java → json-path/src/main/java/com/jayway/jsonpath/internal/path/token/PropertyPathToken.java

@ -12,22 +12,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
package com.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.InvalidPathException;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.Utils;
import java.util.ArrayList;
import java.util.List;
import static com.jayway.jsonpath.internal.Utils.onlyOneIsTrueNonThrow;
/**
*
*/
class PropertyPathToken extends PathToken {
public class PropertyPathToken extends PathToken {
private final List<String> properties;
private final String stringDelimiter;
@ -57,38 +52,6 @@ class PropertyPathToken extends PathToken {
return ! isLeaf() && properties.size() > 1;
}
@Override
public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) {
// Can't assert it in ctor because isLeaf() could be changed later on.
assert onlyOneIsTrueNonThrow(singlePropertyCase(), multiPropertyMergeCase(), multiPropertyIterationCase());
if (!ctx.jsonProvider().isMap(model)) {
if (! isUpstreamDefinite()) {
return;
} else {
String m = model == null ? "null" : model.getClass().getName();
throw new PathNotFoundException(String.format(
"Expected to find an object with property %s in path %s but found '%s'. " +
"This is not a json object according to the JsonProvider: '%s'.",
getPathFragment(), currentPath, m, ctx.configuration().jsonProvider().getClass().getName()));
}
}
if (singlePropertyCase() || multiPropertyMergeCase()) {
handleObjectProperty(currentPath, model, ctx, properties);
return;
}
assert multiPropertyIterationCase();
final List<String> currentlyHandledProperty = new ArrayList<String>(1);
currentlyHandledProperty.add(null);
for (final String property : properties) {
currentlyHandledProperty.set(0, property);
handleObjectProperty(currentPath, model, ctx, currentlyHandledProperty);
}
}
@Override
public boolean isTokenDefinite() {
// in case of leaf multiprops will be merged, so it's kinda definite

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

@ -12,9 +12,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jayway.jsonpath.internal.path;
package com.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.internal.PathRef;
import com.jayway.jsonpath.internal.path.PathTokenAppender;
/**
*
@ -26,7 +26,7 @@ public class RootPathToken extends PathToken {
private final String rootToken;
RootPathToken(char rootToken) {
public RootPathToken(char rootToken) {
this.rootToken = Character.toString(rootToken);;
this.tail = this;
this.tokenCount = 1;
@ -53,16 +53,6 @@ public class RootPathToken extends PathToken {
};
}
@Override
public void evaluate(String currentPath, PathRef pathRef, Object model, EvaluationContextImpl ctx) {
if (isLeaf()) {
PathRef op = ctx.forUpdate() ? pathRef : PathRef.NO_OP;
ctx.addResult(rootToken, op, model);
} else {
next().evaluate(rootToken, pathRef, model, ctx);
}
}
@Override
public String getPathFragment() {
return rootToken;

35
json-path/src/main/java/com/jayway/jsonpath/internal/path/token/ScanPathToken.java

@ -0,0 +1,35 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path.token;
/**
*
*/
public class ScanPathToken extends PathToken {
public ScanPathToken() {
}
@Override
public boolean isTokenDefinite() {
return false;
}
@Override
public String getPathFragment() {
return "..";
}
}

27
json-path/src/main/java/com/jayway/jsonpath/internal/path/token/SliceArrayPathToken.java

@ -0,0 +1,27 @@
package com.jayway.jsonpath.internal.path.token;
import com.jayway.jsonpath.internal.path.operation.ArraySliceOperation;
public class SliceArrayPathToken extends ArrayPathToken {
protected final ArraySliceOperation arraySliceOperation;
public SliceArrayPathToken(final ArraySliceOperation arraySliceOperation) {
super();
if (arraySliceOperation == null) throw new IllegalArgumentException();
this.arraySliceOperation = arraySliceOperation;
}
@Override
public String getPathFragment() {
return arraySliceOperation.toString();
}
@Override
public boolean isTokenDefinite() {
return false;
}
public ArraySliceOperation operation() {
return arraySliceOperation;
}
}

34
json-path/src/main/java/com/jayway/jsonpath/internal/path/token/WildcardPathToken.java

@ -0,0 +1,34 @@
/*
* Copyright 2011 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.jayway.jsonpath.internal.path.token;
/**
*
*/
public class WildcardPathToken extends PathToken {
public WildcardPathToken() {
}
@Override
public boolean isTokenDefinite() {
return false;
}
@Override
public String getPathFragment() {
return "[*]";
}
}

4
json-path/src/test/java/com/jayway/jsonpath/internal/path/PathTokenTest.java

@ -1,6 +1,10 @@
package com.jayway.jsonpath.internal.path;
import com.jayway.jsonpath.BaseTest;
import com.jayway.jsonpath.internal.path.token.PathToken;
import com.jayway.jsonpath.internal.path.token.PropertyPathToken;
import com.jayway.jsonpath.internal.path.token.ScanPathToken;
import com.jayway.jsonpath.internal.path.token.WildcardPathToken;
import org.junit.Test;
import java.util.Arrays;

Loading…
Cancel
Save