Skip to content

Commit a05bfaf

Browse files
committed
support for nested fields
1 parent 088496b commit a05bfaf

File tree

5 files changed

+63
-28
lines changed

5 files changed

+63
-28
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Parentheses, `(` and `)`, can be used for grouping.
4747
#### Usage Notes
4848
* Phrases that includes quotes, like `content = "It's a wonderful day"`
4949
* Phrases that includes quotes, like `attribute = 'She said "Hello World"'`
50+
* For nested keys in data map you can use the dot notation, like `person.age`
5051

5152
## Usage
5253
POM

src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111
import com.github.sidhant92.boolparser.domain.ComparisonNode;
1212
import com.github.sidhant92.boolparser.domain.Node;
1313
import com.github.sidhant92.boolparser.domain.UnaryNode;
14+
import com.github.sidhant92.boolparser.exception.DataNotFoundException;
1415
import com.github.sidhant92.boolparser.exception.InvalidUnaryOperand;
1516
import com.github.sidhant92.boolparser.operator.OperatorService;
1617
import com.github.sidhant92.boolparser.parser.BoolExpressionParser;
18+
import com.github.sidhant92.boolparser.util.ValueUtils;
1719
import io.vavr.control.Try;
1820
import lombok.extern.slf4j.Slf4j;
1921

@@ -55,19 +57,13 @@ private boolean evaluateToken(final Node node, final Map<String, Object> data) {
5557
}
5658

5759
private boolean evaluateComparisonToken(final ComparisonNode comparisonToken, final Map<String, Object> data) {
58-
if (checkFieldDataMissing(comparisonToken.getField(), data)) {
59-
return false;
60-
}
61-
final Object fieldData = data.get(comparisonToken.getField());
60+
final Object fieldData = ValueUtils.getValueFromMap(comparisonToken.getField(), data).orElseThrow(DataNotFoundException::new);
6261
return operatorService.evaluate(comparisonToken.getOperator(), ContainerDataType.primitive, comparisonToken.getDataType(), fieldData,
6362
comparisonToken.getValue());
6463
}
6564

6665
private boolean evaluateNumericRangeToken(final NumericRangeNode numericRangeToken, final Map<String, Object> data) {
67-
if (checkFieldDataMissing(numericRangeToken.getField(), data)) {
68-
return false;
69-
}
70-
final Object fieldData = data.get(numericRangeToken.getField());
66+
final Object fieldData = ValueUtils.getValueFromMap(numericRangeToken.getField(), data).orElseThrow(DataNotFoundException::new);
7167
return operatorService.evaluate(Operator.GREATER_THAN_EQUAL, ContainerDataType.primitive, numericRangeToken.getFromDataType(), fieldData,
7268
numericRangeToken.getFromValue()) && operatorService.evaluate(Operator.LESS_THAN_EQUAL,
7369
ContainerDataType.primitive,
@@ -76,10 +72,7 @@ private boolean evaluateNumericRangeToken(final NumericRangeNode numericRangeTok
7672
}
7773

7874
private boolean evaluateInToken(final InNode inToken, final Map<String, Object> data) {
79-
if (checkFieldDataMissing(inToken.getField(), data)) {
80-
return false;
81-
}
82-
final Object fieldData = data.get(inToken.getField());
75+
final Object fieldData = ValueUtils.getValueFromMap(inToken.getField(), data).orElseThrow(DataNotFoundException::new);
8376
final DataType dataType = inToken.getItems().get(0).getLeft();
8477
final Object[] values = inToken.getItems()
8578
.stream()
@@ -91,14 +84,11 @@ private boolean evaluateUnaryToken(final UnaryNode unaryToken, final Map<String,
9184
if (unaryToken.getDataType().equals(DataType.BOOLEAN)) {
9285
return (boolean) unaryToken.getValue();
9386
}
94-
if (checkFieldDataMissing(unaryToken.getValue().toString(), data)) {
95-
return false;
96-
}
97-
final Object fieldValue = data.get(unaryToken.getValue().toString());
98-
if (!(fieldValue instanceof Boolean)) {
87+
final Object fieldData = ValueUtils.getValueFromMap(unaryToken.getValue().toString(), data).orElseThrow(DataNotFoundException::new);
88+
if (!(fieldData instanceof Boolean)) {
9989
throw new InvalidUnaryOperand();
10090
}
101-
return (boolean) fieldValue;
91+
return (boolean) fieldData;
10292
}
10393

10494
private boolean evaluateBooleanNode(final BooleanNode booleanToken, final Map<String, Object> data) {
@@ -111,12 +101,4 @@ private boolean evaluateBooleanNode(final BooleanNode booleanToken, final Map<St
111101
return !evaluateToken(booleanToken.getLeft(), data);
112102
}
113103
}
114-
115-
private boolean checkFieldDataMissing(final String field, final Map<String, Object> data) {
116-
if (!data.containsKey(field)) {
117-
log.error("Error data not found for field {}", field);
118-
return true;
119-
}
120-
return false;
121-
}
122104
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.sidhant92.boolparser.exception;
2+
3+
public class DataNotFoundException extends RuntimeException {
4+
@Override
5+
public String getMessage() {
6+
return "Data not found";
7+
}
8+
}

src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
package com.github.sidhant92.boolparser.util;
22

3+
import java.util.Map;
34
import java.util.Optional;
45
import org.apache.maven.artifact.versioning.ComparableVersion;
56
import com.github.sidhant92.boolparser.constant.DataType;
7+
import lombok.extern.slf4j.Slf4j;
68

9+
@Slf4j
710
public class ValueUtils {
11+
public static Optional<Object> getValueFromMap(final String key, final Map<String, Object> data) {
12+
final String[] keys = key.split("\\.");
13+
final int size = keys.length;
14+
final Optional<Object> fieldData = Optional.ofNullable(data.get(keys[0]));
15+
if (size == 1) {
16+
return fieldData;
17+
}
18+
if (fieldData.isPresent() && fieldData.get() instanceof Map) {
19+
try {
20+
return getValueFromMap(keys[1], (Map<String, Object>) fieldData.get());
21+
} catch (ClassCastException ex) {
22+
return Optional.empty();
23+
}
24+
}
25+
log.error("could not find key {} for the data {}", key, data);
26+
return Optional.empty();
27+
}
28+
829
public static Object convertValue(final String value, final DataType dataType) {
930
switch (dataType) {
1031
case INTEGER:

src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.HashMap;
66
import java.util.Map;
77
import org.junit.jupiter.api.Test;
8+
import com.github.sidhant92.boolparser.exception.DataNotFoundException;
89
import com.github.sidhant92.boolparser.exception.InvalidDataType;
910
import com.github.sidhant92.boolparser.exception.InvalidUnaryOperand;
1011
import com.github.sidhant92.boolparser.parser.antlr.BoolParser;
@@ -108,6 +109,28 @@ public void testNumericGreaterThanCorrectExpression() {
108109
assertTrue(booleanOptional.get());
109110
}
110111

112+
@Test
113+
public void testNestedField() {
114+
final Map<String, Object> data = new HashMap<>();
115+
final Map<String, Object> person = new HashMap<>();
116+
person.put("age", 24);
117+
data.put("person", person);
118+
final Try<Boolean> booleanOptional = booleanExpressionEvaluator.evaluate("person.age > 20", data);
119+
assertTrue(booleanOptional.isSuccess());
120+
assertTrue(booleanOptional.get());
121+
}
122+
123+
@Test
124+
public void testMissingNestedField() {
125+
final Map<String, Object> data = new HashMap<>();
126+
final Map<String, Object> person = new HashMap<>();
127+
person.put("age", 24);
128+
data.put("person", person);
129+
final Try<Boolean> booleanOptional = booleanExpressionEvaluator.evaluate("person.agee > 20", data);
130+
assertTrue(booleanOptional.isFailure());
131+
assertTrue(booleanOptional.getCause() instanceof DataNotFoundException);
132+
}
133+
111134
@Test
112135
public void testNumericGreaterThanIncorrectExpression() {
113136
final Map<String, Object> data = new HashMap<>();
@@ -330,8 +353,8 @@ public void testKeyMissing() {
330353
final Map<String, Object> data = new HashMap<>();
331354
data.put("agee", 34);
332355
final Try<Boolean> booleanOptional = booleanExpressionEvaluator.evaluate("age = 24", data);
333-
assertTrue(booleanOptional.isSuccess());
334-
assertFalse(booleanOptional.get());
356+
assertTrue(booleanOptional.isFailure());
357+
assertTrue(booleanOptional.getCause() instanceof DataNotFoundException);
335358
}
336359

337360
@Test

0 commit comments

Comments
 (0)