Kalle Stenflo
9 years ago
26 changed files with 2210 additions and 1208 deletions
File diff suppressed because it is too large
Load Diff
@ -1,109 +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; |
|
||||||
|
|
||||||
import java.util.Deque; |
|
||||||
import java.util.LinkedList; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
import java.util.concurrent.locks.ReentrantLock; |
|
||||||
|
|
||||||
public class Cache { |
|
||||||
|
|
||||||
private final ReentrantLock lock = new ReentrantLock(); |
|
||||||
|
|
||||||
private final Map<String, Path> map = new ConcurrentHashMap<String, Path>(); |
|
||||||
private final Deque<String> queue = new LinkedList<String>(); |
|
||||||
private final int limit; |
|
||||||
|
|
||||||
public Cache(int limit) { |
|
||||||
this.limit = limit; |
|
||||||
} |
|
||||||
|
|
||||||
public void put(String key, Path value) { |
|
||||||
Path oldValue = map.put(key, value); |
|
||||||
if (oldValue != null) { |
|
||||||
removeThenAddKey(key); |
|
||||||
} else { |
|
||||||
addKey(key); |
|
||||||
} |
|
||||||
if (map.size() > limit) { |
|
||||||
map.remove(removeLast()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public Path get(String key) { |
|
||||||
if(map.containsKey(key)){ |
|
||||||
removeThenAddKey(key); |
|
||||||
} |
|
||||||
return map.get(key); |
|
||||||
} |
|
||||||
|
|
||||||
private void addKey(String key) { |
|
||||||
lock.lock(); |
|
||||||
try { |
|
||||||
queue.addFirst(key); |
|
||||||
} finally { |
|
||||||
lock.unlock(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private String removeLast() { |
|
||||||
lock.lock(); |
|
||||||
try { |
|
||||||
final String removedKey = queue.removeLast(); |
|
||||||
return removedKey; |
|
||||||
} finally { |
|
||||||
lock.unlock(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void removeThenAddKey(String key) { |
|
||||||
lock.lock(); |
|
||||||
try { |
|
||||||
queue.removeFirstOccurrence(key); |
|
||||||
queue.addFirst(key); |
|
||||||
} finally { |
|
||||||
lock.unlock(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
private void removeFirstOccurrence(String key) { |
|
||||||
lock.lock(); |
|
||||||
try { |
|
||||||
queue.removeFirstOccurrence(key); |
|
||||||
} finally { |
|
||||||
lock.unlock(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public Path getSilent(String key) { |
|
||||||
return map.get(key); |
|
||||||
} |
|
||||||
|
|
||||||
public void remove(String key) { |
|
||||||
removeFirstOccurrence(key); |
|
||||||
map.remove(key); |
|
||||||
} |
|
||||||
|
|
||||||
public int size() { |
|
||||||
return map.size(); |
|
||||||
} |
|
||||||
|
|
||||||
public String toString() { |
|
||||||
return map.toString(); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,280 @@ |
|||||||
|
package com.jayway.jsonpath.internal; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.InvalidPathException; |
||||||
|
|
||||||
|
public class CharacterIndex { |
||||||
|
|
||||||
|
private static final char OPEN_BRACKET = '('; |
||||||
|
private static final char CLOSE_BRACKET = ')'; |
||||||
|
private static final char CLOSE_SQUARE_BRACKET = ']'; |
||||||
|
private static final char SPACE = ' '; |
||||||
|
private static final char ESCAPE = '\\'; |
||||||
|
private static final char TICK = '\''; |
||||||
|
private static final char MINUS = '-'; |
||||||
|
private static final char PERIOD = '.'; |
||||||
|
private static final char REGEX = '/'; |
||||||
|
|
||||||
|
private final CharSequence charSequence; |
||||||
|
private int position; |
||||||
|
|
||||||
|
public CharacterIndex(CharSequence charSequence) { |
||||||
|
this.charSequence = charSequence; |
||||||
|
this.position = 0; |
||||||
|
} |
||||||
|
|
||||||
|
public int length() { |
||||||
|
return charSequence.length(); |
||||||
|
} |
||||||
|
|
||||||
|
public char charAt(int idx) { |
||||||
|
return charSequence.charAt(idx); |
||||||
|
} |
||||||
|
|
||||||
|
public char currentChar() { |
||||||
|
return charSequence.charAt(position); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean currentCharIs(char c) { |
||||||
|
return (charSequence.charAt(position) == c); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean nextCharIs(char c) { |
||||||
|
return inBounds(position + 1) && (charSequence.charAt(position + 1) == c); |
||||||
|
} |
||||||
|
|
||||||
|
public int incrementPosition(int charCount) { |
||||||
|
return setPosition(position + charCount); |
||||||
|
} |
||||||
|
|
||||||
|
public int setPosition(int newPosition) { |
||||||
|
//position = min(newPosition, charSequence.length() - 1);
|
||||||
|
position = newPosition; |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
public int position(){ |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
public int indexOfClosingSquareBracket(int startPosition) { |
||||||
|
int readPosition = startPosition; |
||||||
|
while (inBounds(readPosition)) { |
||||||
|
if(charAt(readPosition) == CLOSE_SQUARE_BRACKET){ |
||||||
|
return readPosition; |
||||||
|
} |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
public int indexOfMatchingCloseChar(int startPosition, char openChar, char closeChar, boolean skipStrings, boolean skipRegex) { |
||||||
|
if(charAt(startPosition) != openChar){ |
||||||
|
throw new InvalidPathException("Expected " + openChar + " but found " + charAt(startPosition)); |
||||||
|
} |
||||||
|
|
||||||
|
int opened = 1; |
||||||
|
int readPosition = startPosition + 1; |
||||||
|
while (inBounds(readPosition)) { |
||||||
|
if (skipStrings) { |
||||||
|
if (charAt(readPosition) == TICK) { |
||||||
|
boolean escaped = false; |
||||||
|
while (inBounds(readPosition)) { |
||||||
|
readPosition++; |
||||||
|
if (escaped) { |
||||||
|
escaped = false; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (charAt(readPosition) == ESCAPE) { |
||||||
|
escaped = true; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (charAt(readPosition) == TICK) { |
||||||
|
readPosition++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (skipRegex) { |
||||||
|
if (charAt(readPosition) == REGEX) { |
||||||
|
while (inBounds(readPosition)) { |
||||||
|
readPosition++; |
||||||
|
if (charAt(readPosition) == REGEX) { |
||||||
|
readPosition++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (charAt(readPosition) == openChar) { |
||||||
|
opened++; |
||||||
|
} |
||||||
|
if (charAt(readPosition) == closeChar) { |
||||||
|
opened--; |
||||||
|
if (opened == 0) { |
||||||
|
return readPosition; |
||||||
|
} |
||||||
|
} |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
public int indexOfClosingBracket(int startPosition, boolean skipStrings, boolean skipRegex) { |
||||||
|
return indexOfMatchingCloseChar(startPosition, OPEN_BRACKET, CLOSE_BRACKET, skipStrings, skipRegex); |
||||||
|
} |
||||||
|
|
||||||
|
public int indexOfNextSignificantChar(char c) { |
||||||
|
return indexOfNextSignificantChar(position, c); |
||||||
|
} |
||||||
|
|
||||||
|
public int indexOfNextSignificantChar(int startPosition, char c) { |
||||||
|
int readPosition = startPosition + 1; |
||||||
|
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) { |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
if (charAt(readPosition) == c) { |
||||||
|
return readPosition; |
||||||
|
} else { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int nextIndexOf(char c) { |
||||||
|
return nextIndexOf(position + 1, c); |
||||||
|
} |
||||||
|
|
||||||
|
public int nextIndexOf(int startPosition, char c) { |
||||||
|
int readPosition = startPosition; |
||||||
|
while (!isOutOfBounds(readPosition)) { |
||||||
|
if (charAt(readPosition) == c) { |
||||||
|
return readPosition; |
||||||
|
} |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
public int nextIndexOfUnescaped(char c) { |
||||||
|
return nextIndexOfUnescaped(position + 1, c); |
||||||
|
} |
||||||
|
|
||||||
|
public int nextIndexOfUnescaped(int startPosition, char c) { |
||||||
|
int readPosition = startPosition; |
||||||
|
char prev1; |
||||||
|
char prev2; |
||||||
|
while (!isOutOfBounds(readPosition)) { |
||||||
|
prev1 = charAtOr(readPosition - 1, ' '); |
||||||
|
prev2 = charAtOr(readPosition - 2, ' '); |
||||||
|
boolean ignore = (prev1 == '\\' && prev2 == '\\'); |
||||||
|
boolean escaped = (prev1 == '\\' && !ignore); |
||||||
|
|
||||||
|
if (charAt(readPosition) == c && !escaped) { |
||||||
|
return readPosition; |
||||||
|
} |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
public char charAtOr(int postition, char defaultChar){ |
||||||
|
if(!inBounds(postition)) return defaultChar; |
||||||
|
else return charAt(postition); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean nextSignificantCharIs(int startPosition, char c) { |
||||||
|
int readPosition = startPosition + 1; |
||||||
|
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) { |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
return !isOutOfBounds(readPosition) && charAt(readPosition) == c; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean nextSignificantCharIs(char c) { |
||||||
|
return nextSignificantCharIs(position, c); |
||||||
|
} |
||||||
|
|
||||||
|
public char nextSignificantChar() { |
||||||
|
return nextSignificantChar(position); |
||||||
|
} |
||||||
|
|
||||||
|
public char nextSignificantChar(int startPosition) { |
||||||
|
int readPosition = startPosition + 1; |
||||||
|
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) { |
||||||
|
readPosition++; |
||||||
|
} |
||||||
|
if (!isOutOfBounds(readPosition)) { |
||||||
|
return charAt(readPosition); |
||||||
|
} else { |
||||||
|
return ' '; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int indexOfPreviousSignificantChar(int startPosition){ |
||||||
|
int readPosition = startPosition - 1; |
||||||
|
while (!isOutOfBounds(readPosition) && charAt(readPosition) == SPACE) { |
||||||
|
readPosition--; |
||||||
|
} |
||||||
|
if (!isOutOfBounds(readPosition)) { |
||||||
|
return readPosition; |
||||||
|
} else { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int indexOfPreviousSignificantChar(){ |
||||||
|
return indexOfPreviousSignificantChar(position); |
||||||
|
} |
||||||
|
|
||||||
|
public char previousSignificantChar(int startPosition) { |
||||||
|
int previousSignificantCharIndex = indexOfPreviousSignificantChar(startPosition); |
||||||
|
if(previousSignificantCharIndex == -1) return ' '; |
||||||
|
else return charAt(previousSignificantCharIndex); |
||||||
|
} |
||||||
|
|
||||||
|
public char previousSignificantChar() { |
||||||
|
return previousSignificantChar(position); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean currentIsTail() { |
||||||
|
return isOutOfBounds(position + 1); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasMoreCharacters() { |
||||||
|
return inBounds(position + 1); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean inBounds(int idx) { |
||||||
|
return (idx >= 0) && (idx < charSequence.length()); |
||||||
|
} |
||||||
|
public boolean inBounds() { |
||||||
|
return inBounds(position); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isOutOfBounds(int idx) { |
||||||
|
return !inBounds(idx); |
||||||
|
} |
||||||
|
|
||||||
|
public CharSequence subSequence(int start, int end) { |
||||||
|
return charSequence.subSequence(start, end); |
||||||
|
} |
||||||
|
|
||||||
|
public CharSequence charSequence() { |
||||||
|
return charSequence; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return charSequence.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isNumberCharacter(int readPosition) { |
||||||
|
char c = charAt(readPosition); |
||||||
|
return Character.isDigit(c) || c == MINUS || c == PERIOD; |
||||||
|
} |
||||||
|
|
||||||
|
public CharacterIndex skipBlanks() { |
||||||
|
while (inBounds() && currentChar() == SPACE){ |
||||||
|
incrementPosition(1); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
|
||||||
|
public interface Evaluator { |
||||||
|
boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx); |
||||||
|
} |
@ -0,0 +1,220 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPathException; |
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
public class EvaluatorFactory { |
||||||
|
|
||||||
|
private static final Map<RelationalOperator, Evaluator> evaluators = new HashMap<RelationalOperator, Evaluator>(); |
||||||
|
|
||||||
|
static { |
||||||
|
evaluators.put(RelationalOperator.EXISTS, new ExistsEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.NE, new NotEqualsEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.EQ, new EqualsEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.LT, new LessThanEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.LTE, new LessThanEqualsEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.GT, new GreaterThanEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.GTE, new GreaterThanEqualsEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.REGEX, new RegexpEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.SIZE, new SizeEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.NOT_EMPTY, new NotEmptyEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.IN, new InEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.NIN, new NotInEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.ALL, new AllEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.CONTAINS, new ContainsEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator()); |
||||||
|
evaluators.put(RelationalOperator.TYPE, new TypeEvaluator()); |
||||||
|
} |
||||||
|
|
||||||
|
public static Evaluator createEvaluator(RelationalOperator operator){ |
||||||
|
return evaluators.get(operator); |
||||||
|
} |
||||||
|
|
||||||
|
private static class ExistsEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(!left.isBooleanNode() && !right.isBooleanNode()){ |
||||||
|
throw new JsonPathException("Failed to evaluate exists expression"); |
||||||
|
} |
||||||
|
return left.asBooleanNode().getBoolean() == right.asBooleanNode().getBoolean(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class NotEqualsEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
return !left.equals(right); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class EqualsEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isJsonNode() && right.isJsonNode()){ |
||||||
|
return left.asJsonNode().equals(right.asJsonNode(), ctx); |
||||||
|
} else { |
||||||
|
return left.equals(right); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class TypeEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
return right.asClassNode().getClazz() == left.type(ctx); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class LessThanEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isNumberNode() && right.isNumberNode()){ |
||||||
|
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) < 0; |
||||||
|
} if(left.isStringNode() && right.isStringNode()){ |
||||||
|
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) < 0; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class LessThanEqualsEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isNumberNode() && right.isNumberNode()){ |
||||||
|
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) <= 0; |
||||||
|
} if(left.isStringNode() && right.isStringNode()){ |
||||||
|
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) <= 0; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class GreaterThanEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isNumberNode() && right.isNumberNode()){ |
||||||
|
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) > 0; |
||||||
|
} else if(left.isStringNode() && right.isStringNode()){ |
||||||
|
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) > 0; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class GreaterThanEqualsEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isNumberNode() && right.isNumberNode()){ |
||||||
|
return left.asNumberNode().getNumber().compareTo(right.asNumberNode().getNumber()) >= 0; |
||||||
|
} else if(left.isStringNode() && right.isStringNode()){ |
||||||
|
return left.asStringNode().getString().compareTo(right.asStringNode().getString()) >= 0; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class SizeEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
int expectedSize = right.asNumberNode().getNumber().intValue(); |
||||||
|
|
||||||
|
if(left.isStringNode()){ |
||||||
|
return left.asStringNode().length() == expectedSize; |
||||||
|
} else if(left.isJsonNode()){ |
||||||
|
return left.asJsonNode().length(ctx) == expectedSize; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class NotEmptyEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isStringNode()){ |
||||||
|
return !left.asStringNode().isEmpty(); |
||||||
|
} else if(left.isJsonNode()){ |
||||||
|
return !left.asJsonNode().isEmpty(ctx); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class InEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
ValueNode.ValueListNode valueListNode = right.asValueListNode(); |
||||||
|
return valueListNode.contains(left); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class NotInEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
return !evaluators.get(RelationalOperator.IN).evaluate(left, right, ctx); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class AllEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
ValueNode.ValueListNode requiredValues = right.asValueListNode(); |
||||||
|
|
||||||
|
if(left.isJsonNode()){ |
||||||
|
ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); //returns UndefinedNode if conversion is not possible
|
||||||
|
if(valueNode.isValueListNode()){ |
||||||
|
ValueNode.ValueListNode shouldContainAll = valueNode.asValueListNode(); |
||||||
|
for (ValueNode required : requiredValues) { |
||||||
|
if(!shouldContainAll.contains(required)){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class ContainsEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(left.isStringNode() && right.isStringNode()){ |
||||||
|
return left.asStringNode().contains(right.asStringNode().getString()); |
||||||
|
} else if(left.isJsonNode()){ |
||||||
|
ValueNode valueNode = left.asJsonNode().asValueListNode(ctx); |
||||||
|
if(valueNode.isUndefinedNode()) return false; |
||||||
|
else { |
||||||
|
boolean res = valueNode.asValueListNode().contains(right); |
||||||
|
return res; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class PredicateMatchEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
return right.asPredicateNode().getPredicate().apply(ctx); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class RegexpEvaluator implements Evaluator { |
||||||
|
@Override |
||||||
|
public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { |
||||||
|
if(!(left.isPatternNode() ^ right.isPatternNode())){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
if(!(left.isStringNode() ^ right.isStringNode())){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
ValueNode.PatternNode patternNode = left.isPatternNode() ? left.asPatternNode() : right.asPatternNode(); |
||||||
|
ValueNode.StringNode stringNode = left.isStringNode() ? left.asStringNode() : right.asStringNode(); |
||||||
|
|
||||||
|
return patternNode.getCompiledPattern().matcher(stringNode.getString()).matches(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
|
||||||
|
public abstract class ExpressionNode implements Predicate { |
||||||
|
|
||||||
|
public static ExpressionNode createExpressionNode(ExpressionNode right, LogicalOperator operator, ExpressionNode left){ |
||||||
|
if(operator == LogicalOperator.AND){ |
||||||
|
if((left instanceof LogicalExpressionNode) && ((LogicalExpressionNode)left).getOperator() == LogicalOperator.AND ){ |
||||||
|
LogicalExpressionNode len = (LogicalExpressionNode) left; |
||||||
|
return len.append(right); |
||||||
|
} else { |
||||||
|
return LogicalExpressionNode.createLogicalAnd(left, right); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if((left instanceof LogicalExpressionNode) && ((LogicalExpressionNode)left).getOperator() == LogicalOperator.OR ){ |
||||||
|
LogicalExpressionNode len = (LogicalExpressionNode) left; |
||||||
|
return len.append(right); |
||||||
|
} else { |
||||||
|
return LogicalExpressionNode.createLogicalOr(left, right); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,343 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.InvalidPathException; |
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
import com.jayway.jsonpath.internal.CharacterIndex; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.util.Stack; |
||||||
|
|
||||||
|
public class FilterCompiler { |
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class); |
||||||
|
|
||||||
|
private static final char DOC_CONTEXT = '$'; |
||||||
|
private static final char EVAL_CONTEXT = '@'; |
||||||
|
private static final char OPEN_SQUARE_BRACKET = '['; |
||||||
|
private static final char CLOSE_SQUARE_BRACKET = ']'; |
||||||
|
private static final char OPEN_BRACKET = '('; |
||||||
|
private static final char CLOSE_BRACKET = ')'; |
||||||
|
private static final char WILDCARD = '*'; |
||||||
|
private static final char PERIOD = '.'; |
||||||
|
private static final char SPACE = ' '; |
||||||
|
private static final char QUESTIONMARK = '?'; |
||||||
|
private static final char COMMA = ','; |
||||||
|
private static final char SPLIT = ':'; |
||||||
|
private static final char MINUS = '-'; |
||||||
|
private static final char TICK = '\''; |
||||||
|
private static final char FUNCTION = '%'; |
||||||
|
private static final char OPERATOR_PIPE = '¦'; |
||||||
|
private static final char LT = '<'; |
||||||
|
private static final char GT = '>'; |
||||||
|
private static final char EQ = '='; |
||||||
|
private static final char TILDE = '~'; |
||||||
|
private static final char TRUE = 't'; |
||||||
|
private static final char FALSE = 'f'; |
||||||
|
private static final char NULL = 'n'; |
||||||
|
private static final char AND = '&'; |
||||||
|
private static final char OR = '|'; |
||||||
|
private static final char OBJECT_OPEN = '{'; |
||||||
|
private static final char OBJECT_CLOSE = '}'; |
||||||
|
private static final char ARRAY_OPEN = '['; |
||||||
|
private static final char ARRAY_CLOSE = ']'; |
||||||
|
private static final char BANG = '!'; |
||||||
|
private static final char PATTERN = '/'; |
||||||
|
|
||||||
|
private CharacterIndex filter; |
||||||
|
|
||||||
|
public static Predicate compile(String filterString) { |
||||||
|
FilterCompiler compiler = new FilterCompiler(filterString); |
||||||
|
return compiler.compile(); |
||||||
|
} |
||||||
|
|
||||||
|
private FilterCompiler(String filterString) { |
||||||
|
filterString = filterString.trim(); |
||||||
|
if (!filterString.startsWith("[") || !filterString.endsWith("]")) { |
||||||
|
throw new InvalidPathException("Filter must start with '[' and end with ']'. " + filterString); |
||||||
|
} |
||||||
|
filterString = filterString.substring(1, filterString.length() - 1).trim(); |
||||||
|
if (!filterString.startsWith("?")) { |
||||||
|
throw new InvalidPathException("Filter must start with '[?' and end with ']'. " + filterString); |
||||||
|
} |
||||||
|
filterString = filterString.substring(1).trim(); |
||||||
|
if (!filterString.startsWith("(") || !filterString.endsWith(")")) { |
||||||
|
throw new InvalidPathException("Filter must start with '[?(' and end with ')]'. " + filterString); |
||||||
|
} |
||||||
|
// filterString = filterString.substring(1, filterString.length() - 1).trim();
|
||||||
|
|
||||||
|
filter = new CharacterIndex(filterString); |
||||||
|
} |
||||||
|
|
||||||
|
public Predicate compile() { |
||||||
|
|
||||||
|
Stack<LogicalOperator> opsStack = new Stack<LogicalOperator>(); |
||||||
|
Stack<ExpressionNode> expStack = new Stack<ExpressionNode>(); |
||||||
|
|
||||||
|
int unbalancedBrackets = 0; |
||||||
|
|
||||||
|
while (filter.skipBlanks().inBounds()) { |
||||||
|
int pos = filter.position(); |
||||||
|
|
||||||
|
switch (filter.currentChar()) { |
||||||
|
case OPEN_BRACKET: |
||||||
|
unbalancedBrackets++; |
||||||
|
filter.incrementPosition(1); |
||||||
|
break; |
||||||
|
case CLOSE_BRACKET: |
||||||
|
unbalancedBrackets--; |
||||||
|
filter.incrementPosition(1); |
||||||
|
while(!opsStack.isEmpty()){ |
||||||
|
//expStack.push(opsStack.pop().createExpressionNode(expStack.pop(), expStack.pop()));
|
||||||
|
expStack.push(ExpressionNode.createExpressionNode(expStack.pop(), opsStack.pop(), expStack.pop())); |
||||||
|
} |
||||||
|
break; |
||||||
|
case BANG: |
||||||
|
filter.incrementPosition(1); |
||||||
|
break; |
||||||
|
case OR: |
||||||
|
case AND: |
||||||
|
LogicalOperator operatorNode = readLogicalOperator(); |
||||||
|
opsStack.push(operatorNode); |
||||||
|
break; |
||||||
|
default: |
||||||
|
RelationalExpressionNode relationalExpressionNode = readExpression(); |
||||||
|
expStack.push(relationalExpressionNode); |
||||||
|
break; |
||||||
|
} |
||||||
|
if(pos >= filter.position()){ |
||||||
|
throw new InvalidPathException("Failed to parse filter " + filter.toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
if(unbalancedBrackets > 0){ |
||||||
|
throw new InvalidPathException("Failed to parse filter. Brackets are not balanced. " + filter.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
Predicate predicate = expStack.pop(); |
||||||
|
logger.trace("--------> {}", predicate.toString()); |
||||||
|
|
||||||
|
return predicate; |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode readValueNode() { |
||||||
|
switch (filter.skipBlanks().currentChar()) { |
||||||
|
case DOC_CONTEXT : return readPath(); |
||||||
|
case EVAL_CONTEXT : return readPath(); |
||||||
|
default : return readLiteral(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode readLiteral(){ |
||||||
|
switch (filter.skipBlanks().currentChar()){ |
||||||
|
case TICK: return readStringLiteral(); |
||||||
|
case TRUE: return readBooleanLiteral(); |
||||||
|
case FALSE: return readBooleanLiteral(); |
||||||
|
case MINUS: return readNumberLiteral(); |
||||||
|
case NULL: return readNullLiteral(); |
||||||
|
case OBJECT_OPEN: return readJsonLiteral(); |
||||||
|
case ARRAY_OPEN: return readJsonLiteral(); |
||||||
|
case PATTERN: return readPattern(); |
||||||
|
default: return readNumberLiteral(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private RelationalExpressionNode readExpression() { |
||||||
|
ValueNode left = readValueNode(); |
||||||
|
if (left.isPathNode()) { |
||||||
|
final PathNode pathNode = left.asPathNode(); |
||||||
|
if (pathNode.isExistsCheck()) { |
||||||
|
|
||||||
|
return new RelationalExpressionNode(pathNode, RelationalOperator.fromString("¦EXISTS¦"), pathNode.shouldExists() ? ValueNode.TRUE : ValueNode.FALSE); |
||||||
|
} |
||||||
|
} |
||||||
|
RelationalOperator operator = readRelationalOperator(); |
||||||
|
ValueNode right = ValueNode.TRUE; |
||||||
|
if(operator != RelationalOperator.NOT_EMPTY) { |
||||||
|
right = readValueNode(); |
||||||
|
} |
||||||
|
|
||||||
|
return new RelationalExpressionNode(left, operator, right); |
||||||
|
} |
||||||
|
|
||||||
|
private LogicalOperator readLogicalOperator(){ |
||||||
|
int begin = filter.skipBlanks().position(); |
||||||
|
int end = begin+1; |
||||||
|
|
||||||
|
if(!filter.inBounds(end)){ |
||||||
|
throw new InvalidPathException("Expected boolean literal"); |
||||||
|
} |
||||||
|
CharSequence logicalOperator = filter.subSequence(begin, end+1); |
||||||
|
if(!logicalOperator.equals("||") && !logicalOperator.equals("&&")){ |
||||||
|
throw new InvalidPathException("Expected logical operator"); |
||||||
|
} |
||||||
|
filter.incrementPosition(logicalOperator.length()); |
||||||
|
logger.trace("LogicalOperator from {} to {} -> [{}]", begin, end, logicalOperator); |
||||||
|
|
||||||
|
return LogicalOperator.fromString(logicalOperator.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
private RelationalOperator readRelationalOperator() { |
||||||
|
int begin = filter.skipBlanks().position(); |
||||||
|
|
||||||
|
if (filter.currentChar() == OPERATOR_PIPE) { |
||||||
|
int closingOperatorIndex = filter.nextIndexOf(OPERATOR_PIPE); |
||||||
|
if (closingOperatorIndex == -1) { |
||||||
|
throw new InvalidPathException("Operator not closed. Expected " + OPERATOR_PIPE + " in " + filter); |
||||||
|
} else { |
||||||
|
filter.setPosition(closingOperatorIndex + 1); |
||||||
|
} |
||||||
|
} else { |
||||||
|
while (filter.inBounds() && isRelationalOperatorChar(filter.currentChar())) { |
||||||
|
filter.incrementPosition(1); |
||||||
|
} |
||||||
|
} |
||||||
|
CharSequence operator = filter.subSequence(begin, filter.position()); |
||||||
|
logger.trace("Operator from {} to {} -> [{}]", begin, filter.position()-1, operator); |
||||||
|
return RelationalOperator.fromString(operator.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode.NullNode readNullLiteral() { |
||||||
|
int begin = filter.position(); |
||||||
|
if(filter.currentChar() == 'n' && filter.inBounds(filter.position() + 3)){ |
||||||
|
CharSequence nullValue = filter.subSequence(filter.position(), filter.position() + 4); |
||||||
|
if("null".endsWith(nullValue.toString())){ |
||||||
|
logger.trace("NullLiteral from {} to {} -> [{}]", begin, filter.position()+3, nullValue); |
||||||
|
filter.incrementPosition(nullValue.length()); |
||||||
|
return ValueNode.createNullNode(); |
||||||
|
} |
||||||
|
} |
||||||
|
throw new InvalidPathException("Expected <null> value"); |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode.JsonNode readJsonLiteral(){ |
||||||
|
int begin = filter.position(); |
||||||
|
|
||||||
|
char openChar = filter.currentChar(); |
||||||
|
|
||||||
|
assert openChar == ARRAY_OPEN || openChar == OBJECT_OPEN; |
||||||
|
|
||||||
|
char closeChar = openChar == ARRAY_OPEN ? ARRAY_CLOSE : OBJECT_CLOSE; |
||||||
|
|
||||||
|
int closingIndex = filter.indexOfMatchingCloseChar(filter.position(), openChar, closeChar, true, false); |
||||||
|
if (closingIndex == -1) { |
||||||
|
throw new InvalidPathException("String not closed. Expected " + TICK + " in " + filter); |
||||||
|
} else { |
||||||
|
filter.setPosition(closingIndex + 1); |
||||||
|
} |
||||||
|
CharSequence json = filter.subSequence(begin, filter.position()); |
||||||
|
logger.trace("JsonLiteral from {} to {} -> [{}]", begin, filter.position(), json); |
||||||
|
return ValueNode.createJsonNode(json); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode.PatternNode readPattern() { |
||||||
|
int begin = filter.position(); |
||||||
|
int closingIndex = filter.nextIndexOfUnescaped(PATTERN); |
||||||
|
if (closingIndex == -1) { |
||||||
|
throw new InvalidPathException("Pattern not closed. Expected " + PATTERN + " in " + filter); |
||||||
|
} else { |
||||||
|
if(filter.inBounds(closingIndex+1) && filter.charAt(closingIndex+1) == 'i'){ |
||||||
|
closingIndex++; |
||||||
|
} |
||||||
|
filter.setPosition(closingIndex + 1); |
||||||
|
} |
||||||
|
CharSequence pattern = filter.subSequence(begin, filter.position()); |
||||||
|
logger.trace("PatternNode from {} to {} -> [{}]", begin, filter.position(), pattern); |
||||||
|
return ValueNode.createPatternNode(pattern); |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode.StringNode readStringLiteral() { |
||||||
|
int begin = filter.position(); |
||||||
|
|
||||||
|
int closingTickIndex = filter.nextIndexOfUnescaped(TICK); |
||||||
|
if (closingTickIndex == -1) { |
||||||
|
throw new InvalidPathException("String not closed. Expected " + TICK + " in " + filter); |
||||||
|
} else { |
||||||
|
filter.setPosition(closingTickIndex + 1); |
||||||
|
} |
||||||
|
CharSequence stringLiteral = filter.subSequence(begin, filter.position()); |
||||||
|
logger.trace("StringLiteral from {} to {} -> [{}]", begin, filter.position(), stringLiteral); |
||||||
|
return ValueNode.createStringNode(stringLiteral, true); |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode.NumberNode readNumberLiteral() { |
||||||
|
int begin = filter.position(); |
||||||
|
|
||||||
|
while (filter.inBounds() && filter.isNumberCharacter(filter.position())) { |
||||||
|
filter.incrementPosition(1); |
||||||
|
} |
||||||
|
CharSequence numberLiteral = filter.subSequence(begin, filter.position()); |
||||||
|
logger.trace("NumberLiteral from {} to {} -> [{}]", begin, filter.position(), numberLiteral); |
||||||
|
return ValueNode.createNumberNode(numberLiteral); |
||||||
|
} |
||||||
|
|
||||||
|
private ValueNode.BooleanNode readBooleanLiteral() { |
||||||
|
int begin = filter.position(); |
||||||
|
int end = filter.currentChar() == 't' ? filter.position() + 3 : filter.position() + 4; |
||||||
|
|
||||||
|
if(!filter.inBounds(end)){ |
||||||
|
throw new InvalidPathException("Expected boolean literal"); |
||||||
|
} |
||||||
|
CharSequence boolValue = filter.subSequence(begin, end+1); |
||||||
|
if(!boolValue.equals("true") && !boolValue.equals("false")){ |
||||||
|
throw new InvalidPathException("Expected boolean literal"); |
||||||
|
} |
||||||
|
filter.incrementPosition(boolValue.length()); |
||||||
|
logger.trace("BooleanLiteral from {} to {} -> [{}]", begin, end, boolValue); |
||||||
|
|
||||||
|
return ValueNode.createBooleanNode(boolValue); |
||||||
|
} |
||||||
|
|
||||||
|
private PathNode readPath() { |
||||||
|
char previousSignificantChar = filter.previousSignificantChar(); |
||||||
|
boolean operatorOnLeft = isRelationalOperatorChar(previousSignificantChar) && previousSignificantChar != BANG; |
||||||
|
int begin = filter.position(); |
||||||
|
|
||||||
|
filter.incrementPosition(1); //skip $ and @
|
||||||
|
while (filter.inBounds()) { |
||||||
|
if (filter.currentChar() == OPEN_SQUARE_BRACKET) { |
||||||
|
int closingSquareBracketIndex = filter.indexOfClosingSquareBracket(filter.position()); |
||||||
|
if (closingSquareBracketIndex == -1) { |
||||||
|
throw new InvalidPathException("Square brackets does not match in filter " + filter); |
||||||
|
} else { |
||||||
|
filter.setPosition(closingSquareBracketIndex + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
boolean closingLogicalBracket = (filter.currentChar() == CLOSE_BRACKET && !currentCharIsClosingFunctionBracket(begin)); |
||||||
|
if (!filter.inBounds() || isRelationalOperatorChar(filter.currentChar()) || filter.currentChar() == SPACE || closingLogicalBracket) { |
||||||
|
break; |
||||||
|
} else { |
||||||
|
filter.incrementPosition(1); |
||||||
|
} |
||||||
|
} |
||||||
|
boolean operatorOnRight = isRelationalOperatorChar(filter.currentChar()) || isRelationalOperatorChar(filter.nextSignificantChar()); |
||||||
|
boolean existsCheck = !operatorOnLeft && !operatorOnRight; |
||||||
|
boolean shouldExists = true; |
||||||
|
if(existsCheck){ |
||||||
|
shouldExists = !(previousSignificantChar == BANG); |
||||||
|
} |
||||||
|
CharSequence path = filter.subSequence(begin, filter.position()); |
||||||
|
return new PathNode(path, existsCheck, shouldExists); |
||||||
|
} |
||||||
|
|
||||||
|
private boolean currentCharIsClosingFunctionBracket(int lowerBound){ |
||||||
|
if(filter.currentChar() != CLOSE_BRACKET){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
int idx = filter.indexOfPreviousSignificantChar(); |
||||||
|
if(idx == -1 || filter.charAt(idx) != OPEN_BRACKET){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
idx--; |
||||||
|
while(filter.inBounds(idx) && idx > lowerBound){ |
||||||
|
if(filter.charAt(idx) == FUNCTION){ |
||||||
|
return true; |
||||||
|
} |
||||||
|
idx--; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isRelationalOperatorChar(char c) { |
||||||
|
return c == OPERATOR_PIPE || c == LT || c == GT || c == EQ || c == TILDE || c == BANG; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
|
||||||
|
public class FunctionNode extends ValueNode { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Void.class; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.internal.Utils; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class LogicalExpressionNode extends ExpressionNode { |
||||||
|
protected List<ExpressionNode> chain = new ArrayList<ExpressionNode>(); |
||||||
|
private final LogicalOperator operator; |
||||||
|
|
||||||
|
public static LogicalExpressionNode createLogicalOr(ExpressionNode left,ExpressionNode right){ |
||||||
|
return new LogicalExpressionNode(left, LogicalOperator.OR, right); |
||||||
|
} |
||||||
|
|
||||||
|
public static LogicalExpressionNode createLogicalAnd(ExpressionNode left,ExpressionNode right){ |
||||||
|
return new LogicalExpressionNode(left, LogicalOperator.AND, right); |
||||||
|
} |
||||||
|
|
||||||
|
private LogicalExpressionNode(ExpressionNode left, LogicalOperator operator, ExpressionNode right) { |
||||||
|
chain.add(left); |
||||||
|
chain.add(right); |
||||||
|
this.operator = operator; |
||||||
|
} |
||||||
|
|
||||||
|
public LogicalExpressionNode and(LogicalExpressionNode other){ |
||||||
|
return createLogicalAnd(this, other); |
||||||
|
} |
||||||
|
|
||||||
|
public LogicalExpressionNode or(LogicalExpressionNode other){ |
||||||
|
return createLogicalOr(this, other); |
||||||
|
} |
||||||
|
|
||||||
|
public LogicalOperator getOperator() { |
||||||
|
return operator; |
||||||
|
} |
||||||
|
|
||||||
|
public LogicalExpressionNode append(ExpressionNode expressionNode) { |
||||||
|
chain.add(expressionNode); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
//return "(" + Utils.join(" " + operator.getOperatorString() + " ", Utils.reverse(chain)) + ")";
|
||||||
|
return "(" + Utils.join(" " + operator.getOperatorString() + " ", chain) + ")"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean apply(PredicateContext ctx) { |
||||||
|
if(operator == LogicalOperator.OR){ |
||||||
|
for (ExpressionNode expression : chain) { |
||||||
|
if(expression.apply(ctx)){ |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
for (ExpressionNode expression : chain) { |
||||||
|
if(!expression.apply(ctx)){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.InvalidPathException; |
||||||
|
|
||||||
|
public enum LogicalOperator { |
||||||
|
|
||||||
|
AND("&&"), |
||||||
|
OR("||"); |
||||||
|
|
||||||
|
private final String operatorString; |
||||||
|
|
||||||
|
LogicalOperator(String operatorString) { |
||||||
|
this.operatorString = operatorString; |
||||||
|
} |
||||||
|
|
||||||
|
public String getOperatorString() { |
||||||
|
return operatorString; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return operatorString; |
||||||
|
} |
||||||
|
|
||||||
|
public static LogicalOperator fromString(String operatorString){ |
||||||
|
if(AND.operatorString.equals(operatorString)) return AND; |
||||||
|
else if(OR.operatorString.equals(operatorString)) return OR; |
||||||
|
else throw new InvalidPathException("Failed to parse operator " + operatorString); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,111 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.Configuration; |
||||||
|
import com.jayway.jsonpath.JsonPathException; |
||||||
|
import com.jayway.jsonpath.Option; |
||||||
|
import com.jayway.jsonpath.PathNotFoundException; |
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
import com.jayway.jsonpath.internal.Path; |
||||||
|
import com.jayway.jsonpath.internal.PathCompiler; |
||||||
|
import com.jayway.jsonpath.internal.token.PredicateContextImpl; |
||||||
|
import com.jayway.jsonpath.spi.json.JsonProvider; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.math.BigDecimal; |
||||||
|
|
||||||
|
public class PathNode extends ValueNode { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(PathNode.class); |
||||||
|
|
||||||
|
private final Path path; |
||||||
|
private final boolean existsCheck; |
||||||
|
private final boolean shouldExist; |
||||||
|
|
||||||
|
public PathNode(Path path) { |
||||||
|
this(path, false, false); |
||||||
|
} |
||||||
|
|
||||||
|
public PathNode(CharSequence charSequence, boolean existsCheck, boolean shouldExist) { |
||||||
|
this(PathCompiler.compile(charSequence.toString()), existsCheck, shouldExist); |
||||||
|
} |
||||||
|
|
||||||
|
public PathNode(Path path, boolean existsCheck, boolean shouldExist) { |
||||||
|
this.path = path; |
||||||
|
this.existsCheck = existsCheck; |
||||||
|
this.shouldExist = shouldExist; |
||||||
|
logger.trace("PathNode {} existsCheck: {}", path, existsCheck); |
||||||
|
} |
||||||
|
|
||||||
|
public Path getPath() { |
||||||
|
return path; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isExistsCheck() { |
||||||
|
return existsCheck; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean shouldExists() { |
||||||
|
return shouldExist; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Void.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPathNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public PathNode asPathNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public PathNode asExistsCheck(boolean shouldExist) { |
||||||
|
return new PathNode(path, true, shouldExist); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return path.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public ValueNode evaluate(Predicate.PredicateContext ctx) { |
||||||
|
Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build(); |
||||||
|
if (isExistsCheck()) { |
||||||
|
try { |
||||||
|
Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false); |
||||||
|
return result == JsonProvider.UNDEFINED ? ValueNode.FALSE : ValueNode.TRUE; |
||||||
|
} catch (PathNotFoundException e) { |
||||||
|
return ValueNode.FALSE; |
||||||
|
} |
||||||
|
} else { |
||||||
|
try { |
||||||
|
Object res; |
||||||
|
if (ctx instanceof PredicateContextImpl) { |
||||||
|
//This will use cache for document ($) queries
|
||||||
|
PredicateContextImpl ctxi = (PredicateContextImpl) ctx; |
||||||
|
res = ctxi.evaluate(path); |
||||||
|
} else { |
||||||
|
Object doc = path.isRootPath() ? ctx.root() : ctx.item(); |
||||||
|
res = path.evaluate(doc, ctx.root(), ctx.configuration()).getValue(); |
||||||
|
} |
||||||
|
res = ctx.configuration().jsonProvider().unwrap(res); |
||||||
|
|
||||||
|
if (res instanceof Number) return ValueNode.createNumberNode(res.toString()); |
||||||
|
else if (res instanceof BigDecimal) return ValueNode.createNumberNode(res.toString()); |
||||||
|
else if (res instanceof String) return ValueNode.createStringNode(res.toString(), false); |
||||||
|
else if (res instanceof Boolean) return ValueNode.createBooleanNode(res.toString()); |
||||||
|
else if (res == null) return ValueNode.NULL_NODE; |
||||||
|
else if (ctx.configuration().jsonProvider().isArray(res)) return ValueNode.createJsonNode(res); |
||||||
|
else if (ctx.configuration().jsonProvider().isMap(res)) return ValueNode.createJsonNode(res); |
||||||
|
else throw new JsonPathException("Could not convert " + res.toString() + " to a ValueNode"); |
||||||
|
} catch (PathNotFoundException e) { |
||||||
|
return ValueNode.UNDEFINED; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
public class RelationalExpressionNode extends ExpressionNode { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(RelationalExpressionNode.class); |
||||||
|
|
||||||
|
private final ValueNode left; |
||||||
|
private final RelationalOperator relationalOperator; |
||||||
|
private final ValueNode right; |
||||||
|
|
||||||
|
public RelationalExpressionNode(ValueNode left, RelationalOperator relationalOperator, ValueNode right) { |
||||||
|
this.left = left; |
||||||
|
this.relationalOperator = relationalOperator; |
||||||
|
this.right = right; |
||||||
|
|
||||||
|
logger.trace("ExpressionNode {}", toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
if(relationalOperator == RelationalOperator.EXISTS){ |
||||||
|
return left.toString(); |
||||||
|
} else { |
||||||
|
return left.toString() + " " + relationalOperator.toString() + " " + right.toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean apply(PredicateContext ctx) { |
||||||
|
ValueNode l = left; |
||||||
|
ValueNode r = right; |
||||||
|
|
||||||
|
if(left.isPathNode()){ |
||||||
|
l = left.asPathNode().evaluate(ctx); |
||||||
|
} |
||||||
|
if(right.isPathNode()){ |
||||||
|
r = right.asPathNode().evaluate(ctx); |
||||||
|
} |
||||||
|
Evaluator evaluator = EvaluatorFactory.createEvaluator(relationalOperator); |
||||||
|
if(evaluator != null){ |
||||||
|
return evaluator.evaluate(l, r, ctx); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.InvalidPathException; |
||||||
|
|
||||||
|
public enum RelationalOperator { |
||||||
|
|
||||||
|
GTE(">="), |
||||||
|
LTE("<="), |
||||||
|
EQ("=="), |
||||||
|
NE("!="), |
||||||
|
LT("<"), |
||||||
|
GT(">"), |
||||||
|
REGEX("=~"), |
||||||
|
NIN("¦NIN¦"), |
||||||
|
IN("¦IN¦"), |
||||||
|
CONTAINS("¦CONTAINS¦"), |
||||||
|
ALL("¦ALL¦"), |
||||||
|
SIZE("¦SIZE¦"), |
||||||
|
EXISTS("¦EXISTS¦"), |
||||||
|
TYPE("¦TYPE¦"), |
||||||
|
MATCHES("¦MATCHES¦"), |
||||||
|
NOT_EMPTY("¦NOT_EMPTY¦"); |
||||||
|
|
||||||
|
private final String operatorString; |
||||||
|
|
||||||
|
RelationalOperator(String operatorString) { |
||||||
|
this.operatorString = operatorString; |
||||||
|
} |
||||||
|
|
||||||
|
public String getOperatorString() { |
||||||
|
return operatorString; |
||||||
|
} |
||||||
|
|
||||||
|
public static RelationalOperator fromString(String operatorString){ |
||||||
|
for (RelationalOperator operator : RelationalOperator.values()) { |
||||||
|
if(operator.operatorString.equals(operatorString) ){ |
||||||
|
return operator; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new InvalidPathException("Operator not supported " + operatorString); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return operatorString; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,700 @@ |
|||||||
|
package com.jayway.jsonpath.internal.filter; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.Configuration; |
||||||
|
import com.jayway.jsonpath.InvalidPathException; |
||||||
|
import com.jayway.jsonpath.JsonPathException; |
||||||
|
import com.jayway.jsonpath.Predicate; |
||||||
|
import com.jayway.jsonpath.internal.PathCompiler; |
||||||
|
import com.jayway.jsonpath.internal.Utils; |
||||||
|
|
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
public abstract class ValueNode { |
||||||
|
|
||||||
|
public static final NullNode NULL_NODE = new NullNode(); |
||||||
|
public static final BooleanNode TRUE = new BooleanNode("true"); |
||||||
|
public static final BooleanNode FALSE = new BooleanNode("false"); |
||||||
|
public static final UndefinedNode UNDEFINED = new UndefinedNode(); |
||||||
|
|
||||||
|
|
||||||
|
public abstract Class<?> type(Predicate.PredicateContext ctx); |
||||||
|
|
||||||
|
public boolean isPatternNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public PatternNode asPatternNode() { |
||||||
|
throw new InvalidPathException("Expected regexp node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPathNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public PathNode asPathNode() { |
||||||
|
throw new InvalidPathException("Expected path node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isNumberNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public NumberNode asNumberNode() { |
||||||
|
throw new InvalidPathException("Expected number node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStringNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public StringNode asStringNode() { |
||||||
|
throw new InvalidPathException("Expected string node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isBooleanNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public BooleanNode asBooleanNode() { |
||||||
|
throw new InvalidPathException("Expected boolean node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isJsonNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public JsonNode asJsonNode() { |
||||||
|
throw new InvalidPathException("Expected json node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPredicateNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public PredicateNode asPredicateNode() { |
||||||
|
throw new InvalidPathException("Expected predicate node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isValueListNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public ValueListNode asValueListNode() { |
||||||
|
throw new InvalidPathException("Expected value list node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isNullNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public NullNode asNullNode() { |
||||||
|
throw new InvalidPathException("Expected null node"); |
||||||
|
} |
||||||
|
|
||||||
|
public UndefinedNode asUndefinedNode() { |
||||||
|
throw new InvalidPathException("Expected undefined node"); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isUndefinedNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isClassNode() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassNode asClassNode() { |
||||||
|
throw new InvalidPathException("Expected class node"); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isPath(Object o) { |
||||||
|
if(o == null || !(o instanceof String)){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
String str = o.toString().trim(); |
||||||
|
if (str.length() <= 0) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
char c0 = str.charAt(0); |
||||||
|
if(c0 == '@' || c0 == '$'){ |
||||||
|
try { |
||||||
|
PathCompiler.compile(str); |
||||||
|
return true; |
||||||
|
} catch(Exception e){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isJson(Object o) { |
||||||
|
if(o == null || !(o instanceof String)){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
String str = o.toString().trim(); |
||||||
|
if (str.length() <= 1) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
char c0 = str.charAt(0); |
||||||
|
char c1 = str.charAt(str.length() - 1); |
||||||
|
if ((c0 == '[' && c1 == ']') || (c0 == '{' && c1 == '}')){ |
||||||
|
try { |
||||||
|
Configuration.defaultConfiguration().jsonProvider().parse(str); |
||||||
|
return false; |
||||||
|
} catch(Exception e){ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------
|
||||||
|
//
|
||||||
|
// Factory methods
|
||||||
|
//
|
||||||
|
//----------------------------------------------------
|
||||||
|
public static ValueNode toValueNode(Object o){ |
||||||
|
if(o == null) return ValueNode.NULL_NODE; |
||||||
|
if(o instanceof ValueNode) return (ValueNode)o; |
||||||
|
if(o instanceof Class) return createClassNode((Class)o); |
||||||
|
else if(isPath(o)) return new PathNode(o.toString(), false, false); |
||||||
|
else if(isJson(o)) return createStringNode(o.toString(), false); |
||||||
|
else if(o instanceof String) return createStringNode(o.toString(), false); |
||||||
|
else if(o instanceof Character) return createStringNode(o.toString(), false); |
||||||
|
else if(o instanceof Number) return createNumberNode(o.toString()); |
||||||
|
else if(o instanceof Boolean) return createBooleanNode(o.toString()); |
||||||
|
else if(o instanceof Pattern) return createPatternNode((Pattern)o); |
||||||
|
else throw new JsonPathException("Could not determine value type"); |
||||||
|
} |
||||||
|
|
||||||
|
public static StringNode createStringNode(CharSequence charSequence, boolean escape){ |
||||||
|
return new StringNode(charSequence, escape); |
||||||
|
} |
||||||
|
|
||||||
|
public static ClassNode createClassNode(Class<?> clazz){ |
||||||
|
return new ClassNode(clazz); |
||||||
|
} |
||||||
|
|
||||||
|
public static NumberNode createNumberNode(CharSequence charSequence){ |
||||||
|
return new NumberNode(charSequence); |
||||||
|
} |
||||||
|
|
||||||
|
public static BooleanNode createBooleanNode(CharSequence charSequence){ |
||||||
|
return Boolean.parseBoolean(charSequence.toString()) ? TRUE : FALSE; |
||||||
|
} |
||||||
|
|
||||||
|
public static NullNode createNullNode(){ |
||||||
|
return NULL_NODE; |
||||||
|
} |
||||||
|
|
||||||
|
public static JsonNode createJsonNode(CharSequence json) { |
||||||
|
return new JsonNode(json); |
||||||
|
} |
||||||
|
|
||||||
|
public static JsonNode createJsonNode(Object parsedJson) { |
||||||
|
return new JsonNode(parsedJson); |
||||||
|
} |
||||||
|
|
||||||
|
public static PatternNode createPatternNode(CharSequence pattern) { |
||||||
|
return new PatternNode(pattern); |
||||||
|
} |
||||||
|
|
||||||
|
public static PatternNode createPatternNode(Pattern pattern) { |
||||||
|
return new PatternNode(pattern); |
||||||
|
} |
||||||
|
|
||||||
|
public static UndefinedNode createUndefinedNode() { |
||||||
|
return UNDEFINED; |
||||||
|
} |
||||||
|
|
||||||
|
//----------------------------------------------------
|
||||||
|
//
|
||||||
|
// ValueNode Implementations
|
||||||
|
//
|
||||||
|
//----------------------------------------------------
|
||||||
|
public static class PatternNode extends ValueNode { |
||||||
|
private final String pattern; |
||||||
|
private final Pattern compiledPattern; |
||||||
|
|
||||||
|
private PatternNode(CharSequence charSequence) { |
||||||
|
String tmp = charSequence.toString(); |
||||||
|
int begin = tmp.indexOf('/'); |
||||||
|
int end = tmp.lastIndexOf('/'); |
||||||
|
int flags = tmp.endsWith("/i") ? Pattern.CASE_INSENSITIVE : 0; |
||||||
|
this.pattern = tmp.substring(begin + 1, end); |
||||||
|
this.compiledPattern = Pattern.compile(pattern, flags); |
||||||
|
} |
||||||
|
|
||||||
|
public PatternNode(Pattern pattern) { |
||||||
|
this.pattern = pattern.pattern(); |
||||||
|
this.compiledPattern = pattern; |
||||||
|
} |
||||||
|
|
||||||
|
public String getPattern() { |
||||||
|
return "/" + pattern + "/"; |
||||||
|
} |
||||||
|
|
||||||
|
public Pattern getCompiledPattern() { |
||||||
|
return compiledPattern; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Void.TYPE; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPatternNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public PatternNode asPatternNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return pattern; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof PatternNode)) return false; |
||||||
|
|
||||||
|
PatternNode that = (PatternNode) o; |
||||||
|
|
||||||
|
return !(compiledPattern != null ? !compiledPattern.equals(that.compiledPattern) : that.compiledPattern != null); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class JsonNode extends ValueNode { |
||||||
|
private final Object json; |
||||||
|
private final boolean parsed; |
||||||
|
|
||||||
|
private JsonNode(CharSequence charSequence) { |
||||||
|
json = charSequence.toString(); |
||||||
|
parsed = false; |
||||||
|
} |
||||||
|
|
||||||
|
public JsonNode(Object parsedJson) { |
||||||
|
json = parsedJson; |
||||||
|
parsed = true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
if(ctx.configuration().jsonProvider().isArray(parse(ctx))) return List.class; |
||||||
|
else if(ctx.configuration().jsonProvider().isMap(parse(ctx))) return Map.class; |
||||||
|
else if(ctx.configuration().jsonProvider().unwrap(parse(ctx)) instanceof Number) return Number.class; |
||||||
|
else if(ctx.configuration().jsonProvider().unwrap(parse(ctx)) instanceof String) return String.class; |
||||||
|
else if(ctx.configuration().jsonProvider().unwrap(parse(ctx)) instanceof Boolean) return Boolean.class; |
||||||
|
else return Void.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isJsonNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public JsonNode asJsonNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public ValueNode asValueListNode(Predicate.PredicateContext ctx){ |
||||||
|
if(!isArray(ctx)){ |
||||||
|
return UNDEFINED; |
||||||
|
} else { |
||||||
|
Collection nodes = new ArrayList(); |
||||||
|
for (Object value : ctx.configuration().jsonProvider().toIterable(parse(ctx))) { |
||||||
|
nodes.add(value); |
||||||
|
} |
||||||
|
return new ValueListNode(nodes); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Object parse(Predicate.PredicateContext ctx){ |
||||||
|
return parsed ? json : ctx.configuration().jsonProvider().parse(json.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isParsed() { |
||||||
|
return parsed; |
||||||
|
} |
||||||
|
|
||||||
|
public Object getJson() { |
||||||
|
return json; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isArray(Predicate.PredicateContext ctx) { |
||||||
|
return ctx.configuration().jsonProvider().isArray(parse(ctx)); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isMap(Predicate.PredicateContext ctx) { |
||||||
|
return ctx.configuration().jsonProvider().isArray(parse(ctx)); |
||||||
|
} |
||||||
|
|
||||||
|
public int length(Predicate.PredicateContext ctx) { |
||||||
|
return isArray(ctx) ? ctx.configuration().jsonProvider().length(parse(ctx)) : -1; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isEmpty(Predicate.PredicateContext ctx) { |
||||||
|
if(isArray(ctx) || isMap(ctx)) return ctx.configuration().jsonProvider().length(parse(ctx)) == 0; |
||||||
|
else if((parse(ctx) instanceof String)) return ((String)parse(ctx)).length() == 0; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return json.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(JsonNode jsonNode, Predicate.PredicateContext ctx) { |
||||||
|
if (this == jsonNode) return true; |
||||||
|
return !(json != null ? !json.equals(jsonNode.parse(ctx)) : jsonNode.json != null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof JsonNode)) return false; |
||||||
|
|
||||||
|
JsonNode jsonNode = (JsonNode) o; |
||||||
|
|
||||||
|
return !(json != null ? !json.equals(jsonNode.json) : jsonNode.json != null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class StringNode extends ValueNode { |
||||||
|
private final String string; |
||||||
|
|
||||||
|
private StringNode(CharSequence charSequence, boolean escape) { |
||||||
|
if(charSequence.length() > 1){ |
||||||
|
if(charSequence.charAt(0) == '\'' && charSequence.charAt(charSequence.length()-1) == '\''){ |
||||||
|
charSequence = charSequence.subSequence(1, charSequence.length()-1); |
||||||
|
} |
||||||
|
} |
||||||
|
string = escape ? Utils.unescape(charSequence.toString()) : charSequence.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public String getString() { |
||||||
|
return string; |
||||||
|
} |
||||||
|
|
||||||
|
public int length(){ |
||||||
|
return getString().length(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isEmpty(){ |
||||||
|
return getString().isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean contains(String str) { |
||||||
|
return getString().contains(str); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return String.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStringNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public StringNode asStringNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "'" + string + "'"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof StringNode)) return false; |
||||||
|
|
||||||
|
StringNode that = (StringNode) o; |
||||||
|
|
||||||
|
return !(string != null ? !string.equals(that.string) : that.string != null); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class NumberNode extends ValueNode { |
||||||
|
private final BigDecimal number; |
||||||
|
|
||||||
|
private NumberNode(CharSequence num) { |
||||||
|
number = new BigDecimal(num.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
public BigDecimal getNumber() { |
||||||
|
return number; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Number.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isNumberNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public NumberNode asNumberNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return number.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof NumberNode)) return false; |
||||||
|
|
||||||
|
ValueNode that = (ValueNode) o; |
||||||
|
|
||||||
|
if(!that.isNumberNode()){ |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
return number.compareTo(that.asNumberNode().number) == 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class BooleanNode extends ValueNode { |
||||||
|
private final Boolean value; |
||||||
|
|
||||||
|
private BooleanNode(CharSequence boolValue) { |
||||||
|
value = Boolean.parseBoolean(boolValue.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Boolean.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isBooleanNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public BooleanNode asBooleanNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean getBoolean() { |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return value.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof BooleanNode)) return false; |
||||||
|
|
||||||
|
BooleanNode that = (BooleanNode) o; |
||||||
|
|
||||||
|
return !(value != null ? !value.equals(that.value) : that.value != null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class ClassNode extends ValueNode { |
||||||
|
private final Class clazz; |
||||||
|
|
||||||
|
private ClassNode(Class clazz) { |
||||||
|
this.clazz = clazz; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Class.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isClassNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassNode asClassNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Class getClazz() { |
||||||
|
return clazz; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return clazz.getName(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof ClassNode)) return false; |
||||||
|
|
||||||
|
ClassNode that = (ClassNode) o; |
||||||
|
|
||||||
|
return !(clazz != null ? !clazz.equals(that.clazz) : that.clazz != null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class NullNode extends ValueNode { |
||||||
|
|
||||||
|
private NullNode() {} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Void.class; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isNullNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public NullNode asNullNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "null"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof NullNode)) return false; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class UndefinedNode extends ValueNode { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Void.class; |
||||||
|
} |
||||||
|
|
||||||
|
public UndefinedNode asUndefinedNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isUndefinedNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class PredicateNode extends ValueNode { |
||||||
|
|
||||||
|
private final Predicate predicate; |
||||||
|
|
||||||
|
public PredicateNode(Predicate predicate) { |
||||||
|
this.predicate = predicate; |
||||||
|
} |
||||||
|
|
||||||
|
public Predicate getPredicate() { |
||||||
|
return predicate; |
||||||
|
} |
||||||
|
|
||||||
|
public PredicateNode asPredicateNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return Void.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isPredicateNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return predicate.toString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class ValueListNode extends ValueNode implements Iterable<ValueNode> { |
||||||
|
|
||||||
|
private List<ValueNode> nodes = new ArrayList<ValueNode>(); |
||||||
|
|
||||||
|
public ValueListNode(Collection<?> values) { |
||||||
|
for (Object value : values) { |
||||||
|
nodes.add(toValueNode(value)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean contains(ValueNode node){ |
||||||
|
return nodes.contains(node); |
||||||
|
} |
||||||
|
|
||||||
|
public List<ValueNode> getNodes() { |
||||||
|
return Collections.unmodifiableList(nodes); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Class<?> type(Predicate.PredicateContext ctx) { |
||||||
|
return List.class; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isValueListNode() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public ValueListNode asValueListNode() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "[" + Utils.join(",", nodes) + "]"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean equals(Object o) { |
||||||
|
if (this == o) return true; |
||||||
|
if (!(o instanceof ValueListNode)) return false; |
||||||
|
|
||||||
|
ValueListNode that = (ValueListNode) o; |
||||||
|
|
||||||
|
return !(that != null ? !nodes.equals(that.nodes) : that.nodes != null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Iterator<ValueNode> iterator() { |
||||||
|
return nodes.iterator(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
package com.jayway.jsonpath; |
||||||
|
|
||||||
|
import com.jayway.jsonpath.internal.filter.FilterCompiler; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
public class FilterCompilerTest { |
||||||
|
|
||||||
|
|
||||||
|
@Test |
||||||
|
public void filter_compiler_test() { |
||||||
|
/* |
||||||
|
FilterCompiler.compile("[?(@)]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?($)]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?(@.firstname)]"); |
||||||
|
FilterCompiler.compile("[?(@.firstname)]"); |
||||||
|
FilterCompiler.compile("[?($.firstname)]"); |
||||||
|
FilterCompiler.compile("[?(@['firstname'])]"); |
||||||
|
|
||||||
|
|
||||||
|
FilterCompiler.compile("[?($['firstname'].lastname)]"); |
||||||
|
FilterCompiler.compile("[?($['firstname']['lastname'])]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?($['firstname']['lastname'].*)]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?($['firstname']['num_eq'] == 1)]"); |
||||||
|
FilterCompiler.compile("[?($['firstname']['num_gt'] > 1.1)]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?($['firstname']['num_lt'] < 11.11)]"); |
||||||
|
FilterCompiler.compile("[?($['firstname']['num_in'] ¦IN¦ 0.1)]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?($['firstname']['str_eq'] == 'hej')]"); |
||||||
|
FilterCompiler.compile("[?($['firstname']['str_eq'] == '')]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?($['firstname']['str_eq'] == null)]"); |
||||||
|
FilterCompiler.compile("[?($['firstname']['str_eq'] == true)]"); |
||||||
|
FilterCompiler.compile("[?($['firstname']['str_eq'] == false)]"); |
||||||
|
|
||||||
|
|
||||||
|
FilterCompiler.compile("[?(@.firstname && @.lastname)]"); |
||||||
|
FilterCompiler.compile("[?((@.firstname || @.lastname) && @.and)]"); |
||||||
|
|
||||||
|
FilterCompiler.compile("[?((@.a || @.b || @.c) && @.x)]"); |
||||||
|
FilterCompiler.compile("[?((@.a && @.b && @.c) || @.x)]"); |
||||||
|
FilterCompiler.compile("[?((@.a && @.b || @.c) || @.x)]"); |
||||||
|
FilterCompiler.compile("[?((@.a && @.b) || (@.c && @.d))]"); |
||||||
|
|
||||||
|
|
||||||
|
FilterCompiler.compile("[?(@.a ¦IN¦ [1,2,3])]"); |
||||||
|
FilterCompiler.compile("[?(@.a ¦IN¦ {'foo':'bar'})]"); |
||||||
|
FilterCompiler.compile("[?(@.value<'7')]"); |
||||||
|
*/ |
||||||
|
|
||||||
|
//FilterCompiler.compile("[?(@.message == 'it\\'s here')]");
|
||||||
|
FilterCompiler.compile("[?(@.message == 'it\\\\')]"); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -1,3 +1,5 @@ |
|||||||
org.slf4j.simpleLogger.log.com.jayway=debug |
org.slf4j.simpleLogger.log.com.jayway=debug |
||||||
|
org.slf4j.simpleLogger.log.com.jayway.jsonpath.internal.FilterCompiler=trace |
||||||
|
org.slf4j.simpleLogger.log.com.jayway.jsonpath.internal.filter=trace |
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in new issue