Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand All @@ -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) {
Expand Down Expand Up @@ -78,21 +79,21 @@ 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;
}
}

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;
Expand All @@ -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()) {
Expand All @@ -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) {
Expand All @@ -145,4 +147,4 @@ public static Object transform(Object value) {

return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
Loading