Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 7 additions & 1 deletion src/main/java/io/github/jamsesso/jsonlogic/JsonLogic.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.github.jamsesso.jsonlogic.ast.JsonLogicNode;
import io.github.jamsesso.jsonlogic.ast.JsonLogicParser;
import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluationException;
import io.github.jamsesso.jsonlogic.evaluator.JsonLogicEvaluator;
import io.github.jamsesso.jsonlogic.evaluator.JsonLogicExpression;
import io.github.jamsesso.jsonlogic.evaluator.expressions.*;
Expand Down Expand Up @@ -84,7 +85,12 @@ public Object apply(String json, Object data) throws JsonLogicException {
evaluator = new JsonLogicEvaluator(expressions);
}

return evaluator.evaluate(parseCache.get(json), data, "$");
try {
return evaluator.evaluate(parseCache.get(json), data, "");
} catch (JsonLogicException e) {
e.prependPartialJsonPath("$");
throw e;
}
}

public static boolean truthy(Object value) {
Expand Down
35 changes: 27 additions & 8 deletions src/main/java/io/github/jamsesso/jsonlogic/JsonLogicException.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,47 @@

public class JsonLogicException extends Exception {

private String jsonPath;
private final StringBuilder jsonPath = new StringBuilder();

private JsonLogicException() {
// The default constructor should not be called for exceptions. A reason must be provided.
}

public JsonLogicException(String msg) {
super(msg);
}

public JsonLogicException(String msg, String jsonPath) {
super(msg);
this.jsonPath = jsonPath;
super(msg);
prependPartialJsonPath(jsonPath);
}

public JsonLogicException(Throwable cause) {
super(cause);
}

public JsonLogicException(Throwable cause, String jsonPath) {
super(cause);
this.jsonPath = jsonPath;
super(cause);
prependPartialJsonPath(jsonPath);
}

public JsonLogicException(String msg, Throwable cause) {
super(msg, cause);
}

public JsonLogicException(String msg, Throwable cause, String jsonPath) {
super(msg, cause);
this.jsonPath = jsonPath;
prependPartialJsonPath(jsonPath);
}

public String getJsonPath() {
return jsonPath;
public String getJsonPath() {
return jsonPath.toString();
}

public void prependPartialJsonPath(String partialPath) {
if (partialPath == null) {
return;
}
jsonPath.insert(0, partialPath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ public JsonLogicParseException(Throwable cause, String jsonPath) {
public JsonLogicParseException(String msg, Throwable cause, String jsonPath) {
super(msg, cause, jsonPath);
}

public JsonLogicParseException(String msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ public static JsonLogicNode parse(String json) throws JsonLogicParseException {
try {
return parse(PARSER.parse(json));
}
catch (JsonLogicParseException e) {
e.prependPartialJsonPath("$");
throw e;
}
catch (JsonSyntaxException 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 @@ -56,9 +57,16 @@ private static JsonLogicNode parse(JsonElement root, String jsonPath) throws Jso
JsonArray array = root.getAsJsonArray();
List<JsonLogicNode> elements = new ArrayList<>(array.size());

int index = 0;
for (JsonElement element : array) {
elements.add(parse(element, String.format("%s[%d]", jsonPath, index++)));
for (int index = 0; index < array.size(); index++) {
JsonElement element = array.get(index);
JsonLogicNode arrayNode;
try {
arrayNode = parse(element);
} catch (JsonLogicParseException e) {
e.prependPartialJsonPath("[" + (index) + "]");
throw e;
}
elements.add(arrayNode);
}

return new JsonLogicArray(elements);
Expand All @@ -68,14 +76,21 @@ private static JsonLogicNode parse(JsonElement root, String jsonPath) throws Jso
JsonObject object = root.getAsJsonObject();

if (object.keySet().size() != 1) {
throw new JsonLogicParseException("objects must have exactly 1 key defined, found " + object.keySet().size(), jsonPath);
throw new JsonLogicParseException("objects must have exactly 1 key defined, found " + object.keySet().size());
}

String key = object.keySet().stream().findAny().get();
JsonLogicNode argumentNode = parse(object.get(key), String.format("%s.%s", jsonPath, key));
JsonLogicNode argumentNode;
JsonLogicArray arguments;

// Always coerce single-argument operations into a JsonLogicArray with a single element.
try {
argumentNode = parse(object.get(key));
} catch (JsonLogicParseException e) {
e.prependPartialJsonPath("." + key);
throw e;
}

// Always coerce single-argument operations into a JsonLogicArray with a single element.
if (argumentNode instanceof JsonLogicArray) {
arguments = (JsonLogicArray) argumentNode;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@
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(Throwable cause, String jsonPath) {
super(cause, jsonPath);
}

public JsonLogicEvaluationException(String msg, Throwable cause) {
super(msg, cause);
}

public JsonLogicEvaluationException(String msg, Throwable cause, String jsonPath) {
super(msg, cause, jsonPath);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.jamsesso.jsonlogic.evaluator;

import io.github.jamsesso.jsonlogic.JsonLogicException;
import io.github.jamsesso.jsonlogic.ast.*;
import io.github.jamsesso.jsonlogic.utils.ArrayLike;

Expand All @@ -24,15 +25,31 @@ public JsonLogicEvaluator(Map<String, JsonLogicExpression> expressions) {
this.expressions = Collections.unmodifiableMap(expressions);
}

@Deprecated
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, jsonPath + ".var");
case ARRAY: return evaluate((JsonLogicArray) node, data, jsonPath);
default: return evaluate((JsonLogicOperation) node, data, jsonPath);
}
try {
return evaluate(node, data);
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath(jsonPath);
throw e;
}
}

public Object evaluate(JsonLogicNode node, Object data) throws JsonLogicEvaluationException {
switch (node.getType()) {
case PRIMITIVE: return evaluate((JsonLogicPrimitive) node);
case VARIABLE:
try {
return evaluate((JsonLogicVariable) node, data);
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath(".var");
throw e;
}
case ARRAY: return evaluate((JsonLogicArray) node, data);
default: return evaluate((JsonLogicOperation) node, data);
}
}

public Object evaluate(JsonLogicPrimitive<?> primitive) {
switch (primitive.getPrimitiveType()) {
case NUMBER: return ((JsonLogicNumber) primitive).getValue();
Expand All @@ -42,20 +59,53 @@ public Object evaluate(JsonLogicPrimitive<?> primitive) {
}
}

public Object evaluate(JsonLogicVariable variable, Object data, String jsonPath)
@Deprecated
public Object evaluate(JsonLogicVariable variable, Object data, String jsonPath)
throws JsonLogicEvaluationException {
try {
return evaluate(variable, data);
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath(jsonPath);
throw e;
}
}

public Object evaluate(JsonLogicVariable variable, Object data)
throws JsonLogicEvaluationException {
Object defaultValue = evaluate(variable.getDefaultValue(), null, jsonPath + "[1]");
Object defaultValue;

try {
defaultValue = evaluate(variable.getDefaultValue(), null, "");
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath("[1]");
throw e;
}

if (data == null) {
if (data == null) {
return defaultValue;
}

Object key = evaluate(variable.getKey(), data, jsonPath + "[0]");
Object key;

try {
key = evaluate(variable.getKey(), data, "");
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath("[0]");
throw e;
}

if (key == null) {
return Optional.of(data)
if (key == null) {
Object varValue;
try {
varValue = evaluate(variable.getDefaultValue(), null, "");
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath("[0]");
throw e;
}

return Optional.of(data)
.map(JsonLogicEvaluator::transform)
.orElse(evaluate(variable.getDefaultValue(), null, jsonPath + "[1]"));
.orElse(varValue);
}

if (key instanceof Number) {
Expand Down Expand Up @@ -84,9 +134,14 @@ public Object evaluate(JsonLogicVariable variable, Object data, String jsonPath)
Object result = data;

for (String partial : keys) {
result = evaluatePartialVariable(partial, result, jsonPath + "[0]");

if (result == MISSING) {
try {
result = evaluatePartialVariable(partial, result);
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath("[0]");
throw e;
}

if (result == MISSING) {
return defaultValue;
} else if (result == null) {
return null;
Expand All @@ -96,10 +151,10 @@ public Object evaluate(JsonLogicVariable variable, Object data, String jsonPath)
return result;
}

throw new JsonLogicEvaluationException("var first argument must be null, number, or string", jsonPath + "[0]");
throw new JsonLogicEvaluationException("var first argument must be null, number, or string", "[0]");
}

private Object evaluatePartialVariable(String key, Object data, String jsonPath) throws JsonLogicEvaluationException {
private Object evaluatePartialVariable(String key, Object data) throws JsonLogicEvaluationException {
if (ArrayLike.isEligible(data)) {
ArrayLike list = new ArrayLike(data);
int index;
Expand All @@ -108,7 +163,7 @@ private Object evaluatePartialVariable(String key, Object data, String jsonPath)
index = Integer.parseInt(key);
}
catch (NumberFormatException e) {
throw new JsonLogicEvaluationException(e, jsonPath);
throw new JsonLogicEvaluationException(e);
}

if (index < 0 || index >= list.size()) {
Expand All @@ -130,25 +185,55 @@ private Object evaluatePartialVariable(String key, Object data, String jsonPath)
return null;
}

public List<Object> evaluate(JsonLogicArray array, Object data, String jsonPath) throws JsonLogicEvaluationException {
@Deprecated
public List<Object> evaluate(JsonLogicArray array, Object data, String jsonPath) throws JsonLogicEvaluationException {
try {
return evaluate(array, data);
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath(jsonPath);
throw e;
}
}

public List<Object> evaluate(JsonLogicArray array, Object data) throws JsonLogicEvaluationException {
List<Object> values = new ArrayList<>(array.size());

int index = 0;
for(JsonLogicNode element : array) {
values.add(evaluate(element, data, String.format("%s[%d]", jsonPath, index++)));
for(int index = 0; index < array.size(); index++) {
JsonLogicNode element = array.get(index);
try {
values.add(evaluate(element, data, ""));
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath("[" + index + "]");
throw e;
}
}

return values;
}

@Deprecated
public Object evaluate(JsonLogicOperation operation, Object data, String jsonPath) throws JsonLogicEvaluationException {
try {
return evaluate(operation, data);
} catch (JsonLogicEvaluationException e) {
e.prependPartialJsonPath(jsonPath);
throw e;
}
}

public Object evaluate(JsonLogicOperation operation, Object data) throws JsonLogicEvaluationException {
JsonLogicExpression handler = expressions.get(operation.getOperator());

if (handler == null) {
throw new JsonLogicEvaluationException("Undefined operation '" + operation.getOperator() + "'", jsonPath);
throw new JsonLogicEvaluationException("Undefined operation '" + operation.getOperator() + "'");
}

return handler.evaluate(this, operation.getArguments(), data, String.format("%s.%s", jsonPath, operation.getOperator()));
try {
return handler.evaluate(this, operation.getArguments(), data, "");
} catch (JsonLogicException e) {
e.prependPartialJsonPath("." + operation.getOperator());
throw e;
}
}

public static Object transform(Object value) {
Expand Down
Loading