diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java index 322a17198..c4f2013ce 100755 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/EvaluatorFactory.java @@ -2,6 +2,7 @@ import com.jayway.jsonpath.JsonPathException; import com.jayway.jsonpath.Predicate; +import com.jayway.jsonpath.internal.path.PredicateContextImpl; import java.util.HashMap; import java.util.Iterator; @@ -253,23 +254,51 @@ public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateCont 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.isPatternNode()) { - if (right.isValueListNode() || (right.isJsonNode() && right.asJsonNode().isArray(ctx))) { - return matchesAny(left.asPatternNode(), right.asJsonNode().asValueListNode(ctx)); + if(left.isPatternNode() ^ right.isPatternNode()){ + if (left.isPatternNode()) { + if (right.isValueListNode() || (right.isJsonNode() && right.asJsonNode().isArray(ctx))) { + return matchesAny(left.asPatternNode(), right.asJsonNode().asValueListNode(ctx)); + } else { + return matches(left.asPatternNode(), getInput(right)); + } } else { - return matches(left.asPatternNode(), getInput(right)); + if (left.isValueListNode() || (left.isJsonNode() && left.asJsonNode().isArray(ctx))) { + return matchesAny(right.asPatternNode(), left.asJsonNode().asValueListNode(ctx)); + } else { + return matches(right.asPatternNode(), getInput(left)); + } } - } else { - if (left.isValueListNode() || (left.isJsonNode() && left.asJsonNode().isArray(ctx))) { - return matchesAny(right.asPatternNode(), left.asJsonNode().asValueListNode(ctx)); - } else { - return matches(right.asPatternNode(), getInput(left)); + } + + if(!left.isPatternNode() && !right.isPatternNode()){ + String leftStr = getInput(left); + String rightStr = getInput(right); + + if (!leftStr.isEmpty() && !rightStr.isEmpty()) { + try { + Pattern pattern; + + if (ctx instanceof PredicateContextImpl) { + PredicateContextImpl ctxi = (PredicateContextImpl) ctx; + HashMap cache = ctxi.compiledPatternCache(); + + pattern = cache.get(rightStr); + if (pattern == null) { + pattern = Pattern.compile(rightStr); + cache.put(rightStr, pattern); + } + } else { + pattern = Pattern.compile(rightStr); + } + + return pattern.matcher(leftStr).matches(); + } catch (Exception e) { + return false; + } } } + + return false; } private boolean matches(PatternNode patternNode, String inputToMatch) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java index d471b6cec..9f858ca0e 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicateContextImpl.java @@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory; import java.util.HashMap; +import java.util.regex.Pattern; public class PredicateContextImpl implements Predicate.PredicateContext { @@ -32,11 +33,14 @@ public class PredicateContextImpl implements Predicate.PredicateContext { private final Configuration configuration; private final HashMap documentPathCache; + private final HashMap compiledPatternCache; + public PredicateContextImpl(Object contextDocument, Object rootDocument, Configuration configuration, HashMap documentPathCache) { this.contextDocument = contextDocument; this.rootDocument = rootDocument; this.configuration = configuration; this.documentPathCache = documentPathCache; + this.compiledPatternCache = new HashMap(); } public Object evaluate(Path path){ @@ -59,6 +63,10 @@ public HashMap documentPathCache() { return documentPathCache; } + public HashMap compiledPatternCache() { + return compiledPatternCache; + } + @Override public Object item() { return contextDocument; diff --git a/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java index 1249474a6..ef2c92c5d 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/InlineFilterTest.java @@ -175,6 +175,47 @@ public void patterns_match_against_lists() { assertThat(haveRefBooks).containsExactly("First"); } + @Test + public void dynamic_patterns_can_be_evaluated() { + String json = "{\n" + + " \"pattern\": \"business\",\n" + + " \"tags\": [\"business\", \"info\"]\n" + + "}"; + + List result = JsonPath.parse(json).read("$.tags[?(@ =~ $.pattern)]"); + assertThat(result).containsExactly("business"); + } + + @Test + public void dynamic_patterns_use_cache_during_evaluation() { + String json = "{\n" + + " \"pattern\": \"test.*\",\n" + + " \"items\": [\"test1\", \"test2\", \"other\", \"test3\"]\n" + + "}"; + + List result = JsonPath.parse(json).read("$.items[?(@ =~ $.pattern)]"); + assertThat(result).containsExactly("test1", "test2", "test3"); + } + + @Test + public void dynamic_patterns_do_not_share_cache_between_executions() { + String json1 = "{\n" + + " \"pattern\": \"foo\",\n" + + " \"items\": [\"foo\", \"bar\"]\n" + + "}"; + + String json2 = "{\n" + + " \"pattern\": \"bar\",\n" + + " \"items\": [\"foo\", \"bar\"]\n" + + "}"; + + List result1 = JsonPath.parse(json1).read("$.items[?(@ =~ $.pattern)]"); + assertThat(result1).containsExactly("foo"); + + List result2 = JsonPath.parse(json2).read("$.items[?(@ =~ $.pattern)]"); + assertThat(result2).containsExactly("bar"); + } + @Test public void negate_exists_check() { List hasIsbn = JsonPath.parse(JSON_DOCUMENT).read("$.store.book[?(@.isbn)].author");