diff --git a/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java b/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java index 47add88..fa25f2f 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java @@ -57,7 +57,7 @@ public JsonLogic() { public JsonLogic addOperation(String name, Function<Object[], Object> function) { return addOperation(new PreEvaluatedArgumentsExpression() { @Override - public Object evaluate(List arguments, Object data) { + public Object evaluate(List arguments, Object data, String jsonPath) { return function.apply(arguments.toArray()); } @@ -84,7 +84,7 @@ public Object apply(String json, Object data) throws JsonLogicException { evaluator = new JsonLogicEvaluator(expressions); } - return evaluator.evaluate(parseCache.get(json), data); + return evaluator.evaluate(parseCache.get(json), data, "$"); } public static boolean truthy(Object value) { diff --git a/src/main/java/io/github/jamsesso/jsonlogic/JsonLogicException.java b/src/main/java/io/github/jamsesso/jsonlogic/JsonLogicException.java index 0c03d25..71b961f 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/JsonLogicException.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/JsonLogicException.java @@ -1,19 +1,24 @@ package io.github.jamsesso.jsonlogic; public class JsonLogicException extends Exception { + + private String jsonPath; + private JsonLogicException() { // The default constructor should not be called for exceptions. A reason must be provided. } - public JsonLogicException(String msg) { + public JsonLogicException(String msg, String jsonPath) { super(msg); + this.jsonPath = jsonPath; } - public JsonLogicException(Throwable cause) { + public JsonLogicException(Throwable cause, String jsonPath) { super(cause); + this.jsonPath = jsonPath; } - public JsonLogicException(String msg, Throwable cause) { - super(msg, cause); + public String getJsonPath() { + return jsonPath; } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParseException.java b/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParseException.java index ff43164..1df20ec 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParseException.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParseException.java @@ -3,15 +3,11 @@ import io.github.jamsesso.jsonlogic.JsonLogicException; public class JsonLogicParseException extends JsonLogicException { - public JsonLogicParseException(String msg) { - super(msg); + public JsonLogicParseException(String msg, String jsonPath) { + super(msg, jsonPath); } - public JsonLogicParseException(Throwable cause) { - super(cause); - } - - public JsonLogicParseException(String msg, Throwable cause) { - super(msg, cause); + public JsonLogicParseException(Throwable cause, String jsonPath) { + super(cause, jsonPath); } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParser.java b/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParser.java index 21cfba7..bf442b8 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParser.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/ast/JsonLogicParser.java @@ -18,11 +18,14 @@ public static JsonLogicNode parse(String json) throws JsonLogicParseException { return parse(PARSER.parse(json)); } catch (JsonSyntaxException e) { - throw new JsonLogicParseException(e); + throw new JsonLogicParseException(e, "$"); } } private static JsonLogicNode parse(JsonElement root) throws JsonLogicParseException { + return parse(root, "$"); + } + private static JsonLogicNode parse(JsonElement root, String jsonPath) throws JsonLogicParseException { // Handle null if (root.isJsonNull()) { return JsonLogicNull.NULL; @@ -53,8 +56,9 @@ private static JsonLogicNode parse(JsonElement root) throws JsonLogicParseExcept JsonArray array = root.getAsJsonArray(); List<JsonLogicNode> elements = new ArrayList<>(array.size()); + int index = 0; for (JsonElement element : array) { - elements.add(parse(element)); + elements.add(parse(element, String.format("%s[%d]", jsonPath, index++))); } return new JsonLogicArray(elements); @@ -64,11 +68,11 @@ private static JsonLogicNode parse(JsonElement root) throws JsonLogicParseExcept JsonObject object = root.getAsJsonObject(); if (object.keySet().size() != 1) { - throw new JsonLogicParseException("objects must have exactly 1 key defined, found " + object.keySet().size()); + throw new JsonLogicParseException("objects must have exactly 1 key defined, found " + object.keySet().size(), jsonPath); } String key = object.keySet().stream().findAny().get(); - JsonLogicNode argumentNode = parse(object.get(key)); + JsonLogicNode argumentNode = parse(object.get(key), String.format("%s.%s", jsonPath, key)); JsonLogicArray arguments; // Always coerce single-argument operations into a JsonLogicArray with a single element. diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluationException.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluationException.java index 20394f9..2fcf35d 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluationException.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluationException.java @@ -3,15 +3,11 @@ import io.github.jamsesso.jsonlogic.JsonLogicException; public class JsonLogicEvaluationException extends JsonLogicException { - public JsonLogicEvaluationException(String msg) { - super(msg); + public JsonLogicEvaluationException(String msg, String jsonPath) { + super(msg, jsonPath); } - public JsonLogicEvaluationException(Throwable cause) { - super(cause); - } - - public JsonLogicEvaluationException(String msg, Throwable cause) { - super(msg, cause); + public JsonLogicEvaluationException(Throwable cause, String jsonPath) { + super(cause, jsonPath); } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluator.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluator.java index 804ee1e..d78892b 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluator.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicEvaluator.java @@ -20,12 +20,12 @@ public JsonLogicEvaluator(Map<String, JsonLogicExpression> expressions) { this.expressions = Collections.unmodifiableMap(expressions); } - public Object evaluate(JsonLogicNode node, Object data) throws JsonLogicEvaluationException { + public Object evaluate(JsonLogicNode node, Object data, String jsonPath) throws JsonLogicEvaluationException { switch (node.getType()) { case PRIMITIVE: return evaluate((JsonLogicPrimitive) node); - case VARIABLE: return evaluate((JsonLogicVariable) node, data); - case ARRAY: return evaluate((JsonLogicArray) node, data); - default: return evaluate((JsonLogicOperation) node, data); + case VARIABLE: return evaluate((JsonLogicVariable) node, data, jsonPath + ".var"); + case ARRAY: return evaluate((JsonLogicArray) node, data, jsonPath); + default: return evaluate((JsonLogicOperation) node, data, jsonPath); } } @@ -38,19 +38,20 @@ public Object evaluate(JsonLogicPrimitive<?> primitive) { } } - public Object evaluate(JsonLogicVariable variable, Object data) throws JsonLogicEvaluationException { - Object defaultValue = evaluate(variable.getDefaultValue(), null); + public Object evaluate(JsonLogicVariable variable, Object data, String jsonPath) + throws JsonLogicEvaluationException { + Object defaultValue = evaluate(variable.getDefaultValue(), null, jsonPath + "[1]"); if (data == null) { return defaultValue; } - Object key = evaluate(variable.getKey(), data); + Object key = evaluate(variable.getKey(), data, jsonPath + "[0]"); if (key == null) { return Optional.of(data) .map(JsonLogicEvaluator::transform) - .orElse(evaluate(variable.getDefaultValue(), null)); + .orElse(evaluate(variable.getDefaultValue(), null, jsonPath + "[1]")); } if (key instanceof Number) { @@ -78,10 +79,10 @@ public Object evaluate(JsonLogicVariable variable, Object data) throws JsonLogic String[] keys = name.split("\\."); Object result = data; - for(String partial : keys) { - result = evaluatePartialVariable(partial, result); + for (String partial : keys) { + result = evaluatePartialVariable(partial, result, jsonPath + "[0]"); - if(result == null) { + if (result == null) { return defaultValue; } } @@ -89,10 +90,10 @@ public Object evaluate(JsonLogicVariable variable, Object data) throws JsonLogic return result; } - throw new JsonLogicEvaluationException("var first argument must be null, number, or string"); + throw new JsonLogicEvaluationException("var first argument must be null, number, or string", jsonPath + "[0]"); } - private Object evaluatePartialVariable(String key, Object data) throws JsonLogicEvaluationException { + private Object evaluatePartialVariable(String key, Object data, String jsonPath) throws JsonLogicEvaluationException { if (ArrayLike.isEligible(data)) { ArrayLike list = new ArrayLike(data); int index; @@ -101,7 +102,7 @@ private Object evaluatePartialVariable(String key, Object data) throws JsonLogic index = Integer.parseInt(key); } catch (NumberFormatException e) { - throw new JsonLogicEvaluationException(e); + throw new JsonLogicEvaluationException(e, jsonPath); } if (index < 0 || index >= list.size()) { @@ -118,24 +119,25 @@ private Object evaluatePartialVariable(String key, Object data) throws JsonLogic return null; } - public List<Object> evaluate(JsonLogicArray array, Object data) throws JsonLogicEvaluationException { + public List<Object> evaluate(JsonLogicArray array, Object data, String jsonPath) throws JsonLogicEvaluationException { List<Object> values = new ArrayList<>(array.size()); + int index = 0; for(JsonLogicNode element : array) { - values.add(evaluate(element, data)); + values.add(evaluate(element, data, String.format("%s[%d]", jsonPath, index++))); } return values; } - public Object evaluate(JsonLogicOperation operation, Object data) throws JsonLogicEvaluationException { + public Object evaluate(JsonLogicOperation operation, Object data, String jsonPath) throws JsonLogicEvaluationException { JsonLogicExpression handler = expressions.get(operation.getOperator()); if (handler == null) { - throw new JsonLogicEvaluationException("Undefined operation '" + operation.getOperator() + "'"); + throw new JsonLogicEvaluationException("Undefined operation '" + operation.getOperator() + "'", jsonPath); } - return handler.evaluate(this, operation.getArguments(), data); + return handler.evaluate(this, operation.getArguments(), data, String.format("%s.%s", jsonPath, operation.getOperator())); } public static Object transform(Object value) { @@ -145,4 +147,4 @@ public static Object transform(Object value) { return value; } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicExpression.java index 7c267bb..0f1381b 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/JsonLogicExpression.java @@ -5,6 +5,6 @@ public interface JsonLogicExpression { String key(); - Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException; } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/AllExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/AllExpression.java index 711387a..2e9350f 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/AllExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/AllExpression.java @@ -20,20 +20,20 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 2) { - throw new JsonLogicEvaluationException("all expects exactly 2 arguments"); + throw new JsonLogicEvaluationException("all expects exactly 2 arguments", jsonPath); } - Object maybeArray = evaluator.evaluate(arguments.get(0), data); + Object maybeArray = evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]"); if (maybeArray == null) { return false; } if (!ArrayLike.isEligible(maybeArray)) { - throw new JsonLogicEvaluationException("first argument to all must be a valid array"); + throw new JsonLogicEvaluationException("first argument to all must be a valid array", jsonPath); } ArrayLike array = new ArrayLike(maybeArray); @@ -42,8 +42,9 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O return false; } + int index = 1; for (Object item : array) { - if(!JsonLogic.truthy(evaluator.evaluate(arguments.get(1), item))) { + if(!JsonLogic.truthy(evaluator.evaluate(arguments.get(1), item, String.format("%s[%d]", jsonPath, index)))) { return false; } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ArrayHasExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ArrayHasExpression.java index 44d3bb5..7d9bb14 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ArrayHasExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ArrayHasExpression.java @@ -23,13 +23,13 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 2) { - throw new JsonLogicEvaluationException("some expects exactly 2 arguments"); + throw new JsonLogicEvaluationException(key() + " expects exactly 2 arguments", jsonPath); } - Object maybeArray = evaluator.evaluate(arguments.get(0), data); + Object maybeArray = evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]"); // Array objects can have null values according to http://jsonlogic.com/ if (maybeArray == null) { @@ -41,15 +41,15 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O } if (!ArrayLike.isEligible(maybeArray)) { - throw new JsonLogicEvaluationException("first argument to some must be a valid array"); + throw new JsonLogicEvaluationException("first argument to " + key() + " must be a valid array", jsonPath + "[0]"); } for (Object item : new ArrayLike(maybeArray)) { - if(JsonLogic.truthy(evaluator.evaluate(arguments.get(1), item))) { + if(JsonLogic.truthy(evaluator.evaluate(arguments.get(1), item, jsonPath + "[1]"))) { return isSome; } } return !isSome; } -} \ No newline at end of file +} diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ConcatenateExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ConcatenateExpression.java index 81dd180..1ef7651 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ConcatenateExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ConcatenateExpression.java @@ -18,7 +18,7 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { return arguments.stream() .map(obj -> { if (obj instanceof Double && obj.toString().endsWith(".0")) { diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/EqualityExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/EqualityExpression.java index efde9ef..0262ba1 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/EqualityExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/EqualityExpression.java @@ -18,9 +18,9 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 2) { - throw new JsonLogicEvaluationException("equality expressions expect exactly 2 arguments"); + throw new JsonLogicEvaluationException("equality expressions expect exactly 2 arguments", jsonPath); } Object left = arguments.get(0); diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/FilterExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/FilterExpression.java index 520c06e..79364e9 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/FilterExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/FilterExpression.java @@ -23,22 +23,22 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 2) { - throw new JsonLogicEvaluationException("filter expects exactly 2 arguments"); + throw new JsonLogicEvaluationException("filter expects exactly 2 arguments", jsonPath); } - Object maybeArray = evaluator.evaluate(arguments.get(0), data); + Object maybeArray = evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]"); if (!ArrayLike.isEligible(maybeArray)) { - throw new JsonLogicEvaluationException("first argument to filter must be a valid array"); + throw new JsonLogicEvaluationException("first argument to filter must be a valid array", jsonPath + "[0]"); } List<Object> result = new ArrayList<>(); for (Object item : new ArrayLike(maybeArray)) { - if(JsonLogic.truthy(evaluator.evaluate(arguments.get(1), item))) { + if(JsonLogic.truthy(evaluator.evaluate(arguments.get(1), item, jsonPath + "[1]"))) { result.add(item); } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/IfExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/IfExpression.java index 165c6b7..230a636 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/IfExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/IfExpression.java @@ -23,7 +23,8 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, + String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() < 1) { return null; @@ -31,13 +32,13 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O // If there is only a single argument, simply evaluate & return that argument. if (arguments.size() == 1) { - return evaluator.evaluate(arguments.get(0), data); + return evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]"); } // If there is 2 arguments, only evaluate the second argument if the first argument is truthy. if (arguments.size() == 2) { - return JsonLogic.truthy(evaluator.evaluate(arguments.get(0), data)) - ? evaluator.evaluate(arguments.get(1), data) + return JsonLogic.truthy(evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]")) + ? evaluator.evaluate(arguments.get(1), data, jsonPath + "[1]") : null; } @@ -45,8 +46,8 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O JsonLogicNode condition = arguments.get(i); JsonLogicNode resultIfTrue = arguments.get(i + 1); - if (JsonLogic.truthy(evaluator.evaluate(condition, data))) { - return evaluator.evaluate(resultIfTrue, data); + if (JsonLogic.truthy(evaluator.evaluate(condition, data, String.format("%s[%d]", jsonPath, i)))) { + return evaluator.evaluate(resultIfTrue, data, String.format("%s[%d]", jsonPath, i + 1)); } } @@ -54,6 +55,6 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O return null; } - return evaluator.evaluate(arguments.get(arguments.size() - 1), data); + return evaluator.evaluate(arguments.get(arguments.size() - 1), data, String.format("%s[%d]", jsonPath, arguments.size() - 1)); } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InExpression.java index 848adf0..e5f6d0b 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InExpression.java @@ -18,7 +18,7 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() < 2) { return false; } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InequalityExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InequalityExpression.java index a0ae49a..747afb0 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InequalityExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/InequalityExpression.java @@ -20,9 +20,9 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { - boolean result = (boolean) delegate.evaluate(evaluator, arguments, data); + boolean result = (boolean) delegate.evaluate(evaluator, arguments, data, jsonPath); return !result; } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogExpression.java index e82c261..43332c5 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogExpression.java @@ -20,9 +20,9 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.isEmpty()) { - throw new JsonLogicEvaluationException("log operator requires exactly 1 argument"); + throw new JsonLogicEvaluationException("log operator requires exactly 1 argument", jsonPath); } Object value = arguments.get(0); diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogicExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogicExpression.java index 0397b14..02a8f28 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogicExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/LogicExpression.java @@ -23,16 +23,17 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() < 1) { - throw new JsonLogicEvaluationException("and operator expects at least 1 argument"); + throw new JsonLogicEvaluationException(key() + " operator expects at least 1 argument", jsonPath); } Object result = null; + int index = 0; for (JsonLogicNode element : arguments) { - result = evaluator.evaluate(element, data); + result = evaluator.evaluate(element, data, String.format("%s[%d]", jsonPath, index)); if ((isAnd && !JsonLogic.truthy(result)) || (!isAnd && JsonLogic.truthy(result))) { return result; diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MapExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MapExpression.java index f046ede..9c3a863 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MapExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MapExpression.java @@ -23,13 +23,13 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 2) { - throw new JsonLogicEvaluationException("map expects exactly 2 arguments"); + throw new JsonLogicEvaluationException("map expects exactly 2 arguments", jsonPath); } - Object maybeArray = evaluator.evaluate(arguments.get(0), data); + Object maybeArray = evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]"); if (!ArrayLike.isEligible(maybeArray)) { return Collections.emptyList(); @@ -38,7 +38,7 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O List<Object> result = new ArrayList<>(); for (Object item : new ArrayLike(maybeArray)) { - result.add(evaluator.evaluate(arguments.get(1), item)); + result.add(evaluator.evaluate(arguments.get(1), item, jsonPath + "[1]")); } return result; diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MathExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MathExpression.java index a077a54..4703715 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MathExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MathExpression.java @@ -1,6 +1,7 @@ package io.github.jamsesso.jsonlogic.evaluator.expressions; import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException; +import io.github.jamsesso.jsonlogic.utils.ArrayLike; import java.util.List; import java.util.function.BiFunction; @@ -34,36 +35,22 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.isEmpty()) { return null; } - if (arguments.size() == 1) { - if (key.equals("+") && arguments.get(0) instanceof String) { - try { - return Double.parseDouble((String) arguments.get(0)); - } - catch (NumberFormatException e) { - throw new JsonLogicEvaluationException(e); - } - } - - if (key.equals("-") && arguments.get(0) instanceof Number) { - return -1 * ((Number) arguments.get(0)).doubleValue(); - } - - if (key.equals("/")) { - return null; - } - } - // Collect all of the arguments double[] values = new double[arguments.size()]; for (int i = 0; i < arguments.size(); i++) { Object value = arguments.get(i); + if (key.equals("*") || key.equals("+")) { + while (ArrayLike.isEligible(value)) { + value = new ArrayLike(value).get(0); + } + } if (value instanceof String) { try { values[i] = Double.parseDouble((String) value); @@ -80,6 +67,16 @@ else if (!(value instanceof Number)) { } } + if (values.length == 1) { + if (key.equals("-")) { + return -values[0]; + } + + if (key.equals("/")) { + return null; + } + } + // Reduce the values into a single result double accumulator = values[0]; diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MergeExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MergeExpression.java index ba34d9b..a07d005 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MergeExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MergeExpression.java @@ -22,11 +22,10 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { return ((List<Object>) arguments).stream() .map(obj -> ArrayLike.isEligible(obj) ? new ArrayLike(obj) : Collections.singleton(obj)) - .map(Collection::stream) - .flatMap(Function.identity()) + .flatMap(Collection::stream) .collect(Collectors.toList()); } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MissingExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MissingExpression.java index 2e073bd..a03a616 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MissingExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/MissingExpression.java @@ -22,10 +22,10 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { - if (isSome && (!ArrayLike.isEligible(arguments.get(1)) || !(arguments.get(0) instanceof Double))) { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { + if (isSome && (arguments.size() < 2 || !ArrayLike.isEligible(arguments.get(1)) || !(arguments.get(0) instanceof Double))) { throw new JsonLogicEvaluationException("missing_some expects first argument to be an integer and the second " + - "argument to be an array"); + "argument to be an array", jsonPath); } if (!MapLike.isEligible(data)) { diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NotExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NotExpression.java index 2fa0e72..e653129 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NotExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NotExpression.java @@ -20,7 +20,7 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) { + public Object evaluate(List arguments, Object data, String jsonPath) { boolean result; if (arguments.isEmpty()) { diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NumericComparisonExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NumericComparisonExpression.java index 897817d..98c98a4 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NumericComparisonExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/NumericComparisonExpression.java @@ -22,12 +22,12 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { // Convert the arguments to doubles int n = Math.min(arguments.size(), 3); if (n < 2) { - throw new JsonLogicEvaluationException("'" + key + "' requires at least 2 arguments"); + throw new JsonLogicEvaluationException("'" + key + "' requires at least 2 arguments", jsonPath); } double[] values = new double[n]; @@ -51,8 +51,8 @@ else if (!(value instanceof Number)) { } } - // Handle between comparisons (supported for < and <=) - if (arguments.size() == 3) { + // Handle between comparisons + if (arguments.size() >= 3) { switch (key) { case "<": return values[0] < values[1] && values[1] < values[2]; @@ -60,8 +60,14 @@ else if (!(value instanceof Number)) { case "<=": return values[0] <= values[1] && values[1] <= values[2]; + case ">": + return values[0] > values[1] && values[1] > values[2]; + + case ">=": + return values[0] >= values[1] && values[1] >= values[2]; + default: - throw new JsonLogicEvaluationException("'" + key + "' does not support between comparisons"); + throw new JsonLogicEvaluationException("'" + key + "' does not support between comparisons", jsonPath); } } @@ -80,7 +86,7 @@ else if (!(value instanceof Number)) { return values[0] >= values[1]; default: - throw new JsonLogicEvaluationException("'" + key + "' is not a comparison expression"); + throw new JsonLogicEvaluationException("'" + key + "' is not a comparison expression", jsonPath); } } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/PreEvaluatedArgumentsExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/PreEvaluatedArgumentsExpression.java index 4c53896..26adfe4 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/PreEvaluatedArgumentsExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/PreEvaluatedArgumentsExpression.java @@ -9,17 +9,17 @@ import java.util.List; public interface PreEvaluatedArgumentsExpression extends JsonLogicExpression { - Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException; + Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException; @Override - default Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + default Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { - List<Object> values = evaluator.evaluate(arguments, data); + List<Object> values = evaluator.evaluate(arguments, data, jsonPath); if (values.size() == 1 && ArrayLike.isEligible(values.get(0))) { values = new ArrayLike(values.get(0)); } - return evaluate(values, data); + return evaluate(values, data, jsonPath); } } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ReduceExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ReduceExpression.java index 7864cc5..35bb882 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ReduceExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/ReduceExpression.java @@ -22,14 +22,14 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 3) { - throw new JsonLogicEvaluationException("reduce expects exactly 3 arguments"); + throw new JsonLogicEvaluationException("reduce expects exactly 3 arguments", jsonPath); } - Object maybeArray = evaluator.evaluate(arguments.get(0), data); - Object accumulator = evaluator.evaluate(arguments.get(2), data); + Object maybeArray = evaluator.evaluate(arguments.get(0), data, jsonPath + "[0]"); + Object accumulator = evaluator.evaluate(arguments.get(2), data, jsonPath + "[2]"); if (!ArrayLike.isEligible(maybeArray)) { return accumulator; @@ -40,7 +40,7 @@ public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, O for (Object item : new ArrayLike(maybeArray)) { context.put("current", item); - context.put("accumulator", evaluator.evaluate(arguments.get(1), context)); + context.put("accumulator", evaluator.evaluate(arguments.get(1), context, jsonPath + "[1]")); } return context.get("accumulator"); diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictEqualityExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictEqualityExpression.java index fcf2325..8cbfc04 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictEqualityExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictEqualityExpression.java @@ -17,9 +17,9 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() != 2) { - throw new JsonLogicEvaluationException("equality expressions expect exactly 2 arguments"); + throw new JsonLogicEvaluationException("equality expressions expect exactly 2 arguments", jsonPath); } Object left = arguments.get(0); diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictInequalityExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictInequalityExpression.java index 41d71ee..68e257c 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictInequalityExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/StrictInequalityExpression.java @@ -21,9 +21,9 @@ public String key() { } @Override - public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data) + public Object evaluate(JsonLogicEvaluator evaluator, JsonLogicArray arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { - boolean result = (boolean) delegate.evaluate(evaluator, arguments, data); + boolean result = (boolean) delegate.evaluate(evaluator, arguments, data, jsonPath); return !result; } diff --git a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/SubstringExpression.java b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/SubstringExpression.java index f9f844f..6fc8b7b 100644 --- a/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/SubstringExpression.java +++ b/src/main/java/io/github/jamsesso/jsonlogic/evaluator/expressions/SubstringExpression.java @@ -17,13 +17,13 @@ public String key() { } @Override - public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationException { + public Object evaluate(List arguments, Object data, String jsonPath) throws JsonLogicEvaluationException { if (arguments.size() < 2 || arguments.size() > 3) { - throw new JsonLogicEvaluationException("substr expects 2 or 3 arguments"); + throw new JsonLogicEvaluationException("substr expects 2 or 3 arguments", jsonPath); } if (!(arguments.get(1) instanceof Double)) { - throw new JsonLogicEvaluationException("first argument to substr must be a number"); + throw new JsonLogicEvaluationException("second argument to substr must be a number", jsonPath + "[1]"); } String value = arguments.get(0).toString(); @@ -44,7 +44,7 @@ public Object evaluate(List arguments, Object data) throws JsonLogicEvaluationEx } else { if (!(arguments.get(2) instanceof Double)) { - throw new JsonLogicEvaluationException("second argument to substr must be an integer"); + throw new JsonLogicEvaluationException("third argument to substr must be an integer", jsonPath + "[2]"); } startIndex = ((Double) arguments.get(1)).intValue(); diff --git a/src/test/java/io/github/jamsesso/jsonlogic/MathExpressionTests.java b/src/test/java/io/github/jamsesso/jsonlogic/MathExpressionTests.java index 8d6ea8d..b7a356b 100644 --- a/src/test/java/io/github/jamsesso/jsonlogic/MathExpressionTests.java +++ b/src/test/java/io/github/jamsesso/jsonlogic/MathExpressionTests.java @@ -3,6 +3,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; public class MathExpressionTests { private static final JsonLogic jsonLogic = new JsonLogic(); @@ -31,6 +32,21 @@ public void testSingleAdd() throws JsonLogicException { assertEquals(3.14, result); } + @Test + public void testAddWithArray() throws JsonLogicException { + String json = "{\"+\":[2,[[3,4],5]]}"; + Object result = jsonLogic.apply(json, null); + + assertEquals(5.0, result); // This matches reference impl at jsonlogic.com + } + + @Test + public void testStringAdd() throws JsonLogicException { + assertNull(jsonLogic.apply("{\"+\" : \"foo\"}", null)); + assertNull(jsonLogic.apply("{\"+\" : [\"foo\"]}", null)); + assertNull(jsonLogic.apply("{\"+\" : [1, \"foo\"]}", null)); + } + @Test public void testSubtract() throws JsonLogicException { String json = "{\"-\":[4,2]}"; @@ -47,6 +63,14 @@ public void testSingleSubtract() throws JsonLogicException { assertEquals(-2.0, result); } + @Test + public void testSingleSubtractString() throws JsonLogicException { + String json = "{\"-\": \"2\" }"; + Object result = jsonLogic.apply(json, null); + + assertEquals(-2.0, result); + } + @Test public void testMultiply() throws JsonLogicException { String json = "{\"*\":[4,2]}"; @@ -63,6 +87,14 @@ public void testMultiMultiply() throws JsonLogicException { assertEquals(32.0, result); } + @Test + public void testMultiplyWithArray() throws JsonLogicException { + String json = "{\"*\":[2,[[3, 4], 5]]}"; + Object result = jsonLogic.apply(json, null); + + assertEquals(6.0, result); // This matches reference impl at jsonlogic.com + } + @Test public void testDivide() throws JsonLogicException { String json = "{\"/\":[4,2]}"; diff --git a/src/test/java/io/github/jamsesso/jsonlogic/NumericComparisonExpressionTests.java b/src/test/java/io/github/jamsesso/jsonlogic/NumericComparisonExpressionTests.java index f1cbf0a..d4633b1 100644 --- a/src/test/java/io/github/jamsesso/jsonlogic/NumericComparisonExpressionTests.java +++ b/src/test/java/io/github/jamsesso/jsonlogic/NumericComparisonExpressionTests.java @@ -54,4 +54,26 @@ public void testBetweenInclusive() throws JsonLogicException { assertEquals(true, result); } + + @Test + public void testGtBetweenExclusive() throws JsonLogicException { + String json = "{\">\" : [3, 2, 1]}"; + Object result = jsonLogic.apply(json, null); + + assertEquals(true, result); + } + + @Test + public void testGtBetweenInclusive() throws JsonLogicException { + String json = "{\">=\" : [3, 1, 1]}"; + Object result = jsonLogic.apply(json, null); + + assertEquals(true, result); + } + + @Test + public void testEdgeCases() throws JsonLogicException { + assertEquals(true, jsonLogic.apply("{\">=\" : [3, 1, 1, 1]}", null)); + assertEquals(false, jsonLogic.apply("{\">=\" : [3, 1, 3, 1]}", null)); + } } diff --git a/src/test/java/io/github/jamsesso/jsonlogic/ParseErrorTests.java b/src/test/java/io/github/jamsesso/jsonlogic/ParseErrorTests.java new file mode 100644 index 0000000..47882d2 --- /dev/null +++ b/src/test/java/io/github/jamsesso/jsonlogic/ParseErrorTests.java @@ -0,0 +1,80 @@ +package io.github.jamsesso.jsonlogic; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class ParseErrorTests { + private static final JsonLogic jsonLogic = new JsonLogic(); + private static final Map<String, Object> empty = new HashMap<>(); + private static final Map<String, Object> arrayData = Collections.singletonMap("key", + Stream.of(1, 2).collect(Collectors.toList())); + + private void testPath(String expression, String jsonPath, String msg) { + testPath(expression, jsonPath, msg, empty); + } + private void testPath(String expression, String jsonPath, String msg, Map<String, Object> data) { + try { + jsonLogic.apply(expression, data); + fail("Expected an exception at " + jsonPath); + } catch (JsonLogicException e) { + assertEquals(e.getMessage(), jsonPath, e.getJsonPath()); + assertEquals(msg, e.getMessage()); + } + } + + @Test + public void testJsonPath() { + testPath("{}", "$", "objects must have exactly 1 key defined, found 0"); + testPath("{\"all\":[1]}","$.all","all expects exactly 2 arguments"); + testPath("{\"all\":[{\"some\":[]}, 1]}","$.all[0].some","some expects exactly 2 arguments"); + testPath("{\"all\":[[1], {\"none\":[]}]}","$.all[1].none","none expects exactly 2 arguments"); + testPath("{\"some\":[0, 1]}","$.some[0]","first argument to some must be a valid array"); + testPath("{\"none\":[1, 2]}","$.none[0]","first argument to none must be a valid array"); + testPath("{\"cat\":[\"foo\", {}]}","$.cat[1]","objects must have exactly 1 key defined, found 0"); + testPath("{\"==\":[0]}","$.==","equality expressions expect exactly 2 arguments"); + testPath("{\"filter\":[0]}","$.filter","filter expects exactly 2 arguments"); + testPath("{\"filter\":[1, 2]}","$.filter[0]","first argument to filter must be a valid array"); + testPath("{\"filter\":[\"foo\", {}]}","$.filter[1]","objects must have exactly 1 key defined, found 0"); + testPath("{\"if\":[{}]}","$.if[0]","objects must have exactly 1 key defined, found 0"); + testPath("{\"if\":[true,{}]}","$.if[1]","objects must have exactly 1 key defined, found 0"); + testPath("{\"if\":[false,1,{\"filter\":[1]}]}","$.if[2].filter","filter expects exactly 2 arguments"); + testPath("{\"if\":[false,1,true,{\"filter\":[1]}]}","$.if[3].filter","filter expects exactly 2 arguments"); + testPath("{\"!=\":[0]}","$.!=","equality expressions expect exactly 2 arguments"); + testPath("{\"log\":[]}","$.log","log operator requires exactly 1 argument"); + testPath("{\"and\":[]}","$.and","and operator expects at least 1 argument"); + testPath("{\"or\":[]}","$.or","or operator expects at least 1 argument"); + testPath("{\"map\":[1]}","$.map","map expects exactly 2 arguments"); + testPath("{\"+\":[1, {\"-\": [2, {\"*\": [3, {\"/\": [4, {\"log\":[]}]}]}]}]}","$.+[1].-[1].*[1]./[1].log","log operator requires exactly 1 argument"); + testPath("{\"%\":[1, {\"min\": [2, {\"max\": [3, {\"log\":[]}]}]}]}","$.%[1].min[1].max[1].log","log operator requires exactly 1 argument"); + testPath("{\"merge\":[1, 2, {\"log\":[]}]}","$.merge[2].log","log operator requires exactly 1 argument"); + testPath("{\"missing_some\":[1]}","$.missing_some","missing_some expects first argument to be an integer and the second argument to be an array"); + testPath("{\"missing_some\":[1, 2]}","$.missing_some","missing_some expects first argument to be an integer and the second argument to be an array"); + testPath("{\"missing\":[1, {\"log\":[]}]}","$.missing[1].log","log operator requires exactly 1 argument"); + testPath("{\"!\":[1, {\"log\":[]}]}","$.![1].log","log operator requires exactly 1 argument"); + testPath("{\"!!\":[{\"log\":[]}]}","$.!![0].log","log operator requires exactly 1 argument"); + testPath("{\"<\":[1]}","$.<","'<' requires at least 2 arguments"); + testPath("{\">\":[1]}","$.>","'>' requires at least 2 arguments"); + testPath("{\"<=\":[1]}","$.<=","'<=' requires at least 2 arguments"); + testPath("{\">=\":[1]}","$.>=","'>=' requires at least 2 arguments"); + testPath("{\">\":[1, {\"log\":[]}]}","$.>[1].log","log operator requires exactly 1 argument"); + testPath("{\">\":[0, 1, 2, \"three\", 4, 5, {\"log\":[]}]}","$.>[6].log","log operator requires exactly 1 argument"); + testPath("{\"reduce\":[1]}","$.reduce","reduce expects exactly 3 arguments"); + testPath("{\"reduce\":[{\"log\":[]}, 2, 3]}","$.reduce[0].log","log operator requires exactly 1 argument"); + testPath("{\"reduce\":[[1], {\"log\":[]}, 3]}","$.reduce[1].log","log operator requires exactly 1 argument"); + testPath("{\"reduce\":[1, 2, {\"log\":[]}]}","$.reduce[2].log","log operator requires exactly 1 argument"); + testPath("{\"===\":[1]}","$.===","equality expressions expect exactly 2 arguments"); + testPath("{\"!==\":[1]}","$.!==","equality expressions expect exactly 2 arguments"); + testPath("{\"substr\":[\"jsonlogic\"]}","$.substr","substr expects 2 or 3 arguments"); + testPath("{\"substr\":[\"jsonlogic\", \"one\"]}","$.substr[1]","second argument to substr must be a number"); + testPath("{\"substr\":[\"jsonlogic\", 1, \"two\"]}","$.substr[2]","third argument to substr must be an integer"); + testPath("{\"var\":[[1, 2]]}","$.var[0]","var first argument must be null, number, or string"); + testPath("{\"var\":\"key.foo\"}","$.var[0]","java.lang.NumberFormatException: For input string: \"foo\"", arrayData); + } +}