From 99e05dbac9dbcfbffb5b1aa42b479da6e9f255d8 Mon Sep 17 00:00:00 2001 From: hansthen Date: Mon, 25 Dec 2017 13:32:57 +0100 Subject: [PATCH 01/21] Fix typo in error message in PathCompiler.java Missing ' --- .../java/com/jayway/jsonpath/internal/path/PathCompiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index 0317311e..55bfc7a6 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -117,7 +117,7 @@ public class PathCompiler { path.incrementPosition(1); if(path.currentChar() != PERIOD && path.currentChar() != OPEN_SQUARE_BRACKET){ - fail("Illegal character at position " + path.position() + " expected '.' or '["); + fail("Illegal character at position " + path.position() + " expected '.' or '['"); } PathTokenAppender appender = pathToken.getPathTokenAppender(); From efd1de8ffb84790fb5a47ffc0faeede43a9e84fa Mon Sep 17 00:00:00 2001 From: John Bard Date: Tue, 2 Jan 2018 16:36:59 -0500 Subject: [PATCH 02/21] Add hasNoJsonPath testcase for explicit null --- .../com/jayway/jsonpath/matchers/HasNoJsonPathTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/HasNoJsonPathTest.java b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/HasNoJsonPathTest.java index edd4ca9e..b463742d 100644 --- a/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/HasNoJsonPathTest.java +++ b/json-path-assert/src/test/java/com/jayway/jsonpath/matchers/HasNoJsonPathTest.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertThat; public class HasNoJsonPathTest { private static final String JSON_STRING = "{" + + "\"none\": null," + "\"name\": \"Jessie\"" + "}"; @@ -21,6 +22,11 @@ public class HasNoJsonPathTest { assertThat(JSON_STRING, not(hasNoJsonPath("$.name"))); } + @Test + public void shouldNotMatchExplicitNull() { + assertThat(JSON_STRING, not(hasNoJsonPath("$.none"))); + } + @Test public void shouldBeDescriptive() { assertThat(hasNoJsonPath("$.name"), From 0b324ec7b8a8e4f269083e018930cd7189d96004 Mon Sep 17 00:00:00 2001 From: Bartlomiej Kowalczyk Date: Tue, 23 Jan 2018 22:04:50 +0100 Subject: [PATCH 03/21] Prevent StackOverflow in case of unclosed property --- .../java/com/jayway/jsonpath/internal/path/PathCompiler.java | 4 ++++ .../src/test/java/com/jayway/jsonpath/old/JsonPathTest.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index 0317311e..b27eda61 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -603,6 +603,10 @@ public class PathCompiler { readPosition++; } + if (inProperty){ + fail("Property has not been closed - missing closing " + potentialStringDelimiter); + } + int endBracketIndex = path.indexOfNextSignificantChar(endPosition, CLOSE_SQUARE_BRACKET) + 1; path.setPosition(endBracketIndex); diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java index 60304267..45ff46bf 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java @@ -322,5 +322,10 @@ public class JsonPathTest extends BaseTest { } } + @Test(expected = InvalidPathException.class) + //see https://github.com/json-path/JsonPath/issues/428 + public void prevent_stack_overflow_error_when_unclosed_property() { + JsonPath.compile("$['boo','foo][?(@ =~ /bar/)]"); + } } From f7dc9280c77000b166ab27a52a2b40cab5baab29 Mon Sep 17 00:00:00 2001 From: Sam Kruglov Date: Wed, 28 Feb 2018 19:02:20 +0300 Subject: [PATCH 04/21] Log the nested exception as well why not --- .../java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java index ca60048c..d011a7e2 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/JacksonJsonProvider.java @@ -93,7 +93,7 @@ public class JacksonJsonProvider extends AbstractJsonProvider { generator.close(); return writer.getBuffer().toString(); } catch (IOException e) { - throw new InvalidJsonException(); + throw new InvalidJsonException(e); } } From 57604c86cd3d4d4dadb935a0d755535afa37918c Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Mon, 5 Mar 2018 17:32:58 +0100 Subject: [PATCH 05/21] Fixes #447: To report correct index of invalid jsonpath failure. This should add 1 to the position instead of appending 1 to its text value. Signed-off-by: Claus Ibsen --- .../java/com/jayway/jsonpath/internal/path/PathCompiler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index 0317311e..cc6189b8 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -484,7 +484,8 @@ public class PathCompiler { if (inBracket) { int wildCardIndex = path.indexOfNextSignificantChar(WILDCARD); if (!path.nextSignificantCharIs(wildCardIndex, CLOSE_SQUARE_BRACKET)) { - throw new InvalidPathException("Expected wildcard token to end with ']' on position " + wildCardIndex + 1); + int offset = wildCardIndex + 1; + throw new InvalidPathException("Expected wildcard token to end with ']' on position " + offset); } int bracketCloseIndex = path.indexOfNextSignificantChar(wildCardIndex, CLOSE_SQUARE_BRACKET); path.setPosition(bracketCloseIndex + 1); From 961cc85c34907895cd73255b472da2c4079c4537 Mon Sep 17 00:00:00 2001 From: Wes Gilleland Date: Thu, 8 Mar 2018 23:51:28 -0500 Subject: [PATCH 06/21] Fixed a missing backtick --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68fa76a8..a5522894 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ String json = "{\"date_as_long\" : 1411455611975}"; Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class); ``` -If you configure JsonPath to use `JacksonMappingProvider` or GsonMappingProvider` you can even map your JsonPath output directly into POJO's. +If you configure JsonPath to use `JacksonMappingProvider` or `GsonMappingProvider` you can even map your JsonPath output directly into POJO's. ```java Book book = JsonPath.parse(json).read("$.store.book[0]", Book.class); From 8712cc2b16efebf1fe5d6d769a3ffc67066172ed Mon Sep 17 00:00:00 2001 From: Benedikt Waldvogel Date: Fri, 16 Mar 2018 11:54:27 +0100 Subject: [PATCH 07/21] Update version in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68fa76a8..23dd7e1e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ JsonPath is available at the Central Maven Repository. Maven users add this to y com.jayway.jsonpath json-path - 2.3.0 + 2.4.0 ``` From 445b9c4ebe6d565e8bd78ac8e159760dc9bb8d00 Mon Sep 17 00:00:00 2001 From: Michael Thacker Date: Fri, 6 Apr 2018 17:10:27 -0700 Subject: [PATCH 08/21] Fix compiling of functions with single character arguments --- .../com/jayway/jsonpath/internal/path/PathCompiler.java | 2 +- .../test/java/com/jayway/jsonpath/PathCompilerTest.java | 8 ++++++++ .../jsonpath/internal/function/NestedFunctionTest.java | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index 0317311e..e3e88b9c 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -195,7 +195,7 @@ public class PathCompiler { } else if (c == OPEN_PARENTHESIS) { isFunction = true; - endPosition = readPosition++; + endPosition = readPosition; break; } readPosition++; diff --git a/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java b/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java index 699ba631..a4b6feb5 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java @@ -252,6 +252,14 @@ public class PathCompilerTest { assertThat(result).containsExactly("] it"); } + @Test + public void a_function_can_be_compiled() { + assertThat(compile("$.aaa.foo()").toString()).isEqualTo("$['aaa'].foo()"); + assertThat(compile("$.aaa.foo(5)").toString()).isEqualTo("$['aaa'].foo(...)"); + assertThat(compile("$.aaa.foo($.bar)").toString()).isEqualTo("$['aaa'].foo(...)"); + assertThat(compile("$.aaa.foo(5,10,15)").toString()).isEqualTo("$['aaa'].foo(...)"); + } + @Test(expected = InvalidPathException.class) public void array_indexes_must_be_separated_by_commas() { compile("$[0, 1, 2 4]"); diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/function/NestedFunctionTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/function/NestedFunctionTest.java index 77226b4a..7ab9f972 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/internal/function/NestedFunctionTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/function/NestedFunctionTest.java @@ -58,6 +58,12 @@ public class NestedFunctionTest extends BaseFunctionTest { verifyMathFunction(conf, "$.sum(5, 3, $.numbers.max(), 2)", 20.0); } + @Test + public void testSimpleLiteralArgument() { + verifyMathFunction(conf, "$.sum(5)", 5.0); + verifyMathFunction(conf, "$.sum(50)", 50.0); + } + @Test public void testStringConcat() { verifyTextFunction(conf, "$.text.concat()", "abcdef"); From bdf0512bffa528140ecbb442a24030e1c721822c Mon Sep 17 00:00:00 2001 From: Franklin Yu Date: Sat, 26 May 2018 18:06:16 -0400 Subject: [PATCH 09/21] Use official badge for javadoc --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68fa76a8..f55df1dd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Jayway JsonPath [![Build Status](https://travis-ci.org/json-path/JsonPath.svg?branch=master)](https://travis-ci.org/json-path/JsonPath) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.jayway.jsonpath/json-path/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.jayway.jsonpath/json-path) -[![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.jayway.jsonpath/json-path/badge.svg)](http://www.javadoc.io/doc/com.jayway.jsonpath/json-path) +[![Javadoc](https://www.javadoc.io/badge/com.jayway.jsonpath/json-path.svg)](http://www.javadoc.io/doc/com.jayway.jsonpath/json-path) Jayway JsonPath is a Java port of [Stefan Goessner JsonPath implementation](http://goessner.net/articles/JsonPath/). From ca1b42d07a3e1477c535078263927127ebc3bbb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=95=8Ao=E9=A2=9Diu=E9=B1=BC?= Date: Wed, 30 May 2018 15:29:05 +0800 Subject: [PATCH 10/21] Wrong pass --- .../java/com/jayway/jsonpath/internal/ParseContextImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java index 976f8481..d03790ba 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/ParseContextImpl.java @@ -46,7 +46,7 @@ public class ParseContextImpl implements ParseContext { @Override public DocumentContext parse(InputStream json, String charset) { notNull(json, "json input stream can not be null"); - notNull(json, "charset can not be null"); + notNull(charset, "charset can not be null"); try { Object obj = configuration.jsonProvider().parse(json, charset); return new JsonContext(obj, configuration); From 0f6eaa7901654b54850337a8625fa04f386b235f Mon Sep 17 00:00:00 2001 From: Ben Fradet Date: Thu, 31 May 2018 16:06:20 +0100 Subject: [PATCH 11/21] Do not map non-existing values, fixes #438 --- .../com/jayway/jsonpath/internal/PathRef.java | 4 ++- .../JacksonJsonNodeJsonProviderTest.java | 35 +++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java index 1b42be31..01ecd7c3 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java @@ -298,7 +298,9 @@ public abstract class PathRef implements Comparable { public void convert(MapFunction mapFunction, Configuration configuration) { for (String property : properties) { Object currentValue = configuration.jsonProvider().getMapValue(parent, property); - configuration.jsonProvider().setProperty(parent, property, mapFunction.map(currentValue, configuration)); + if (currentValue != JsonProvider.UNDEFINED) { + configuration.jsonProvider().setProperty(parent, property, mapFunction.map(currentValue, configuration)); + } } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java b/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java index 0267590a..25c80960 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/JacksonJsonNodeJsonProviderTest.java @@ -3,9 +3,13 @@ package com.jayway.jsonpath; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider; +import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider; import com.jayway.jsonpath.spi.mapper.MappingException; import org.junit.Test; @@ -55,7 +59,7 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest { context.put("$", "child", child1); ObjectNode node2 = context.read("$"); ObjectNode child2 = context.read("$.child"); - + assertThat(node1).isSameAs(node2); assertThat(child1).isSameAs(child2); } @@ -112,7 +116,32 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest { using(JACKSON_JSON_NODE_CONFIGURATION).parse(JSON).read("$", typeRef); } - + + @Test + public void mapPropertyWithPOJO() { + String someJson = "" + + "{\n" + + " \"a\": \"a\",\n" + + " \"b\": \"b\"\n" + + "}"; + ObjectMapper om = new ObjectMapper(); + om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + Configuration c = Configuration + .builder() + .mappingProvider(new JacksonMappingProvider()) + .jsonProvider(new JacksonJsonNodeJsonProvider(om)) + .build(); + DocumentContext context = JsonPath.using(c).parse(someJson); + String someJsonStr = context.jsonString(); + DocumentContext altered = context.map("$['a', 'b', 'c']", new MapFunction() { + @Override + public Object map(Object currentValue, Configuration configuration) { + return currentValue; + } + }); + assertThat(altered.jsonString()).isEqualTo(someJsonStr); + } + @Test // https://github.com/json-path/JsonPath/issues/364 public void setPropertyWithPOJO() { @@ -161,7 +190,7 @@ public class JacksonJsonNodeJsonProviderTest extends BaseTest { public static class Gen { public String eric; } - + public static final class Data { @JsonProperty("id") UUID id; From f6336650f204439fbc9d925af42f5629d27dba49 Mon Sep 17 00:00:00 2001 From: Elias Ross Date: Fri, 10 Nov 2017 10:52:30 -0800 Subject: [PATCH 12/21] 2.3 merge update: Also fix for quote evaluation This is a problem in JsonPath where the left-hand side string value was being quoted by mistake. Issue 410 on github: https://github.com/json-path/JsonPath/issues/ Also fix issue 409 as well. This should improve performance a bit when parsing. There are casts because the compiler in IntelliJ was confused as to what overloaded method to call. Casts don't really hurt. --- .../jsonpath/internal/CharacterIndex.java | 9 ++-- .../internal/filter/FilterCompiler.java | 12 ++--- .../jsonpath/internal/filter/ValueNode.java | 11 ++--- .../com/jayway/jsonpath/MultiPropTest.java | 3 +- .../java/com/jayway/jsonpath/OptionsTest.java | 24 +++++----- .../com/jayway/jsonpath/PathCompilerTest.java | 44 +++++++++++++++++++ .../com/jayway/jsonpath/ReturnTypeTest.java | 8 ++-- .../com/jayway/jsonpath/old/IssuesTest.java | 14 +++--- .../com/jayway/jsonpath/old/JsonPathTest.java | 2 +- 9 files changed, 85 insertions(+), 42 deletions(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java b/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java index 1aa28679..378d5c4b 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java @@ -220,16 +220,17 @@ public class CharacterIndex { incrementPosition(1); } - public void readSignificantSubSequence(CharSequence s) { + public boolean hasSignificantSubSequence(CharSequence s) { skipBlanks(); if (! inBounds(position + s.length() - 1)) { - throw new InvalidPathException(String.format("End of string reached while expecting: %s", s)); + return false; } if (! subSequence(position, position + s.length()).equals(s)) { - throw new InvalidPathException(String.format("Expected: %s", s)); + return false; } incrementPosition(s.length()); + return true; } public int indexOfPreviousSignificantChar(int startPosition){ @@ -314,4 +315,4 @@ public class CharacterIndex { skipBlanksAtEnd(); return this; } -} \ No newline at end of file +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java index 67b512e4..f6b9f2d3 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java @@ -132,11 +132,9 @@ public class FilterCompiler { while (true) { int savepoint = filter.position(); - try { - filter.readSignificantSubSequence(LogicalOperator.OR.getOperatorString()); + if (filter.hasSignificantSubSequence(LogicalOperator.OR.getOperatorString())) { ops.add(readLogicalAND()); - } - catch (InvalidPathException exc) { + } else { filter.setPosition(savepoint); break; } @@ -152,11 +150,9 @@ public class FilterCompiler { while (true) { int savepoint = filter.position(); - try { - filter.readSignificantSubSequence(LogicalOperator.AND.getOperatorString()); + if (filter.hasSignificantSubSequence(LogicalOperator.AND.getOperatorString())) { ops.add(readLogicalANDOperand()); - } - catch (InvalidPathException exc) { + } else { filter.setPosition(savepoint); break; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java index d1619c45..23afe785 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java @@ -402,18 +402,19 @@ public abstract class ValueNode { private boolean useSingleQuote = true; private StringNode(CharSequence charSequence, boolean escape) { - if(charSequence.length() > 1){ + if (escape && charSequence.length() > 1) { char open = charSequence.charAt(0); char close = charSequence.charAt(charSequence.length()-1); - - if(open == '\'' && close == '\''){ + if (open == '\'' && close == '\'') { charSequence = charSequence.subSequence(1, charSequence.length()-1); - } else if(open == '"' && close == '"'){ + } else if (open == '"' && close == '"') { charSequence = charSequence.subSequence(1, charSequence.length()-1); useSingleQuote = false; } + string = Utils.unescape(charSequence.toString()); + } else { + string = charSequence.toString(); } - string = escape ? Utils.unescape(charSequence.toString()) : charSequence.toString(); } @Override diff --git a/json-path/src/test/java/com/jayway/jsonpath/MultiPropTest.java b/json-path/src/test/java/com/jayway/jsonpath/MultiPropTest.java index fc5567a8..de5824b9 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/MultiPropTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/MultiPropTest.java @@ -3,6 +3,7 @@ package com.jayway.jsonpath; import org.junit.Test; import java.util.HashMap; +import java.util.List; import java.util.Map; import static com.jayway.jsonpath.JsonPath.using; @@ -114,7 +115,7 @@ public class MultiPropTest { final Configuration conf = Configuration.defaultConfiguration().addOptions(Option.REQUIRE_PROPERTIES); final String json = "{\"a\": {\"v\": 5}, \"b\": {\"v\": 4}, \"c\": {\"v\": 1}}"; - assertThat(using(conf).parse(json).read("$['a', 'c'].v")).asList().containsOnly(5, 1); + assertThat((List)using(conf).parse(json).read("$['a', 'c'].v")).asList().containsOnly(5, 1); assertEvaluationThrows(json, "$['d', 'a', 'c', 'm'].v", PathNotFoundException.class, conf); } } diff --git a/json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java b/json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java index 1093ff7a..873b3b6a 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/OptionsTest.java @@ -21,7 +21,7 @@ public class OptionsTest extends BaseTest { Configuration conf = Configuration.defaultConfiguration(); - assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.baz")).isNull(); + assertThat((String)using(conf).parse("{\"foo\" : \"bar\"}").read("$.baz")).isNull(); } @Test @@ -37,7 +37,7 @@ public class OptionsTest extends BaseTest { Configuration conf = Configuration.defaultConfiguration(); - assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(String.class); + assertThat((String)using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(String.class); } @Test @@ -45,11 +45,11 @@ public class OptionsTest extends BaseTest { Configuration conf = Configuration.builder().options(ALWAYS_RETURN_LIST).build(); - assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(List.class); + assertThat((List)using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isInstanceOf(List.class); - assertThat(using(conf).parse("{\"foo\": null}").read("$.foo")).isInstanceOf(List.class); + assertThat((List)using(conf).parse("{\"foo\": null}").read("$.foo")).isInstanceOf(List.class); - assertThat(using(conf).parse("{\"foo\": [1, 4, 8]}").read("$.foo")).asList() + assertThat((List)using(conf).parse("{\"foo\": [1, 4, 8]}").read("$.foo")).asList() .containsExactly(Arrays.asList(1, 4, 8)); } @@ -61,7 +61,7 @@ public class OptionsTest extends BaseTest { assertThat(result).hasSize(1); assertThat(result.get(0)).isNull(); - assertThat(using(conf).parse("{\"bar\": {\"foo\": [1, 4, 8]}}").read("$..foo")).asList() + assertThat((List)using(conf).parse("{\"bar\": {\"foo\": [1, 4, 8]}}").read("$..foo")).asList() .containsExactly(Arrays.asList(1, 4, 8)); } @@ -69,7 +69,7 @@ public class OptionsTest extends BaseTest { public void a_path_evaluation_is_returned_as_VALUE_by_default() { Configuration conf = Configuration.defaultConfiguration(); - assertThat(using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isEqualTo("bar"); + assertThat((String)using(conf).parse("{\"foo\" : \"bar\"}").read("$.foo")).isEqualTo("bar"); } @Test @@ -142,13 +142,13 @@ public class OptionsTest extends BaseTest { public void issue_suppress_exceptions_does_not_break_indefinite_evaluation() { Configuration conf = Configuration.builder().options(SUPPRESS_EXCEPTIONS).build(); - assertThat(using(conf).parse("{\"foo2\": [5]}").read("$..foo2[0]")).asList().containsOnly(5); - assertThat(using(conf).parse("{\"foo\" : {\"foo2\": [5]}}").read("$..foo2[0]")).asList().containsOnly(5); - assertThat(using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo2[0]")).asList().containsOnly(5); + assertThat((List)using(conf).parse("{\"foo2\": [5]}").read("$..foo2[0]")).asList().containsOnly(5); + assertThat((List)using(conf).parse("{\"foo\" : {\"foo2\": [5]}}").read("$..foo2[0]")).asList().containsOnly(5); + assertThat((List)using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo2[0]")).asList().containsOnly(5); - assertThat(using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo.foo2[0]")).asList().containsOnly(5); + assertThat((List)using(conf).parse("[null, [{\"foo\" : {\"foo2\": [5]}}]]").read("$..foo.foo2[0]")).asList().containsOnly(5); - assertThat(using(conf).parse("{\"aoo\" : {}, \"foo\" : {\"foo2\": [5]}, \"zoo\" : {}}").read("$[*].foo2[0]")).asList().containsOnly(5); + assertThat((List)using(conf).parse("{\"aoo\" : {}, \"foo\" : {\"foo2\": [5]}, \"zoo\" : {}}").read("$[*].foo2[0]")).asList().containsOnly(5); } @Test diff --git a/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java b/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java index 699ba631..e8b7bfe6 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/PathCompilerTest.java @@ -1,5 +1,6 @@ package com.jayway.jsonpath; +import com.jayway.jsonpath.internal.ParseContextImpl; import org.junit.Ignore; import org.junit.Test; @@ -236,6 +237,49 @@ public class PathCompilerTest { assertThat(result).hasSize(1); } + @Test + public void issue_predicate_can_have_double_quotes() { + String json = "{\n" + + " \"logs\": [\n" + + " {\n" + + " \"message\": \"\\\"it\\\"\",\n" + + " }\n" + + " ]\n" + + "}"; + List result = JsonPath.read(json, "$.logs[?(@.message == '\"it\"')].message"); + assertThat(result).containsExactly("\"it\""); + } + + @Test + public void issue_predicate_can_have_single_quotes() { + String json = "{\n" + + " \"logs\": [\n" + + " {\n" + + " \"message\": \"'it'\",\n" + + " }\n" + + " ]\n" + + "}"; + DocumentContext parse = JsonPath.parse(json); + JsonPath compile = JsonPath.compile("$.logs[?(@.message == \"'it'\")].message"); + List result = parse.read(compile); + assertThat(result).containsExactly("'it'"); + } + + @Test + public void issue_predicate_can_have_single_quotes_escaped() { + String json = "{\n" + + " \"logs\": [\n" + + " {\n" + + " \"message\": \"'it'\",\n" + + " }\n" + + " ]\n" + + "}"; + DocumentContext parse = JsonPath.parse(json); + JsonPath compile = JsonPath.compile("$.logs[?(@.message == '\\'it\\'')].message"); + List result = parse.read(compile); + assertThat(result).containsExactly("'it'"); + } + @Test public void issue_predicate_can_have_square_bracket_in_prop() { String json = "{\n" diff --git a/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java b/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java index 144817a3..c9b3fb3b 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/ReturnTypeTest.java @@ -18,7 +18,7 @@ public class ReturnTypeTest extends BaseTest { @Test public void assert_strings_can_be_read() { - assertThat(reader.read("$.string-property")).isEqualTo("string-value"); + assertThat((String)reader.read("$.string-property")).isEqualTo("string-value"); } @Test @@ -28,17 +28,17 @@ public class ReturnTypeTest extends BaseTest { @Test public void assert_longs_can_be_read() { - assertThat(reader.read("$.long-max-property")).isEqualTo(Long.MAX_VALUE); + assertThat((Long)reader.read("$.long-max-property")).isEqualTo(Long.MAX_VALUE); } @Test public void assert_boolean_values_can_be_read() { - assertThat(reader.read("$.boolean-property")).isEqualTo(true); + assertThat((Boolean)reader.read("$.boolean-property")).isEqualTo(true); } @Test public void assert_null_values_can_be_read() { - assertThat(reader.read("$.null-property")).isNull(); + assertThat((String)reader.read("$.null-property")).isNull(); } @Test diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java index d66313bb..9dc4d5dc 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/old/IssuesTest.java @@ -431,7 +431,7 @@ public class IssuesTest extends BaseTest { "]"; - assertEquals(1, read(json, "$[0].a")); + assertEquals(Integer.valueOf(1), read(json, "$[0].a")); } @Test(expected = PathNotFoundException.class) @@ -471,9 +471,9 @@ public class IssuesTest extends BaseTest { String json = "{\"test\":null}"; - assertThat(read(json, "test")).isNull(); + assertThat((String)read(json, "test")).isNull(); - assertThat(JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS)).parse(json).read("nonExistingProperty")).isNull(); + assertThat((String)JsonPath.using(Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS)).parse(json).read("nonExistingProperty")).isNull(); try { read(json, "nonExistingProperty"); @@ -498,7 +498,7 @@ public class IssuesTest extends BaseTest { public void issue_45() { String json = "{\"rootkey\":{\"sub.key\":\"value\"}}"; - assertThat(read(json, "rootkey['sub.key']")).isEqualTo("value"); + assertThat((String)read(json, "rootkey['sub.key']")).isEqualTo("value"); } @Test @@ -508,7 +508,7 @@ public class IssuesTest extends BaseTest { String json = "{\"a\": {}}"; Configuration configuration = Configuration.defaultConfiguration().setOptions(Option.SUPPRESS_EXCEPTIONS); - assertThat(JsonPath.using(configuration).parse(json).read("a.x")).isNull(); + assertThat((String)JsonPath.using(configuration).parse(json).read("a.x")).isNull(); try { read(json, "a.x"); @@ -1018,8 +1018,8 @@ public class IssuesTest extends BaseTest { DocumentContext doc = JsonPath.parse(json).set("$.jsonArr[1].name", "Jayway"); - assertThat(doc.read("$.jsonArr[0].name")).isEqualTo("nOne"); - assertThat(doc.read("$.jsonArr[1].name")).isEqualTo("Jayway"); + assertThat((String)doc.read("$.jsonArr[0].name")).isEqualTo("nOne"); + assertThat((String)doc.read("$.jsonArr[1].name")).isEqualTo("Jayway"); } @Test diff --git a/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java b/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java index 60304267..81c6203d 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/old/JsonPathTest.java @@ -133,7 +133,7 @@ public class JsonPathTest extends BaseTest { Assertions.fail("Expected PathNotFoundException"); } catch (PathNotFoundException e) { } - Assertions.assertThat(JsonPath.read(json, "$.data2.passes[0].id")).isEqualTo("1"); + Assertions.assertThat((String)JsonPath.read(json, "$.data2.passes[0].id")).isEqualTo("1"); } @Test From bb0853fb5e1e5e503b1d0e589b49e8ccb9f2dacc Mon Sep 17 00:00:00 2001 From: Elias Ross Date: Thu, 30 Nov 2017 10:43:21 -0800 Subject: [PATCH 13/21] Various warning clean-ups from IntelliJ Split ValueNode into a separate class to avoid possible race-condition in class loading. Modified ArrayPathToken to be super-class of ArrayIndex/ArraySlice token because IntelliJ warned about null pointer exception possibilities. Removed redundant boolean checks. Removed use of StringBuffer -> StringBuilder. Removed dangling ; --- .../java/com/jayway/jsonpath/Criteria.java | 16 +- .../jsonpath/internal/CharacterIndex.java | 2 +- .../jsonpath/internal/DefaultsImpl.java | 2 +- .../com/jayway/jsonpath/internal/PathRef.java | 2 +- .../com/jayway/jsonpath/internal/Utils.java | 8 +- .../internal/filter/EvaluatorFactory.java | 13 +- .../internal/filter/FilterCompiler.java | 21 +- .../jsonpath/internal/filter/ValueNode.java | 653 +---------------- .../jsonpath/internal/filter/ValueNodes.java | 662 ++++++++++++++++++ .../internal/function/text/Concatenate.java | 2 +- .../internal/path/ArrayIndexToken.java | 52 ++ .../internal/path/ArrayPathToken.java | 138 +--- .../internal/path/ArraySliceOperation.java | 18 +- .../internal/path/ArraySliceToken.java | 114 +++ .../jsonpath/internal/path/CompiledPath.java | 2 +- .../jsonpath/internal/path/PathCompiler.java | 4 +- .../internal/path/PathTokenFactory.java | 4 +- .../internal/path/PredicatePathToken.java | 3 +- .../jsonpath/internal/path/RootPathToken.java | 2 +- .../internal/path/WildcardPathToken.java | 4 +- .../jsonpath/spi/cache/CacheProvider.java | 4 +- .../spi/json/AbstractJsonProvider.java | 2 +- .../java/com/jayway/jsonpath/FilterTest.java | 3 +- 23 files changed, 893 insertions(+), 838 deletions(-) create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java index 92f9b2bc..ea32460e 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java @@ -28,7 +28,9 @@ import java.util.List; import java.util.regex.Pattern; import static com.jayway.jsonpath.internal.Utils.notNull; - +import com.jayway.jsonpath.internal.filter.ValueNodes; +import static com.jayway.jsonpath.internal.filter.ValueNodes.ValueListNode; +import static com.jayway.jsonpath.internal.filter.ValueNodes.PredicateNode; /** * @@ -225,7 +227,7 @@ public class Criteria implements Predicate { public Criteria in(Collection c) { notNull(c, "collection can not be null"); this.criteriaType = RelationalOperator.IN; - this.right = new ValueNode.ValueListNode(c); + this.right = new ValueListNode(c); return this; } @@ -263,7 +265,7 @@ public class Criteria implements Predicate { public Criteria nin(Collection c) { notNull(c, "collection can not be null"); this.criteriaType = RelationalOperator.NIN; - this.right = new ValueNode.ValueListNode(c); + this.right = new ValueListNode(c); return this; } @@ -290,7 +292,7 @@ public class Criteria implements Predicate { public Criteria subsetof(Collection c) { notNull(c, "collection can not be null"); this.criteriaType = RelationalOperator.SUBSETOF; - this.right = new ValueNode.ValueListNode(c); + this.right = new ValueListNode(c); return this; } @@ -315,7 +317,7 @@ public class Criteria implements Predicate { public Criteria all(Collection c) { notNull(c, "collection can not be null"); this.criteriaType = RelationalOperator.ALL; - this.right = new ValueNode.ValueListNode(c); + this.right = new ValueListNode(c); return this; } @@ -389,7 +391,7 @@ public class Criteria implements Predicate { */ public Criteria empty(boolean empty) { this.criteriaType = RelationalOperator.EMPTY; - this.right = empty ? ValueNode.TRUE : ValueNode.FALSE; + this.right = empty ? ValueNodes.TRUE : ValueNodes.FALSE; return this; } @@ -401,7 +403,7 @@ public class Criteria implements Predicate { */ public Criteria matches(Predicate p) { this.criteriaType = RelationalOperator.MATCHES; - this.right = new ValueNode.PredicateNode(p); + this.right = new PredicateNode(p); return this; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java b/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java index 378d5c4b..bc152776 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/CharacterIndex.java @@ -172,7 +172,7 @@ public class CharacterIndex { inEscape = false; } else if('\\' == charAt(readPosition)){ inEscape = true; - } else if (c == charAt(readPosition) && !inEscape){ + } else if (c == charAt(readPosition)){ return readPosition; } readPosition ++; diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java b/json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java index d4919ca8..900bd807 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/DefaultsImpl.java @@ -32,6 +32,6 @@ public final class DefaultsImpl implements Defaults { } private DefaultsImpl() { - }; + } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java index 1b42be31..2dce79d7 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java @@ -212,7 +212,7 @@ public abstract class PathRef implements Comparable { public int compareTo(PathRef o) { if(o instanceof ArrayIndexPathRef){ ArrayIndexPathRef pf = (ArrayIndexPathRef) o; - return Integer.valueOf(pf.index).compareTo(this.index); + return Integer.compare(pf.index, this.index); } return super.compareTo(o); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java b/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java index 976c0209..ad7900fa 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/Utils.java @@ -24,8 +24,8 @@ import java.util.Iterator; public final class Utils { // accept a collection of objects, since all objects have toString() - public static String join(String delimiter, String wrap, Iterable objs) { - Iterator iter = objs.iterator(); + public static String join(String delimiter, String wrap, Iterable objs) { + Iterator iter = objs.iterator(); if (!iter.hasNext()) { return ""; } @@ -38,7 +38,7 @@ public final class Utils { } // accept a collection of objects, since all objects have toString() - public static String join(String delimiter, Iterable objs) { + public static String join(String delimiter, Iterable objs) { return join(delimiter, "", objs); } @@ -175,7 +175,7 @@ public final class Utils { } int len = str.length(); StringWriter writer = new StringWriter(len); - StringBuffer unicode = new StringBuffer(4); + StringBuilder unicode = new StringBuilder(4); boolean hadSlash = false; boolean inUnicode = false; for (int i = 0; i < len; i++) { 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 ac47274a..0669e7e8 100644 --- 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 @@ package com.jayway.jsonpath.internal.filter; import com.jayway.jsonpath.JsonPathException; import com.jayway.jsonpath.Predicate; +import static com.jayway.jsonpath.internal.filter.ValueNodes.*; import java.util.HashMap; import java.util.Map; @@ -168,7 +169,7 @@ public class EvaluatorFactory { private static class InEvaluator implements Evaluator { @Override public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode valueListNode; + ValueListNode valueListNode; if(right.isJsonNode()){ ValueNode vn = right.asJsonNode().asValueListNode(ctx); if(vn.isUndefinedNode()){ @@ -193,12 +194,12 @@ public class EvaluatorFactory { private static class AllEvaluator implements Evaluator { @Override public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode requiredValues = right.asValueListNode(); + 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(); + ValueListNode shouldContainAll = valueNode.asValueListNode(); for (ValueNode required : requiredValues) { if(!shouldContainAll.contains(required)){ return false; @@ -249,7 +250,7 @@ public class EvaluatorFactory { } } - private boolean matches(ValueNode.PatternNode patternNode, String inputToMatch) { + private boolean matches(PatternNode patternNode, String inputToMatch) { return patternNode.getCompiledPattern().matcher(inputToMatch).matches(); } @@ -269,7 +270,7 @@ public class EvaluatorFactory { private static class SubsetOfEvaluator implements Evaluator { @Override public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode rightValueListNode; + ValueListNode rightValueListNode; if(right.isJsonNode()){ ValueNode vn = right.asJsonNode().asValueListNode(ctx); if(vn.isUndefinedNode()){ @@ -280,7 +281,7 @@ public class EvaluatorFactory { } else { rightValueListNode = right.asValueListNode(); } - ValueNode.ValueListNode leftValueListNode; + ValueListNode leftValueListNode; if(left.isJsonNode()){ ValueNode vn = left.asJsonNode().asValueListNode(ctx); if(vn.isUndefinedNode()){ diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java index f6b9f2d3..107d2364 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java @@ -4,13 +4,14 @@ import com.jayway.jsonpath.Filter; import com.jayway.jsonpath.InvalidPathException; import com.jayway.jsonpath.Predicate; import com.jayway.jsonpath.internal.CharacterIndex; +import static com.jayway.jsonpath.internal.filter.ValueNodes.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; -public class FilterCompiler { +public class FilterCompiler { private static final Logger logger = LoggerFactory.getLogger(FilterCompiler.class); private static final char DOC_CONTEXT = '$'; @@ -197,10 +198,10 @@ public class FilterCompiler { filter.setPosition(savepoint); } - ValueNode.PathNode pathNode = left.asPathNode(); + PathNode pathNode = left.asPathNode(); left = pathNode.asExistsCheck(pathNode.shouldExists()); RelationalOperator operator = RelationalOperator.EXISTS; - ValueNode right = left.asPathNode().shouldExists() ? ValueNode.TRUE : ValueNode.FALSE; + ValueNode right = left.asPathNode().shouldExists() ? ValueNodes.TRUE : ValueNodes.FALSE; return new RelationalExpressionNode(left, operator, right); } @@ -239,7 +240,7 @@ public class FilterCompiler { return RelationalOperator.fromString(operator.toString()); } - private ValueNode.NullNode readNullLiteral() { + private NullNode readNullLiteral() { int begin = filter.position(); if(filter.currentChar() == NULL && filter.inBounds(filter.position() + 3)){ CharSequence nullValue = filter.subSequence(filter.position(), filter.position() + 4); @@ -252,7 +253,7 @@ public class FilterCompiler { throw new InvalidPathException("Expected value"); } - private ValueNode.JsonNode readJsonLiteral(){ + private JsonNode readJsonLiteral(){ int begin = filter.position(); char openChar = filter.currentChar(); @@ -273,7 +274,7 @@ public class FilterCompiler { } - private ValueNode.PatternNode readPattern() { + private PatternNode readPattern() { int begin = filter.position(); int closingIndex = filter.nextIndexOfUnescaped(PATTERN); if (closingIndex == -1) { @@ -289,7 +290,7 @@ public class FilterCompiler { return ValueNode.createPatternNode(pattern); } - private ValueNode.StringNode readStringLiteral(char endChar) { + private StringNode readStringLiteral(char endChar) { int begin = filter.position(); int closingSingleQuoteIndex = filter.nextIndexOfUnescaped(endChar); @@ -303,7 +304,7 @@ public class FilterCompiler { return ValueNode.createStringNode(stringLiteral, true); } - private ValueNode.NumberNode readNumberLiteral() { + private NumberNode readNumberLiteral() { int begin = filter.position(); while (filter.inBounds() && filter.isNumberCharacter(filter.position())) { @@ -314,7 +315,7 @@ public class FilterCompiler { return ValueNode.createNumberNode(numberLiteral); } - private ValueNode.BooleanNode readBooleanLiteral() { + private BooleanNode readBooleanLiteral() { int begin = filter.position(); int end = filter.currentChar() == TRUE ? filter.position() + 3 : filter.position() + 4; @@ -331,7 +332,7 @@ public class FilterCompiler { return ValueNode.createBooleanNode(boolValue); } - private ValueNode.PathNode readPath() { + private PathNode readPath() { char previousSignificantChar = filter.previousSignificantChar(); int begin = filter.position(); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java index 23afe785..d87994ee 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNode.java @@ -1,40 +1,17 @@ package com.jayway.jsonpath.internal.filter; -import com.jayway.jsonpath.Configuration; +import java.util.regex.Pattern; + import com.jayway.jsonpath.InvalidPathException; 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.Utils; import com.jayway.jsonpath.internal.path.PathCompiler; -import com.jayway.jsonpath.internal.path.PredicateContextImpl; -import com.jayway.jsonpath.spi.json.JsonProvider; - import net.minidev.json.parser.JSONParser; -import net.minidev.json.parser.ParseException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -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; +import static com.jayway.jsonpath.internal.filter.ValueNodes.*; 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() { @@ -174,7 +151,7 @@ public abstract class ValueNode { // //---------------------------------------------------- public static ValueNode toValueNode(Object o){ - if(o == null) return ValueNode.NULL_NODE; + if(o == null) return 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); @@ -235,625 +212,5 @@ public abstract class ValueNode { return new PathNode(path); } - //---------------------------------------------------- - // - // 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 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() { - - String flags = ""; - if((compiledPattern.flags() & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE){ - flags = "i"; - } - if(!pattern.startsWith("/")){ - return "/" + pattern + "/" + flags; - } else { - 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(isArray(ctx)) return List.class; - else if(isMap(ctx)) return Map.class; - else if(parse(ctx) instanceof Number) return Number.class; - else if(parse(ctx) instanceof String) return String.class; - else if(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 { - return new ValueListNode(Collections.unmodifiableList((List) parse(ctx))); - } - } - - public Object parse(Predicate.PredicateContext ctx){ - try { - return parsed ? json : new JSONParser(JSONParser.MODE_PERMISSIVE).parse(json.toString()); - } catch (ParseException e) { - throw new IllegalArgumentException(e); - } - } - - public boolean isParsed() { - return parsed; - } - - public Object getJson() { - return json; - } - - public boolean isArray(Predicate.PredicateContext ctx) { - return parse(ctx) instanceof List; - } - - public boolean isMap(Predicate.PredicateContext ctx) { - return parse(ctx) instanceof Map; - } - - public int length(Predicate.PredicateContext ctx) { - return isArray(ctx) ? ((List) parse(ctx)).size() : -1; - } - - public boolean isEmpty(Predicate.PredicateContext ctx) { - if (isArray(ctx) || isMap(ctx)) return ((Collection) parse(ctx)).size() == 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 boolean useSingleQuote = true; - - private StringNode(CharSequence charSequence, boolean escape) { - if (escape && charSequence.length() > 1) { - char open = charSequence.charAt(0); - char close = charSequence.charAt(charSequence.length()-1); - if (open == '\'' && close == '\'') { - charSequence = charSequence.subSequence(1, charSequence.length()-1); - } else if (open == '"' && close == '"') { - charSequence = charSequence.subSequence(1, charSequence.length()-1); - useSingleQuote = false; - } - string = Utils.unescape(charSequence.toString()); - } else { - string = charSequence.toString(); - } - } - - @Override - public NumberNode asNumberNode() { - BigDecimal number = null; - try { - number = new BigDecimal(string); - } catch (NumberFormatException nfe){ - return NumberNode.NAN; - } - return new NumberNode(number); - } - - 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() { - String quote = useSingleQuote ? "'" : "\""; - return quote + Utils.escape(string, true) + quote; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof StringNode) && !(o instanceof NumberNode)) return false; - - StringNode that = ((ValueNode) o).asStringNode(); - - return !(string != null ? !string.equals(that.getString()) : that.getString() != null); - - } - } - - public static class NumberNode extends ValueNode { - - public static NumberNode NAN = new NumberNode((BigDecimal)null); - - private final BigDecimal number; - - private NumberNode(BigDecimal number) { - this.number = number; - } - private NumberNode(CharSequence num) { - number = new BigDecimal(num.toString()); - } - - @Override - public StringNode asStringNode() { - return new StringNode(number.toString(), false); - } - - 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) && !(o instanceof StringNode)) return false; - - NumberNode that = ((ValueNode)o).asNumberNode(); - - if(that == NumberNode.NAN){ - return false; - } else { - return number.compareTo(that.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 { - - private List nodes = new ArrayList(); - - public ValueListNode(Collection values) { - for (Object value : values) { - nodes.add(toValueNode(value)); - } - } - - public boolean contains(ValueNode node){ - return nodes.contains(node); - } - - public boolean subsetof(ValueListNode right) { - for (ValueNode leftNode : nodes) { - if (!right.nodes.contains(leftNode)) { - return false; - } - } - return true; - } - - public List 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 iterator() { - return nodes.iterator(); - } - } - - public static 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; - - PathNode(Path path) { - this(path, false, false); - } - - PathNode(CharSequence charSequence, boolean existsCheck, boolean shouldExist) { - this(PathCompiler.compile(charSequence.toString()), existsCheck, shouldExist); - } - - 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 existsCheck && ! shouldExist ? Utils.concat("!" , path.toString()) : path.toString(); - } - - public ValueNode evaluate(Predicate.PredicateContext ctx) { - if (isExistsCheck()) { - try { - Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build(); - 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(ctx.configuration().mappingProvider().map(res, List.class, ctx.configuration())); - else if (ctx.configuration().jsonProvider().isMap(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, Map.class, ctx.configuration())); - else throw new JsonPathException("Could not convert " + res.toString() + " to a ValueNode"); - } catch (PathNotFoundException e) { - return ValueNode.UNDEFINED; - } - } - } - - - } } + diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java new file mode 100644 index 00000000..531868db --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java @@ -0,0 +1,662 @@ +package com.jayway.jsonpath.internal.filter; + +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; + +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.Utils; +import com.jayway.jsonpath.internal.path.PathCompiler; +import com.jayway.jsonpath.internal.path.PredicateContextImpl; +import com.jayway.jsonpath.spi.json.JsonProvider; +import net.minidev.json.parser.JSONParser; +import net.minidev.json.parser.ParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Moved these nodes out of the ValueNode abstract class. + * This is to avoid this possible issue: + * + * Classes that refer to their own subclasses in their static initializers or in static fields. + * Such references can cause JVM-level deadlocks in multithreaded environment, when + * one thread tries to load superclass and another thread tries to load subclass at the same time. + */ +public interface ValueNodes { + + NullNode NULL_NODE = new NullNode(); + BooleanNode TRUE = new BooleanNode("true"); + BooleanNode FALSE = new BooleanNode("false"); + UndefinedNode UNDEFINED = new UndefinedNode(); + + //---------------------------------------------------- + // + // ValueNode Implementations + // + //---------------------------------------------------- + class PatternNode extends ValueNode { + private final String pattern; + private final Pattern compiledPattern; + + 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); + } + + PatternNode(Pattern pattern) { + this.pattern = pattern.pattern(); + this.compiledPattern = pattern; + } + + + 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() { + + String flags = ""; + if((compiledPattern.flags() & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE){ + flags = "i"; + } + if(!pattern.startsWith("/")){ + return "/" + pattern + "/" + flags; + } else { + 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); + + } + } + + class JsonNode extends ValueNode { + private final Object json; + private final boolean parsed; + + JsonNode(CharSequence charSequence) { + json = charSequence.toString(); + parsed = false; + } + + JsonNode(Object parsedJson) { + json = parsedJson; + parsed = true; + } + + @Override + public Class type(Predicate.PredicateContext ctx) { + if(isArray(ctx)) return List.class; + else if(isMap(ctx)) return Map.class; + else if(parse(ctx) instanceof Number) return Number.class; + else if(parse(ctx) instanceof String) return String.class; + else if(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 { + return new ValueListNode(Collections.unmodifiableList((List) parse(ctx))); + } + } + + public Object parse(Predicate.PredicateContext ctx){ + try { + return parsed ? json : new JSONParser(JSONParser.MODE_PERMISSIVE).parse(json.toString()); + } catch (ParseException e) { + throw new IllegalArgumentException(e); + } + } + + public boolean isParsed() { + return parsed; + } + + public Object getJson() { + return json; + } + + public boolean isArray(Predicate.PredicateContext ctx) { + return parse(ctx) instanceof List; + } + + public boolean isMap(Predicate.PredicateContext ctx) { + return parse(ctx) instanceof Map; + } + + public int length(Predicate.PredicateContext ctx) { + return isArray(ctx) ? ((List) parse(ctx)).size() : -1; + } + + public boolean isEmpty(Predicate.PredicateContext ctx) { + if (isArray(ctx) || isMap(ctx)) return ((Collection) parse(ctx)).size() == 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); + } + } + + class StringNode extends ValueNode { + private final String string; + private boolean useSingleQuote = true; + + StringNode(CharSequence charSequence, boolean escape) { + if (escape && charSequence.length() > 1) { + char open = charSequence.charAt(0); + char close = charSequence.charAt(charSequence.length()-1); + if (open == '\'' && close == '\'') { + charSequence = charSequence.subSequence(1, charSequence.length()-1); + } else if (open == '"' && close == '"') { + charSequence = charSequence.subSequence(1, charSequence.length()-1); + useSingleQuote = false; + } + string = Utils.unescape(charSequence.toString()); + } else { + string = charSequence.toString(); + } + } + + @Override + public NumberNode asNumberNode() { + BigDecimal number = null; + try { + number = new BigDecimal(string); + } catch (NumberFormatException nfe){ + return NumberNode.NAN; + } + return new NumberNode(number); + } + + 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() { + String quote = useSingleQuote ? "'" : "\""; + return quote + Utils.escape(string, true) + quote; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StringNode) && !(o instanceof NumberNode)) return false; + + StringNode that = ((ValueNode) o).asStringNode(); + + return !(string != null ? !string.equals(that.getString()) : that.getString() != null); + + } + } + + class NumberNode extends ValueNode { + + public static NumberNode NAN = new NumberNode((BigDecimal)null); + + private final BigDecimal number; + + NumberNode(BigDecimal number) { + this.number = number; + } + NumberNode(CharSequence num) { + number = new BigDecimal(num.toString()); + } + + @Override + public StringNode asStringNode() { + return new StringNode(number.toString(), false); + } + + 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) && !(o instanceof StringNode)) return false; + + NumberNode that = ((ValueNode)o).asNumberNode(); + + if(that == NumberNode.NAN){ + return false; + } else { + return number.compareTo(that.number) == 0; + } + } + } + + 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); + } + } + + class ClassNode extends ValueNode { + private final Class clazz; + + 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); + } + } + + 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; + } + } + + 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; + } + } + + 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(); + } + } + + class ValueListNode extends ValueNode implements Iterable { + + private List nodes = new ArrayList(); + + public ValueListNode(Collection values) { + for (Object value : values) { + nodes.add(toValueNode(value)); + } + } + + public boolean contains(ValueNode node){ + return nodes.contains(node); + } + + public boolean subsetof(ValueListNode right) { + for (ValueNode leftNode : nodes) { + if (!right.nodes.contains(leftNode)) { + return false; + } + } + return true; + } + + public List 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 nodes.equals(that.nodes); + } + + @Override + public Iterator iterator() { + return nodes.iterator(); + } + } + + 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; + + PathNode(Path path) { + this(path, false, false); + } + + PathNode(CharSequence charSequence, boolean existsCheck, boolean shouldExist) { + this(PathCompiler.compile(charSequence.toString()), existsCheck, shouldExist); + } + + 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 existsCheck && ! shouldExist ? Utils.concat("!" , path.toString()) : path.toString(); + } + + public ValueNode evaluate(Predicate.PredicateContext ctx) { + if (isExistsCheck()) { + try { + Configuration c = Configuration.builder().jsonProvider(ctx.configuration().jsonProvider()).options(Option.REQUIRE_PROPERTIES).build(); + Object result = path.evaluate(ctx.item(), ctx.root(), c).getValue(false); + return result == JsonProvider.UNDEFINED ? FALSE : TRUE; + } catch (PathNotFoundException e) { + return 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 String) return ValueNode.createStringNode(res.toString(), false); + else if (res instanceof Boolean) return ValueNode.createBooleanNode(res.toString()); + else if (res == null) return NULL_NODE; + else if (ctx.configuration().jsonProvider().isArray(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, List.class, ctx.configuration())); + else if (ctx.configuration().jsonProvider().isMap(res)) return ValueNode.createJsonNode(ctx.configuration().mappingProvider().map(res, Map.class, ctx.configuration())); + else throw new JsonPathException("Could not convert " + res.toString() + " to a ValueNode"); + } catch (PathNotFoundException e) { + return UNDEFINED; + } + } + } + + + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java b/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java index 6386d6e2..d499afef 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/function/text/Concatenate.java @@ -15,7 +15,7 @@ import java.util.List; public class Concatenate implements PathFunction { @Override public Object invoke(String currentPath, PathRef parent, Object model, EvaluationContext ctx, List parameters) { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); if(ctx.configuration().jsonProvider().isArray(model)){ Iterable objects = ctx.configuration().jsonProvider().toIterable(model); for (Object obj : objects) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java new file mode 100644 index 00000000..3e4b36e2 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayIndexToken.java @@ -0,0 +1,52 @@ +/* + * Copyright 2011 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jayway.jsonpath.internal.path; + +import com.jayway.jsonpath.internal.PathRef; + +import static java.lang.String.format; + +public class ArrayIndexToken extends ArrayPathToken { + + private final ArrayIndexOperation arrayIndexOperation; + + ArrayIndexToken(final ArrayIndexOperation arrayIndexOperation) { + this.arrayIndexOperation = arrayIndexOperation; + } + + @Override + public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { + if (!checkArrayModel(currentPath, model, ctx)) + return; + if (arrayIndexOperation.isSingleIndexOperation()) { + handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx); + } else { + for (Integer index : arrayIndexOperation.indexes()) { + handleArrayIndex(index, currentPath, model, ctx); + } + } + } + + @Override + public String getPathFragment() { + return arrayIndexOperation.toString(); + } + + @Override + public boolean isTokenDefinite() { + return arrayIndexOperation.isSingleIndexOperation(); + } + +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java index 05d29d76..15a0f2da 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArrayPathToken.java @@ -22,143 +22,7 @@ import org.slf4j.LoggerFactory; import static java.lang.String.format; -/** - * - */ -public class ArrayPathToken extends PathToken { - - private static final Logger logger = LoggerFactory.getLogger(ArrayPathToken.class); - - private final ArraySliceOperation arraySliceOperation; - private final ArrayIndexOperation arrayIndexOperation; - - ArrayPathToken(final ArraySliceOperation arraySliceOperation) { - this.arraySliceOperation = arraySliceOperation; - this.arrayIndexOperation = null; - } - - ArrayPathToken(final ArrayIndexOperation arrayIndexOperation) { - this.arrayIndexOperation = arrayIndexOperation; - this.arraySliceOperation = null; - } - - @Override - public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { - if (! checkArrayModel(currentPath, model, ctx)) - return; - if(arraySliceOperation != null){ - evaluateSliceOperation(currentPath, parent, model, ctx); - } else { - evaluateIndexOperation(currentPath, parent, model, ctx); - } - - } - - public void evaluateIndexOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { - - if (! checkArrayModel(currentPath, model, ctx)) - return; - - if(arrayIndexOperation.isSingleIndexOperation()){ - handleArrayIndex(arrayIndexOperation.indexes().get(0), currentPath, model, ctx); - } else { - for (Integer index : arrayIndexOperation.indexes()) { - handleArrayIndex(index, currentPath, model, ctx); - } - } - } - - public void evaluateSliceOperation(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { - - if (! checkArrayModel(currentPath, model, ctx)) - return; - - switch (arraySliceOperation.operation()) { - case SLICE_FROM: - sliceFrom(arraySliceOperation, currentPath, parent, model, ctx); - break; - case SLICE_BETWEEN: - sliceBetween(arraySliceOperation, currentPath, parent, model, ctx); - break; - case SLICE_TO: - sliceTo(arraySliceOperation, currentPath, parent, model, ctx); - break; - } - } - - public void sliceFrom(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { - int length = ctx.jsonProvider().length(model); - int from = operation.from(); - if (from < 0) { - //calculate slice start from array length - from = length + from; - } - from = Math.max(0, from); - - logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString()); - - if (length == 0 || from >= length) { - return; - } - for (int i = from; i < length; i++) { - handleArrayIndex(i, currentPath, model, ctx); - } - } - - public void sliceBetween(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { - int length = ctx.jsonProvider().length(model); - int from = operation.from(); - int to = operation.to(); - - to = Math.min(length, to); - - if (from >= to || length == 0) { - return; - } - - logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString()); - - for (int i = from; i < to; i++) { - handleArrayIndex(i, currentPath, model, ctx); - } - } - - public void sliceTo(ArraySliceOperation operation, String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { - int length = ctx.jsonProvider().length(model); - if (length == 0) { - return; - } - int to = operation.to(); - if (to < 0) { - //calculate slice end from array length - to = length + to; - } - to = Math.min(length, to); - - logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString()); - - for (int i = 0; i < to; i++) { - handleArrayIndex(i, currentPath, model, ctx); - } - } - - @Override - public String getPathFragment() { - if(arrayIndexOperation != null){ - return arrayIndexOperation.toString(); - } else { - return arraySliceOperation.toString(); - } - } - - @Override - public boolean isTokenDefinite() { - if(arrayIndexOperation != null){ - return arrayIndexOperation.isSingleIndexOperation(); - } else { - return false; - } - } +public abstract class ArrayPathToken extends PathToken { /** * Check if model is non-null and array. diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java index e6dcb8c1..185af512 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceOperation.java @@ -58,19 +58,19 @@ public class ArraySliceOperation { Integer tempFrom = tryRead(tokens, 0); Integer tempTo = tryRead(tokens, 1); - Operation tempOperpation; - - if(tempFrom != null && tempTo == null){ - tempOperpation = Operation.SLICE_FROM; - } else if(tempFrom != null && tempTo != null){ - tempOperpation = Operation.SLICE_BETWEEN; - } else if(tempFrom == null && tempTo != null){ - tempOperpation = Operation.SLICE_TO; + Operation tempOperation; + + if (tempFrom != null && tempTo == null) { + tempOperation = Operation.SLICE_FROM; + } else if (tempFrom != null) { + tempOperation = Operation.SLICE_BETWEEN; + } else if (tempTo != null) { + tempOperation = Operation.SLICE_TO; } else { throw new InvalidPathException("Failed to parse SliceOperation: " + operation); } - return new ArraySliceOperation(tempFrom, tempTo, tempOperpation); + return new ArraySliceOperation(tempFrom, tempTo, tempOperation); } private static Integer tryRead(String[] tokens, int idx){ diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java new file mode 100644 index 00000000..af43e7eb --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/ArraySliceToken.java @@ -0,0 +1,114 @@ +/* + * Copyright 2011 the original author or authors. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jayway.jsonpath.internal.path; + +import com.jayway.jsonpath.internal.PathRef; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ArraySliceToken extends ArrayPathToken { + + private static final Logger logger = LoggerFactory.getLogger(ArraySliceToken.class); + + private final ArraySliceOperation operation; + + ArraySliceToken(final ArraySliceOperation operation) { + this.operation = operation; + } + + @Override + public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { + if (!checkArrayModel(currentPath, model, ctx)) + return; + switch (operation.operation()) { + case SLICE_FROM: + sliceFrom(currentPath, parent, model, ctx); + break; + case SLICE_BETWEEN: + sliceBetween(currentPath, parent, model, ctx); + break; + case SLICE_TO: + sliceTo(currentPath, parent, model, ctx); + break; + } + } + + private void sliceFrom(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { + int length = ctx.jsonProvider().length(model); + int from = operation.from(); + if (from < 0) { + //calculate slice start from array length + from = length + from; + } + from = Math.max(0, from); + + logger.debug("Slice from index on array with length: {}. From index: {} to: {}. Input: {}", length, from, length - 1, toString()); + + if (length == 0 || from >= length) { + return; + } + for (int i = from; i < length; i++) { + handleArrayIndex(i, currentPath, model, ctx); + } + } + + private void sliceBetween(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { + int length = ctx.jsonProvider().length(model); + int from = operation.from(); + int to = operation.to(); + + to = Math.min(length, to); + + if (from >= to || length == 0) { + return; + } + + logger.debug("Slice between indexes on array with length: {}. From index: {} to: {}. Input: {}", length, from, to, toString()); + + for (int i = from; i < to; i++) { + handleArrayIndex(i, currentPath, model, ctx); + } + } + + private void sliceTo(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { + int length = ctx.jsonProvider().length(model); + if (length == 0) { + return; + } + int to = operation.to(); + if (to < 0) { + //calculate slice end from array length + to = length + to; + } + to = Math.min(length, to); + + logger.debug("Slice to index on array with length: {}. From index: 0 to: {}. Input: {}", length, to, toString()); + + for (int i = 0; i < to; i++) { + handleArrayIndex(i, currentPath, model, ctx); + } + } + + @Override + public String getPathFragment() { + return operation.toString(); + } + + @Override + public boolean isTokenDefinite() { + return false; + } + +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java index 383fde89..131e068f 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/CompiledPath.java @@ -97,7 +97,7 @@ public class CompiledPath implements Path { try { PathRef op = ctx.forUpdate() ? PathRef.createRoot(rootDocument) : PathRef.NO_OP; root.evaluate("", op, document, ctx); - } catch (EvaluationAbortException abort){}; + } catch (EvaluationAbortException abort) {} return ctx; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java index 0317311e..e11c4c02 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathCompiler.java @@ -275,7 +275,7 @@ public class PathCompiler { Boolean endOfStream = false; char priorChar = 0; List parameters = new ArrayList(); - StringBuffer parameter = new StringBuffer(); + StringBuilder parameter = new StringBuilder(); while (path.inBounds() && !endOfStream) { char c = path.currentChar(); path.incrementPosition(1); @@ -580,7 +580,7 @@ public class PathCompiler { } break; } else if (c == potentialStringDelimiter) { - if (inProperty && !inEscape) { + if (inProperty) { char nextSignificantChar = path.nextSignificantChar(readPosition); if (nextSignificantChar != CLOSE_SQUARE_BRACKET && nextSignificantChar != COMMA) { fail("Property must be separated by comma or Property must be terminated close square bracket at index "+readPosition); diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java index 995c73e4..b9dacb6d 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PathTokenFactory.java @@ -23,11 +23,11 @@ public class PathTokenFactory { } public static PathToken createSliceArrayPathToken(final ArraySliceOperation arraySliceOperation) { - return new ArrayPathToken(arraySliceOperation); + return new ArraySliceToken(arraySliceOperation); } public static PathToken createIndexArrayPathToken(final ArrayIndexOperation arrayIndexOperation) { - return new ArrayPathToken(arrayIndexOperation); + return new ArrayIndexToken(arrayIndexOperation); } public static PathToken createWildCardPathToken() { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java index af668634..ee67fc1e 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/PredicatePathToken.java @@ -20,6 +20,7 @@ import com.jayway.jsonpath.Predicate; import com.jayway.jsonpath.internal.PathRef; import java.util.Collection; +import java.util.Collections; import static java.lang.String.format; import static java.util.Arrays.asList; @@ -33,7 +34,7 @@ public class PredicatePathToken extends PathToken { private final Collection predicates; PredicatePathToken(Predicate filter) { - this.predicates = asList(filter); + this.predicates = Collections.singletonList(filter); } PredicatePathToken(Collection predicates) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java index 349af9f7..2d443ec1 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/RootPathToken.java @@ -27,7 +27,7 @@ public class RootPathToken extends PathToken { RootPathToken(char rootToken) { - this.rootToken = Character.toString(rootToken);; + this.rootToken = Character.toString(rootToken); this.tail = this; this.tokenCount = 1; } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java b/json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java index 37d02ec9..7f3ccec1 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/path/WildcardPathToken.java @@ -14,6 +14,8 @@ */ package com.jayway.jsonpath.internal.path; +import java.util.Collections; + import com.jayway.jsonpath.Option; import com.jayway.jsonpath.PathNotFoundException; import com.jayway.jsonpath.internal.PathRef; @@ -32,7 +34,7 @@ public class WildcardPathToken extends PathToken { public void evaluate(String currentPath, PathRef parent, Object model, EvaluationContextImpl ctx) { if (ctx.jsonProvider().isMap(model)) { for (String property : ctx.jsonProvider().getPropertyKeys(model)) { - handleObjectProperty(currentPath, model, ctx, asList(property)); + handleObjectProperty(currentPath, model, ctx, Collections.singletonList(property)); } } else if (ctx.jsonProvider().isArray(model)) { for (int idx = 0; idx < ctx.jsonProvider().length(model); idx++) { diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java index 82d6f18f..fe5ced53 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/cache/CacheProvider.java @@ -6,7 +6,6 @@ import static com.jayway.jsonpath.internal.Utils.notNull; public class CacheProvider { private static Cache cache; - private static boolean cachingEnabled; public static void setCache(Cache cache){ notNull(cache, "Cache may not be null"); @@ -16,7 +15,6 @@ public class CacheProvider { } else { CacheProvider.cache = cache; } - cachingEnabled = !(CacheProvider.cache instanceof NOOPCache); } } @@ -36,4 +34,4 @@ public class CacheProvider { return new LRUCache(400); //return new NOOPCache(); } -} \ No newline at end of file +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java b/json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java index 56cfb47c..df60a629 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java +++ b/json-path/src/main/java/com/jayway/jsonpath/spi/json/AbstractJsonProvider.java @@ -162,7 +162,7 @@ public abstract class AbstractJsonProvider implements JsonProvider { * @return an Iterable that iterates over the entries of an array */ @SuppressWarnings("unchecked") - public Iterable toIterable(Object obj) { + public Iterable toIterable(Object obj) { if (isArray(obj)) return ((Iterable) obj); else diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java index e786ae83..74116b47 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java @@ -279,7 +279,8 @@ public class FilterTest extends BaseTest { Filter farr = parse("[?(@.foo == " + arr + ")]"); //Filter fobjF = parse("[?(@.foo == " + nest + ")]"); //Filter fobjT = parse("[?(@.bar == " + nest + ")]"); - assertThat(farr.apply(context)).isEqualTo(true); + boolean apply = farr.apply(context); + assertThat(apply).isEqualTo(true); //assertThat(fobjF.apply(context)).isEqualTo(false); //assertThat(fobjT.apply(context)).isEqualTo(true); } From 16df8c3e9c2a9ab8d5a6fae2c132f967d5c69412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Skowro=C5=84ski?= Date: Sun, 14 Oct 2018 21:57:56 +0200 Subject: [PATCH 14/21] fix "Invalid delete operation" mistake for set operation "Invalid delete operation" -> "Invalid set operation" in set method --- .../src/main/java/com/jayway/jsonpath/internal/PathRef.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java index 1b42be31..95a836a5 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/PathRef.java @@ -107,7 +107,7 @@ public abstract class PathRef implements Comparable { @Override public void set(Object newVal, Configuration configuration) { - throw new InvalidModificationException("Invalid delete operation"); + throw new InvalidModificationException("Invalid set operation"); } public void convert(MapFunction mapFunction, Configuration configuration){ From 0e3a0a9717640a226f12f9ccd8b7df5f847b6ebd Mon Sep 17 00:00:00 2001 From: Magnus Reftel Date: Mon, 10 Dec 2018 08:26:20 +0100 Subject: [PATCH 15/21] Correct count of JsonProviders in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68fa76a8..d431f002 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ This option makes sure no exceptions are propagated from path evaluation. It fol ### JsonProvider SPI -JsonPath is shipped with three different JsonProviders: +JsonPath is shipped with five different JsonProviders: * [JsonSmartJsonProvider](https://code.google.com/p/json-smart/) (default) * [JacksonJsonProvider](https://github.com/FasterXML/jackson) From d17fc9134a19d2d635a6b052b5c90b40df8c970a Mon Sep 17 00:00:00 2001 From: Magnus Reftel Date: Mon, 10 Dec 2018 08:35:17 +0100 Subject: [PATCH 16/21] Use 2.4.0 as version in maven dependency example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68fa76a8..23dd7e1e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ JsonPath is available at the Central Maven Repository. Maven users add this to y com.jayway.jsonpath json-path - 2.3.0 + 2.4.0 ``` From f3ef9d0992d8bf141cf481af78c30b97190f9033 Mon Sep 17 00:00:00 2001 From: kallestenflo Date: Tue, 22 Jan 2019 14:29:36 +0100 Subject: [PATCH 17/21] Revert "Add support of new operators ANYOF and NONEOF" --- README.md | 44 ++++++----- .../java/com/jayway/jsonpath/Criteria.java | 50 ------------- .../internal/filter/EvaluatorFactory.java | 75 ------------------- .../internal/filter/RelationalOperator.java | 4 +- .../com/jayway/jsonpath/FilterParseTest.java | 20 +---- .../java/com/jayway/jsonpath/FilterTest.java | 31 +------- 6 files changed, 24 insertions(+), 200 deletions(-) diff --git a/README.md b/README.md index 60846654..bbbefe09 100644 --- a/README.md +++ b/README.md @@ -77,13 +77,13 @@ Functions Functions can be invoked at the tail end of a path - the input to a function is the output of the path expression. The function output is dictated by the function itself. -| Function | Description | Output | -| :------------------------ | :------------------------------------------------------------------ |-----------| -| min() | Provides the min value of an array of numbers | Double | -| max() | Provides the max value of an array of numbers | Double | -| avg() | Provides the average value of an array of numbers | Double | -| stddev() | Provides the standard deviation value of an array of numbers | Double | -| length() | Provides the length of an array | Integer | +| Function | Description | Output | +| :------------------------ | :----------------------------------------------------------------- |-----------| +| min() | Provides the min value of an array of numbers | Double | +| max() | Provides the max value of an array of numbers | Double | +| avg() | Provides the average value of an array of numbers | Double | +| stddev() | Provides the standard deviation value of an array of numbers | Double | +| length() | Provides the length of an array | Integer | Filter Operators @@ -91,22 +91,20 @@ Filter Operators Filters are logical expressions used to filter arrays. A typical filter would be `[?(@.age > 18)]` where `@` represents the current item being processed. More complex filters can be created with logical operators `&&` and `||`. String literals must be enclosed by single or double quotes (`[?(@.color == 'blue')]` or `[?(@.color == "blue")]`). -| Operator | Description | -| :----------------------- | :-------------------------------------------------------------------- | -| == | left is equal to right (note that 1 is not equal to '1') | -| != | left is not equal to right | -| < | left is less than right | -| <= | left is less or equal to right | -| > | left is greater than right | -| >= | left is greater than or equal to right | -| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] | -| in | left exists in right [?(@.size in ['S', 'M'])] | -| nin | left does not exists in right | -| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] | -| anyof | left has an intersection with right [?(@.sizes anyof ['M', 'L'])] | -| noneof | left has no intersection with right [?(@.sizes noneof ['M', 'L'])] | -| size | size of left (array or string) should match right | -| empty | left (array or string) should be empty | +| Operator | Description | +| :----------------------- | :---------------------------------------------------------------- | +| == | left is equal to right (note that 1 is not equal to '1') | +| != | left is not equal to right | +| < | left is less than right | +| <= | left is less or equal to right | +| > | left is greater than right | +| >= | left is greater than or equal to right | +| =~ | left matches regular expression [?(@.name =~ /foo.*?/i)] | +| in | left exists in right [?(@.size in ['S', 'M'])] | +| nin | left does not exists in right | +| subsetof | left is a subset of right [?(@.sizes subsetof ['S', 'M', 'L'])] | +| size | size of left (array or string) should match right | +| empty | left (array or string) should be empty | Path Examples diff --git a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java index 5155b486..ea32460e 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Criteria.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Criteria.java @@ -296,56 +296,6 @@ public class Criteria implements Predicate { return this; } - /** - * The anyof 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 anyof 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 noneof 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 noneof 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 all operator is similar to $in, but instead of matching any value * in the specified array all values in the array must be matched. 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 a006e638..0669e7e8 100644 --- 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 @@ -31,8 +31,6 @@ public class EvaluatorFactory { evaluators.put(RelationalOperator.MATCHES, new PredicateMatchEvaluator()); evaluators.put(RelationalOperator.TYPE, new TypeEvaluator()); evaluators.put(RelationalOperator.SUBSETOF, new SubsetOfEvaluator()); - evaluators.put(RelationalOperator.ANYOF, new AnyOfEvaluator()); - evaluators.put(RelationalOperator.NONEOF, new NoneOfEvaluator()); } public static Evaluator createEvaluator(RelationalOperator operator){ @@ -298,77 +296,4 @@ public class EvaluatorFactory { } } - private static class AnyOfEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode rightValueListNode; - if (right.isJsonNode()) { - ValueNode vn = right.asJsonNode().asValueListNode(ctx); - if (vn.isUndefinedNode()) { - return false; - } else { - rightValueListNode = vn.asValueListNode(); - } - } else { - rightValueListNode = right.asValueListNode(); - } - ValueNode.ValueListNode leftValueListNode; - if (left.isJsonNode()) { - ValueNode vn = left.asJsonNode().asValueListNode(ctx); - if (vn.isUndefinedNode()) { - return false; - } else { - leftValueListNode = vn.asValueListNode(); - } - } else { - leftValueListNode = left.asValueListNode(); - } - - for (ValueNode leftValueNode : leftValueListNode) { - for (ValueNode rightValueNode : rightValueListNode) { - if (leftValueNode.equals(rightValueNode)) { - return true; - } - } - } - return false; - } - } - - private static class NoneOfEvaluator implements Evaluator { - @Override - public boolean evaluate(ValueNode left, ValueNode right, Predicate.PredicateContext ctx) { - ValueNode.ValueListNode rightValueListNode; - if (right.isJsonNode()) { - ValueNode vn = right.asJsonNode().asValueListNode(ctx); - if (vn.isUndefinedNode()) { - return false; - } else { - rightValueListNode = vn.asValueListNode(); - } - } else { - rightValueListNode = right.asValueListNode(); - } - ValueNode.ValueListNode leftValueListNode; - if (left.isJsonNode()) { - ValueNode vn = left.asJsonNode().asValueListNode(ctx); - if (vn.isUndefinedNode()) { - return false; - } else { - leftValueListNode = vn.asValueListNode(); - } - } else { - leftValueListNode = left.asValueListNode(); - } - - for (ValueNode leftValueNode : leftValueListNode) { - for (ValueNode rightValueNode : rightValueListNode) { - if (leftValueNode.equals(rightValueNode)) { - return false; - } - } - } - return true; - } - } } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java index 84e2aed0..830cc3bb 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/RelationalOperator.java @@ -30,9 +30,7 @@ public enum RelationalOperator { TYPE("TYPE"), MATCHES("MATCHES"), EMPTY("EMPTY"), - SUBSETOF("SUBSETOF"), - ANYOF("ANYOF"), - NONEOF("NONEOF"); + SUBSETOF("SUBSETOF"); private final String operatorString; diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java index 37e37f79..948ca75f 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/FilterParseTest.java @@ -1,9 +1,9 @@ package com.jayway.jsonpath; +import java.util.Collections; import org.assertj.core.api.Assertions; import org.junit.Test; -import java.util.Collections; import java.util.regex.Pattern; import static com.jayway.jsonpath.Criteria.where; @@ -152,24 +152,6 @@ public class FilterParseTest { assertThat(filter).isEqualTo(parsed); } - @Test - public void a_anyof_filter_can_be_serialized() { - - String filter = filter(where("a").anyof(Collections.emptyList())).toString(); - String parsed = parse("[?(@['a'] ANYOF [])]").toString(); - - assertThat(filter).isEqualTo(parsed); - } - - @Test - public void a_noneof_filter_can_be_serialized() { - - String filter = filter(where("a").noneof(Collections.emptyList())).toString(); - String parsed = parse("[?(@['a'] NONEOF [])]").toString(); - - assertThat(filter).isEqualTo(parsed); - } - @Test public void a_exists_filter_can_be_serialized() { diff --git a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java index 5cb9b1b0..74116b47 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/FilterTest.java @@ -1,5 +1,6 @@ package com.jayway.jsonpath; +import java.util.ArrayList; import org.assertj.core.util.Lists; import org.junit.Test; @@ -378,36 +379,6 @@ public class FilterTest extends BaseTest { assertThat(filter(where("string-arr").subsetof(list)).apply(createPredicateContext(json))).isEqualTo(false); } - //---------------------------------------------------------------------------- - // - // ANYOF - // - //---------------------------------------------------------------------------- - @Test - public void array_anyof_evals() { - List list = Lists.newArrayList("a", "z"); - assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(true); - list = Lists.newArrayList("z", "b", "a"); - assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(true); - list = Lists.newArrayList("x", "y", "z"); - assertThat(filter(where("string-arr").anyof(list)).apply(createPredicateContext(json))).isEqualTo(false); - } - - //---------------------------------------------------------------------------- - // - // NONEOF - // - //---------------------------------------------------------------------------- - @Test - public void array_noneof_evals() { - List list = Lists.newArrayList("a", "z"); - assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(false); - list = Lists.newArrayList("z", "b", "a"); - assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(false); - list = Lists.newArrayList("x", "y", "z"); - assertThat(filter(where("string-arr").noneof(list)).apply(createPredicateContext(json))).isEqualTo(true); - } - //---------------------------------------------------------------------------- // // EXISTS From cbdc9c82e00edb9496f41a4aca3d12ef7ee8321b Mon Sep 17 00:00:00 2001 From: Uladzislau Arlouski Date: Fri, 14 Dec 2018 16:48:22 +0300 Subject: [PATCH 18/21] Add support for pattern flags --- .../internal/filter/FilterCompiler.java | 7 ++- .../jsonpath/internal/filter/PatternFlag.java | 48 +++++++++++++++++++ .../jsonpath/internal/filter/ValueNodes.java | 11 ++--- .../internal/filter/PatternFlagTest.java | 47 ++++++++++++++++++ .../internal/filter/RegexpEvaluatorTest.java | 30 ++++++++---- 5 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java create mode 100644 json-path/src/test/java/com/jayway/jsonpath/internal/filter/PatternFlagTest.java diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java index 107d2364..6fcb5a71 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/FilterCompiler.java @@ -280,8 +280,11 @@ public class FilterCompiler { if (closingIndex == -1) { throw new InvalidPathException("Pattern not closed. Expected " + PATTERN + " in " + filter); } else { - if(filter.inBounds(closingIndex+1) && filter.charAt(closingIndex+1) == IGNORE_CASE){ - closingIndex++; + if(filter.inBounds(closingIndex+1)) { + int equalSignIndex = filter.nextIndexOf('='); + int endIndex = equalSignIndex > closingIndex ? equalSignIndex : filter.nextIndexOfUnescaped(CLOSE_PARENTHESIS); + CharSequence flags = filter.subSequence(closingIndex + 1, endIndex); + closingIndex += flags.length(); } filter.setPosition(closingIndex + 1); } diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java new file mode 100644 index 00000000..3ac05128 --- /dev/null +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/PatternFlag.java @@ -0,0 +1,48 @@ +package com.jayway.jsonpath.internal.filter; + +import java.util.regex.Pattern; + +public enum PatternFlag { + UNIX_LINES(Pattern.UNIX_LINES, 'd'), + CASE_INSENSITIVE(Pattern.CASE_INSENSITIVE, 'i'), + COMMENTS(Pattern.COMMENTS, 'x'), + MULTILINE(Pattern.MULTILINE, 'm'), + DOTALL(Pattern.DOTALL, 's'), + UNICODE_CASE(Pattern.UNICODE_CASE, 'u'), + UNICODE_CHARACTER_CLASS(Pattern.UNICODE_CHARACTER_CLASS, 'U'); + + private final int code; + private final char flag; + + private PatternFlag(int code, char flag) { + this.code = code; + this.flag = flag; + } + + public static int parseFlags(char[] flags) { + int flagsValue = 0; + for (char flag : flags) { + flagsValue |= getCodeByFlag(flag); + } + return flagsValue; + } + + public static String parseFlags(int flags) { + StringBuilder builder = new StringBuilder(); + for (PatternFlag patternFlag : PatternFlag.values()) { + if ((patternFlag.code & flags) == patternFlag.code) { + builder.append(patternFlag.flag); + } + } + return builder.toString(); + } + + private static int getCodeByFlag(char flag) { + for (PatternFlag patternFlag : PatternFlag.values()) { + if (patternFlag.flag == flag) { + return patternFlag.code; + } + } + return 0; + } +} diff --git a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java index 531868db..c4030bc7 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java +++ b/json-path/src/main/java/com/jayway/jsonpath/internal/filter/ValueNodes.java @@ -47,19 +47,22 @@ public interface ValueNodes { class PatternNode extends ValueNode { private final String pattern; private final Pattern compiledPattern; + private final String flags; 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); + int flagsIndex = end + 1; + this.flags = tmp.length() > flagsIndex ? tmp.substring(flagsIndex) : ""; + this.compiledPattern = Pattern.compile(pattern, PatternFlag.parseFlags(flags.toCharArray())); } PatternNode(Pattern pattern) { this.pattern = pattern.pattern(); this.compiledPattern = pattern; + this.flags = PatternFlag.parseFlags(pattern.flags()); } @@ -83,10 +86,6 @@ public interface ValueNodes { @Override public String toString() { - String flags = ""; - if((compiledPattern.flags() & Pattern.CASE_INSENSITIVE) == Pattern.CASE_INSENSITIVE){ - flags = "i"; - } if(!pattern.startsWith("/")){ return "/" + pattern + "/" + flags; } else { diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/filter/PatternFlagTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/filter/PatternFlagTest.java new file mode 100644 index 00000000..c83d80dd --- /dev/null +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/filter/PatternFlagTest.java @@ -0,0 +1,47 @@ +package com.jayway.jsonpath.internal.filter; + +import com.jayway.jsonpath.BaseTest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; + +@RunWith(Parameterized.class) +public class PatternFlagTest extends BaseTest { + + private final int flags; + private final String expectedFlags; + + public PatternFlagTest(int flags, String expectedFlags) { + this.flags = flags; + this.expectedFlags = expectedFlags; + } + + @Test + public void testParseFlags() { + Assert.assertEquals(expectedFlags, PatternFlag.parseFlags(flags)); + } + + @Parameterized.Parameters + public static Iterable data() { + return Arrays.asList( + new Object[][]{ + { 1, "d" }, + { 2, "i" }, + { 4, "x" }, + { 8, "m" }, + { 32, "s" }, + { 64, "u" }, + { 256, "U" }, + { 300, "xmsU" }, + { 13, "dxm" }, + { 7, "dix" }, + { 100, "xsu" }, + { 367, "dixmsuU" } + } + ); + } +} diff --git a/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java b/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java index 614f6b6c..0bb61f1f 100644 --- a/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java +++ b/json-path/src/test/java/com/jayway/jsonpath/internal/filter/RegexpEvaluatorTest.java @@ -49,14 +49,28 @@ public class RegexpEvaluatorTest extends BaseTest { public static Iterable data() { return Arrays.asList( new Object[][]{ - { "/true|false/", createStringNode("true", true), true }, - { "/9.*9/", createNumberNode("9979"), true }, - { "/fa.*se/", createBooleanNode("false"), true }, - { "/Eval.*or/", createClassNode(String.class), false }, - { "/JsonNode/", createJsonNode(json()), false }, - { "/PathNode/", createPathNode(path()), false }, - { "/Undefined/", createUndefinedNode(), false }, - { "/NullNode/", createNullNode(), false } + { "/true|false/", createStringNode("true", true), true }, + { "/9.*9/", createNumberNode("9979"), true }, + { "/fa.*se/", createBooleanNode("false"), true }, + { "/Eval.*or/", createClassNode(String.class), false }, + { "/JsonNode/", createJsonNode(json()), false }, + { "/PathNode/", createPathNode(path()), false }, + { "/Undefined/", createUndefinedNode(), false }, + { "/NullNode/", createNullNode(), false }, + { "/test/i", createStringNode("tEsT", true), true }, + { "/test/", createStringNode("tEsT", true), false }, + { "/\u00de/ui", createStringNode("\u00fe", true), true }, + { "/\u00de/", createStringNode("\u00fe", true), false }, + { "/\u00de/i", createStringNode("\u00fe", true), false }, + { "/test# code/", createStringNode("test", true), false }, + { "/test# code/x", createStringNode("test", true), true }, + { "/.*test.*/d", createStringNode("my\rtest", true), true }, + { "/.*test.*/", createStringNode("my\rtest", true), false }, + { "/.*tEst.*/is", createStringNode("test\ntest", true), true }, + { "/.*tEst.*/i", createStringNode("test\ntest", true), false }, + { "/^\\w+$/U", createStringNode("\u00fe", true), true }, + { "/^\\w+$/", createStringNode("\u00fe", true), false }, + { "/^test$\\ntest$/m", createStringNode("test\ntest", true), true } } ); } From eda48090d2943e4d503275300dd36b21fc0f98c4 Mon Sep 17 00:00:00 2001 From: Document Doctor Date: Wed, 6 Mar 2019 01:06:08 -0500 Subject: [PATCH 19/21] Fixed typos in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbbefe09..90826ee2 100644 --- a/README.md +++ b/README.md @@ -322,7 +322,7 @@ List> books = Path vs Value ------------- -In the Goessner implementation a JsonPath can return either `Path` or `Value`. `Value` is the default and what all the examples above are returning. If you rather have the path of the elements our query is hitting this can be acheived with an option. +In the Goessner implementation a JsonPath can return either `Path` or `Value`. `Value` is the default and what all the examples above are returning. If you rather have the path of the elements our query is hitting this can be achieved with an option. ```java Configuration conf = Configuration.builder() From 9e62af7ec5d694951a8519b9fd29c274f8d32d12 Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Mon, 25 Mar 2019 16:56:36 +0100 Subject: [PATCH 20/21] Added out directory to git ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7fd04a4c..88c91660 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ TODO gradle.properties build bin/ +out/ From 4bdf9b7bdc32d1c36cc39e0dd5300b4599be8987 Mon Sep 17 00:00:00 2001 From: Kalle Stenflo Date: Thu, 28 Mar 2019 19:48:52 +0100 Subject: [PATCH 21/21] bump dependency versions and fix build issues --- build.gradle | 29 ++++++++++++------- .../com/jayway/jsonassert/JsonAsserter.java | 10 +++---- .../jsonpath/matchers/WithJsonPath.java | 2 +- .../com/jayway/jsonpath/Configuration.java | 11 ++----- .../main/java/com/jayway/jsonpath/Filter.java | 2 +- .../java/com/jayway/jsonpath/JsonPath.java | 10 +++---- .../jsonpath/internal/ParseContextImpl.java | 1 + .../internal/function/PathFunction.java | 2 +- .../latebinding/JsonLateBindingValue.java | 2 +- .../latebinding/PathLateBindingValue.java | 3 +- .../internal/path/FunctionPathToken.java | 2 +- .../com/jayway/jsonpath/spi/cache/Cache.java | 5 ++-- .../spi/json/AbstractJsonProvider.java | 1 + .../spi/json/JacksonJsonNodeJsonProvider.java | 2 +- .../spi/json/JacksonJsonProvider.java | 4 +-- .../java/com/jayway/jsonpath/FilterTest.java | 18 ++++++------ .../internal/filter/RegexpEvaluatorTest.java | 3 +- .../jsonpath/internal/function/Issue234.java | 4 +-- .../jayway/jsonpath/old/ArraySlicingTest.java | 2 +- .../com/jayway/jsonpath/old/FilterTest.java | 6 ++-- .../com/jayway/jsonpath/old/IssuesTest.java | 4 +-- .../jayway/jsonpath/old/NullHandlingTest.java | 4 +-- .../old/internal/PropertyPathTokenTest.java | 2 +- system.properties | 2 +- 24 files changed, 66 insertions(+), 65 deletions(-) diff --git a/build.gradle b/build.gradle index 2778e719..52c4963f 100644 --- a/build.gradle +++ b/build.gradle @@ -4,24 +4,31 @@ buildscript { } dependencies { classpath 'me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1' - classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' classpath 'me.champeau.gradle:japicmp-gradle-plugin:0.1.0' + classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3' } } ext { libs = [ - slf4jApi: 'org.slf4j:slf4j-api:1.7.25', - jsonSmart: 'net.minidev:json-smart:2.3', - jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.6.3', - gson: 'com.google.code.gson:gson:2.3.1', - jettison: 'org.codehaus.jettison:jettison:1.3.7', - jsonOrg: 'org.json:json:20140107', - tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.3', + gson: 'com.google.code.gson:gson:2.8.5', hamcrestCore: 'org.hamcrest:hamcrest-core:1.3', hamcrestLibrary: 'org.hamcrest:hamcrest-library:1.3', + jacksonDatabind: 'com.fasterxml.jackson.core:jackson-databind:2.9.8', + jettison: 'org.codehaus.jettison:jettison:1.3.7', + jsonOrg: 'org.json:json:20140107', + jsonSmart: 'net.minidev:json-smart:2.3', + slf4jApi: 'org.slf4j:slf4j-api:1.7.26', + tapestryJson: 'org.apache.tapestry:tapestry-json:5.4.4', - test: ['org.slf4j:slf4j-simple:1.7.16', 'org.assertj:assertj-core:2.1.0', 'commons-io:commons-io:2.4','org.hamcrest:hamcrest-core:1.3', 'org.hamcrest:hamcrest-library:1.3', 'junit:junit:4.12'] + test: [ + 'commons-io:commons-io:2.4', + 'junit:junit:4.12', + 'org.assertj:assertj-core:3.11.1', + 'org.hamcrest:hamcrest-core:1.3', + 'org.hamcrest:hamcrest-library:1.3', + 'org.slf4j:slf4j-simple:1.7.26' + ] ] snapshotVersion = false } @@ -45,8 +52,8 @@ subprojects { apply plugin: 'signing' apply plugin: 'osgi' - sourceCompatibility = 1.6 - targetCompatibility = 1.6 + sourceCompatibility = 1.8 + targetCompatibility = 1.8 repositories { mavenCentral() diff --git a/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java b/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java index 2a152fbb..e420fc9d 100644 --- a/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java +++ b/json-path-assert/src/main/java/com/jayway/jsonassert/JsonAsserter.java @@ -22,11 +22,11 @@ public interface JsonAsserter { JsonAsserter assertThat(String path, Matcher matcher); /** - * @param path - * @param matcher - * @param message - * @param - * @return + * @param path the json path specifying the value being compared + * @param matcher an expression, built of Matchers, specifying allowed values + * @param message the explanation message + * @param the static type that should be returned by the path + * @return this to allow fluent assertion chains */ JsonAsserter assertThat(String path, Matcher matcher, String message); diff --git a/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java index 73acaaad..ca570d33 100644 --- a/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java +++ b/json-path-assert/src/main/java/com/jayway/jsonpath/matchers/WithJsonPath.java @@ -38,7 +38,7 @@ public class WithJsonPath extends TypeSafeMatcher { @Override protected void describeMismatchSafely(ReadContext context, Description mismatchDescription) { try { - T value = jsonPath.read(context.json()); + T value = jsonPath.read(context.jsonString()); mismatchDescription .appendText("json path ") .appendValue(jsonPath.getPath()) diff --git a/json-path/src/main/java/com/jayway/jsonpath/Configuration.java b/json-path/src/main/java/com/jayway/jsonpath/Configuration.java index 1a591ea8..dd59fd70 100644 --- a/json-path/src/main/java/com/jayway/jsonpath/Configuration.java +++ b/json-path/src/main/java/com/jayway/jsonpath/Configuration.java @@ -18,12 +18,7 @@ import com.jayway.jsonpath.internal.DefaultsImpl; import com.jayway.jsonpath.spi.json.JsonProvider; import com.jayway.jsonpath.spi.mapper.MappingProvider; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.Set; +import java.util.*; import static com.jayway.jsonpath.internal.Utils.notNull; import static java.util.Arrays.asList; @@ -142,7 +137,7 @@ public class Configuration { /** * Creates a new configuration with the provided options. Options in this configuration are discarded. * @param options - * @return + * @return the new configuration instance */ public Configuration setOptions(Option... options) { return Configuration.builder().jsonProvider(jsonProvider).mappingProvider(mappingProvider).options(options).evaluationListener(evaluationListeners).build(); @@ -150,7 +145,7 @@ public class Configuration { /** * Returns the options used by this configuration - * @return + * @return the new configuration instance */ public Set