|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
import com.jayway.jsonpath.internal.Path;
|
|
|
|
import com.jayway.jsonpath.internal.Utils;
|
|
|
|
import com.jayway.jsonpath.internal.filter.RelationalExpressionNode;
|
|
|
|
import com.jayway.jsonpath.internal.filter.RelationalOperator;
|
|
|
|
import com.jayway.jsonpath.internal.filter.ValueNode;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
import static com.jayway.jsonpath.internal.Utils.notNull;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
public class Criteria implements Predicate {
|
|
|
|
|
|
|
|
private final List<Criteria> criteriaChain;
|
|
|
|
private ValueNode left;
|
|
|
|
private RelationalOperator criteriaType;
|
|
|
|
private ValueNode right;
|
|
|
|
|
|
|
|
private Criteria(List<Criteria> criteriaChain, ValueNode left) {
|
|
|
|
this.left = left;
|
|
|
|
this.criteriaChain = criteriaChain;
|
|
|
|
this.criteriaChain.add(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Criteria(ValueNode left) {
|
|
|
|
this(new LinkedList<Criteria>(), left);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean apply(PredicateContext ctx) {
|
|
|
|
for (RelationalExpressionNode expressionNode : toRelationalExpressionNodes()) {
|
|
|
|
if(!expressionNode.apply(ctx)){
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return Utils.join(" && ", toRelationalExpressionNodes());
|
|
|
|
}
|
|
|
|
|
|
|
|
private Collection<RelationalExpressionNode> toRelationalExpressionNodes(){
|
|
|
|
List<RelationalExpressionNode> nodes = new ArrayList<RelationalExpressionNode>(criteriaChain.size());
|
|
|
|
for (Criteria criteria : criteriaChain) {
|
|
|
|
nodes.add(new RelationalExpressionNode(criteria.left, criteria.criteriaType, criteria.right));
|
|
|
|
}
|
|
|
|
return nodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Static factory method to create a Criteria using the provided key
|
|
|
|
*
|
|
|
|
* @param key filed name
|
|
|
|
* @return the new criteria
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
//This should be private.It exposes internal classes
|
|
|
|
public static Criteria where(Path key) {
|
|
|
|
return new Criteria(ValueNode.createPathNode(key));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Static factory method to create a Criteria using the provided key
|
|
|
|
*
|
|
|
|
* @param key filed name
|
|
|
|
* @return the new criteria
|
|
|
|
*/
|
|
|
|
|
|
|
|
public static Criteria where(String key) {
|
|
|
|
return new Criteria(ValueNode.toValueNode(prefixPath(key)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Static factory method to create a Criteria using the provided key
|
|
|
|
*
|
|
|
|
* @param key ads new filed to criteria
|
|
|
|
* @return the criteria builder
|
|
|
|
*/
|
|
|
|
public Criteria and(String key) {
|
|
|
|
checkComplete();
|
|
|
|
return new Criteria(this.criteriaChain, ValueNode.toValueNode(prefixPath(key)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using equality
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria is(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.EQ;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using equality
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria eq(Object o) {
|
|
|
|
return is(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using the <b>!=</b> operator
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria ne(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.NE;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using the <b><</b> operator
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria lt(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.LT;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using the <b><=</b> operator
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria lte(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.LTE;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using the <b>></b> operator
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria gt(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.GT;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using the <b>>=</b> operator
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria gte(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.GTE;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a criterion using a Regex
|
|
|
|
*
|
|
|
|
* @param pattern
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria regex(Pattern pattern) {
|
|
|
|
notNull(pattern, "pattern can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.REGEX;
|
|
|
|
this.right = ValueNode.toValueNode(pattern);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>in</code> operator is analogous to the SQL IN modifier, allowing you
|
|
|
|
* to specify an array of possible matches.
|
|
|
|
*
|
|
|
|
* @param o the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria in(Object... o) {
|
|
|
|
return in(Arrays.asList(o));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>in</code> operator is analogous to the SQL IN modifier, allowing you
|
|
|
|
* to specify an array of possible matches.
|
|
|
|
*
|
|
|
|
* @param c the collection containing the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria in(Collection<?> c) {
|
|
|
|
notNull(c, "collection can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.IN;
|
|
|
|
this.right = new ValueNode.ValueListNode(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>contains</code> operator asserts that the provided object is contained
|
|
|
|
* in the result. The object that should contain the input can be either an object or a String.
|
|
|
|
*
|
|
|
|
* @param o that should exists in given collection or
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria contains(Object o) {
|
|
|
|
this.criteriaType = RelationalOperator.CONTAINS;
|
|
|
|
this.right = ValueNode.toValueNode(o);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>nin</code> operator is similar to $in except that it selects objects for
|
|
|
|
* which the specified field does not have any value in the specified array.
|
|
|
|
*
|
|
|
|
* @param o the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria nin(Object... o) {
|
|
|
|
return nin(Arrays.asList(o));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>nin</code> operator is similar to $in except that it selects objects for
|
|
|
|
* which the specified field does not have any value in the specified array.
|
|
|
|
*
|
|
|
|
* @param c the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria nin(Collection<?> c) {
|
|
|
|
notNull(c, "collection can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.NIN;
|
|
|
|
this.right = new ValueNode.ValueListNode(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>subsetof</code> operator selects objects for which the specified field is
|
|
|
|
* an array whose elements comprise a subset of the set comprised by the elements of
|
|
|
|
* the specified array.
|
|
|
|
*
|
|
|
|
* @param o the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria subsetof(Object... o) {
|
|
|
|
return subsetof(Arrays.asList(o));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>subsetof</code> operator selects objects for which the specified field is
|
|
|
|
* an array whose elements comprise a subset of the set comprised by the elements of
|
|
|
|
* the specified array.
|
|
|
|
*
|
|
|
|
* @param c the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria subsetof(Collection<?> c) {
|
|
|
|
notNull(c, "collection can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.SUBSETOF;
|
|
|
|
this.right = new ValueNode.ValueListNode(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>anyof</code> operator selects objects for which the specified field is
|
|
|
|
* an array that contain at least an element in the specified array.
|
|
|
|
*
|
|
|
|
* @param o the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria anyof(Object... o) {
|
|
|
|
return subsetof(Arrays.asList(o));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>anyof</code> operator selects objects for which the specified field is
|
|
|
|
* an array that contain at least an element in the specified array.
|
|
|
|
*
|
|
|
|
* @param c the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria anyof(Collection<?> c) {
|
|
|
|
notNull(c, "collection can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.ANYOF;
|
|
|
|
this.right = new ValueNode.ValueListNode(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>noneof</code> operator selects objects for which the specified field is
|
|
|
|
* an array that does not contain any of the elements of the specified array.
|
|
|
|
*
|
|
|
|
* @param o the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria noneof(Object... o) {
|
|
|
|
return subsetof(Arrays.asList(o));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>noneof</code> operator selects objects for which the specified field is
|
|
|
|
* an array that does not contain any of the elements of the specified array.
|
|
|
|
*
|
|
|
|
* @param c the values to match against
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria noneof(Collection<?> c) {
|
|
|
|
notNull(c, "collection can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.NONEOF;
|
|
|
|
this.right = new ValueNode.ValueListNode(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>all</code> operator is similar to $in, but instead of matching any value
|
|
|
|
* in the specified array all values in the array must be matched.
|
|
|
|
*
|
|
|
|
* @param o
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria all(Object... o) {
|
|
|
|
return all(Arrays.asList(o));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>all</code> operator is similar to $in, but instead of matching any value
|
|
|
|
* in the specified array all values in the array must be matched.
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria all(Collection<?> c) {
|
|
|
|
notNull(c, "collection can not be null");
|
|
|
|
this.criteriaType = RelationalOperator.ALL;
|
|
|
|
this.right = new ValueNode.ValueListNode(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>size</code> operator matches:
|
|
|
|
* <p/>
|
|
|
|
* <ol>
|
|
|
|
* <li>array with the specified number of elements.</li>
|
|
|
|
* <li>string with given length.</li>
|
|
|
|
* </ol>
|
|
|
|
*
|
|
|
|
* @param size
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria size(int size) {
|
|
|
|
this.criteriaType = RelationalOperator.SIZE;
|
|
|
|
this.right = ValueNode.toValueNode(size);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The $type operator matches values based on their Java JSON type.
|
|
|
|
*
|
|
|
|
* Supported types are:
|
|
|
|
*
|
|
|
|
* List.class
|
|
|
|
* Map.class
|
|
|
|
* String.class
|
|
|
|
* Number.class
|
|
|
|
* Boolean.class
|
|
|
|
*
|
|
|
|
* Other types evaluates to false
|
|
|
|
*
|
|
|
|
* @param clazz
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria type(Class<?> clazz) {
|
|
|
|
this.criteriaType = RelationalOperator.TYPE;
|
|
|
|
this.right = ValueNode.createClassNode(clazz);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check for existence (or lack thereof) of a field.
|
|
|
|
*
|
|
|
|
* @param shouldExist
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria exists(boolean shouldExist) {
|
|
|
|
this.criteriaType = RelationalOperator.EXISTS;
|
|
|
|
this.right = ValueNode.toValueNode(shouldExist);
|
|
|
|
this.left = left.asPathNode().asExistsCheck(shouldExist);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>notEmpty</code> operator checks that an array or String is not empty.
|
|
|
|
*
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public Criteria notEmpty() {
|
|
|
|
return empty(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>notEmpty</code> operator checks that an array or String is empty.
|
|
|
|
*
|
|
|
|
* @param empty should be empty
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria empty(boolean empty) {
|
|
|
|
this.criteriaType = RelationalOperator.EMPTY;
|
|
|
|
this.right = empty ? ValueNode.TRUE : ValueNode.FALSE;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>matches</code> operator checks that an object matches the given predicate.
|
|
|
|
*
|
|
|
|
* @param p
|
|
|
|
* @return the criteria
|
|
|
|
*/
|
|
|
|
public Criteria matches(Predicate p) {
|
|
|
|
this.criteriaType = RelationalOperator.MATCHES;
|
|
|
|
this.right = new ValueNode.PredicateNode(p);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse the provided criteria
|
|
|
|
*
|
|
|
|
* Deprecated use {@link Filter#parse(String)}
|
|
|
|
*
|
|
|
|
* @param criteria
|
|
|
|
* @return a criteria
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public static Criteria parse(String criteria) {
|
|
|
|
if(criteria == null){
|
|
|
|
throw new InvalidPathException("Criteria can not be null");
|
|
|
|
}
|
|
|
|
String[] split = criteria.trim().split(" ");
|
|
|
|
if(split.length == 3){
|
|
|
|
return create(split[0], split[1], split[2]);
|
|
|
|
} else if(split.length == 1){
|
|
|
|
return create(split[0], "EXISTS", "true");
|
|
|
|
} else {
|
|
|
|
throw new InvalidPathException("Could not parse criteria");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new criteria
|
|
|
|
* @param left path to evaluate in criteria
|
|
|
|
* @param operator operator
|
|
|
|
* @param right expected value
|
|
|
|
* @return a new Criteria
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public static Criteria create(String left, String operator, String right) {
|
|
|
|
Criteria criteria = new Criteria(ValueNode.toValueNode(left));
|
|
|
|
criteria.criteriaType = RelationalOperator.fromString(operator);
|
|
|
|
criteria.right = ValueNode.toValueNode(right);
|
|
|
|
return criteria;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static String prefixPath(String key){
|
|
|
|
if (!key.startsWith("$") && !key.startsWith("@")) {
|
|
|
|
key = "@." + key;
|
|
|
|
}
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void checkComplete(){
|
|
|
|
boolean complete = (left != null && criteriaType != null && right != null);
|
|
|
|
if(!complete){
|
|
|
|
throw new JsonPathException("Criteria build exception. Complete on criteria before defining next.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|