Skip to content

Commit 8c00ff3

Browse files
authored
Merge pull request #23 from sidhant92/develop
Support for Array operations
2 parents 8e52c7c + 896d77b commit 8c00ff3

36 files changed

+1052
-316
lines changed

build.gradle

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ sourceCompatibility = 1.8
2222
targetCompatibility = 1.8
2323

2424
group 'com.github.sidhant92'
25-
version = "1.1.1"
25+
version = "1.2.0"
2626

2727
apply plugin: "com.dipien.semantic-version"
2828

@@ -36,7 +36,7 @@ dependencies {
3636
implementation 'ch.qos.logback.contrib:logback-jackson:0.1.5'
3737
implementation 'net.logstash.logback:logstash-logback-encoder:5.2'
3838
implementation 'org.apache.maven:maven-artifact:3.5.2'
39-
implementation 'org.antlr:antlr4-runtime:4.11.1'
39+
implementation 'org.antlr:antlr4-runtime:4.13.1'
4040
implementation 'io.vavr:vavr:0.10.4'
4141
implementation 'com.github.ben-manes.caffeine:caffeine:2.9.3'
4242
implementation 'org.projectlombok:lombok:1.18.26'
@@ -91,7 +91,7 @@ publishing {
9191
pom {
9292
name = 'bool-parser'
9393
description = 'Java parser for boolean expressions'
94-
url = 'https://github.com/sidhant92/bool-parser'
94+
url = 'https://github.com/sidhant92/bool-parser-java'
9595
licenses {
9696
license {
9797
name = 'The Apache License, Version 2.0'
@@ -105,9 +105,9 @@ publishing {
105105
}
106106
}
107107
scm {
108-
url = 'https://github.com/sidhant92/bool-parser'
109-
connection = 'scm:git://github.com/sidhant92/bool-parser.git'
110-
developerConnection = 'scm:git://github.com/sidhant92/bool-parser.git'
108+
url = 'https://github.com/sidhant92/bool-parser-java'
109+
connection = 'scm:git://github.com/sidhant92/bool-parser-java.git'
110+
developerConnection = 'scm:git://github.com/sidhant92/bool-parser-java.git'
111111
}
112112
}
113113
}

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
import com.github.sidhant92.boolparser.constant.ContainerDataType;
66
import com.github.sidhant92.boolparser.constant.DataType;
77
import com.github.sidhant92.boolparser.constant.Operator;
8+
import com.github.sidhant92.boolparser.domain.ArrayNode;
89
import com.github.sidhant92.boolparser.domain.BooleanNode;
910
import com.github.sidhant92.boolparser.domain.InNode;
1011
import com.github.sidhant92.boolparser.domain.NumericRangeNode;
1112
import com.github.sidhant92.boolparser.domain.ComparisonNode;
1213
import com.github.sidhant92.boolparser.domain.Node;
1314
import com.github.sidhant92.boolparser.domain.UnaryNode;
1415
import com.github.sidhant92.boolparser.exception.DataNotFoundException;
16+
import com.github.sidhant92.boolparser.exception.HeterogeneousArrayException;
1517
import com.github.sidhant92.boolparser.exception.InvalidUnaryOperand;
1618
import com.github.sidhant92.boolparser.operator.OperatorService;
1719
import com.github.sidhant92.boolparser.parser.BoolExpressionParser;
@@ -51,6 +53,8 @@ private boolean evaluateToken(final Node node, final Map<String, Object> data) {
5153
return evaluateNumericRangeToken((NumericRangeNode) node, data);
5254
case IN:
5355
return evaluateInToken((InNode) node, data);
56+
case ARRAY:
57+
return evaluateArrayToken((ArrayNode) node, data);
5458
case UNARY:
5559
return evaluateUnaryToken((UnaryNode) node, data);
5660
case BOOLEAN:
@@ -62,15 +66,15 @@ private boolean evaluateToken(final Node node, final Map<String, Object> data) {
6266

6367
private boolean evaluateComparisonToken(final ComparisonNode comparisonToken, final Map<String, Object> data) {
6468
final Object fieldData = ValueUtils.getValueFromMap(comparisonToken.getField(), data).orElseThrow(DataNotFoundException::new);
65-
return operatorService.evaluate(comparisonToken.getOperator(), ContainerDataType.primitive, comparisonToken.getDataType(), fieldData,
69+
return operatorService.evaluate(comparisonToken.getOperator(), ContainerDataType.PRIMITIVE, comparisonToken.getDataType(), fieldData,
6670
comparisonToken.getValue());
6771
}
6872

6973
private boolean evaluateNumericRangeToken(final NumericRangeNode numericRangeToken, final Map<String, Object> data) {
7074
final Object fieldData = ValueUtils.getValueFromMap(numericRangeToken.getField(), data).orElseThrow(DataNotFoundException::new);
71-
return operatorService.evaluate(Operator.GREATER_THAN_EQUAL, ContainerDataType.primitive, numericRangeToken.getFromDataType(), fieldData,
75+
return operatorService.evaluate(Operator.GREATER_THAN_EQUAL, ContainerDataType.PRIMITIVE, numericRangeToken.getFromDataType(), fieldData,
7276
numericRangeToken.getFromValue()) && operatorService.evaluate(Operator.LESS_THAN_EQUAL,
73-
ContainerDataType.primitive,
77+
ContainerDataType.PRIMITIVE,
7478
numericRangeToken.getToDataType(), fieldData,
7579
numericRangeToken.getToValue());
7680
}
@@ -81,7 +85,21 @@ private boolean evaluateInToken(final InNode inToken, final Map<String, Object>
8185
final Object[] values = inToken.getItems()
8286
.stream()
8387
.map(Pair::getRight).toArray();
84-
return operatorService.evaluate(Operator.IN, ContainerDataType.primitive, dataType, fieldData, values);
88+
return operatorService.evaluate(Operator.IN, ContainerDataType.PRIMITIVE, dataType, fieldData, values);
89+
}
90+
91+
private boolean evaluateArrayToken(final ArrayNode arrayNode, final Map<String, Object> data) {
92+
final Object fieldData = ValueUtils.getValueFromMap(arrayNode.getField(), data).orElseThrow(DataNotFoundException::new);
93+
if (arrayNode.getItems()
94+
.stream()
95+
.map(Pair::getLeft).distinct().count() > 1) {
96+
throw new HeterogeneousArrayException();
97+
}
98+
final DataType dataType = arrayNode.getItems().get(0).getLeft();
99+
final Object[] values = arrayNode.getItems()
100+
.stream()
101+
.map(Pair::getRight).toArray();
102+
return operatorService.evaluate(arrayNode.getOperator(), ContainerDataType.LIST, dataType, fieldData, values);
85103
}
86104

87105
private boolean evaluateUnaryToken(final UnaryNode unaryToken, final Map<String, Object> data) {

src/main/java/com/github/sidhant92/boolparser/constant/ContainerDataType.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package com.github.sidhant92.boolparser.constant;
22

3+
import java.util.Collection;
4+
import java.util.List;
5+
import java.util.Objects;
36
import java.util.Optional;
7+
import java.util.stream.Collectors;
8+
import java.util.stream.Stream;
49
import com.github.sidhant92.boolparser.datatype.DataTypeFactory;
510
import com.github.sidhant92.boolparser.exception.InvalidDataType;
611
import lombok.AllArgsConstructor;
@@ -15,7 +20,7 @@
1520
@AllArgsConstructor
1621
@Slf4j
1722
public enum ContainerDataType {
18-
primitive() {
23+
PRIMITIVE() {
1924
@Override
2025
public <T> Optional<T> getValue(final DataType dataType, final Object value) {
2126
final Optional<T> result = DataTypeFactory.getDataType(dataType).getValue(value);
@@ -30,6 +35,39 @@ public <T> Optional<T> getValue(final DataType dataType, final Object value) {
3035
public boolean isValid(final DataType dataType, final Object value) {
3136
return DataTypeFactory.getDataType(dataType).isValid(value);
3237
}
38+
},
39+
LIST() {
40+
@Override
41+
public Optional<List<?>> getValue(final DataType dataType, final Object value) {
42+
if (Objects.isNull(value) || !(value instanceof Collection<?> || value instanceof Object[])) {
43+
return Optional.empty();
44+
}
45+
if (value instanceof Object[]) {
46+
return Optional.of(Stream.of((Object[]) value)
47+
.map(v -> DataTypeFactory.getDataType(dataType).getValue(v))
48+
.filter(Optional::isPresent)
49+
.map(Optional::get)
50+
.collect(Collectors.toList()));
51+
}
52+
return Optional.of(((Collection<?>) value)
53+
.stream()
54+
.map(v -> DataTypeFactory.getDataType(dataType).getValue(v))
55+
.filter(Optional::isPresent)
56+
.map(Optional::get)
57+
.collect(Collectors.toList()));
58+
}
59+
60+
@Override
61+
public boolean isValid(final DataType dataType, final Object value) {
62+
if (Objects.isNull(value) || !(value instanceof Collection<?> || value instanceof Object[])) {
63+
return false;
64+
}
65+
if (value instanceof Object[]) {
66+
return Stream.of((Object[]) value).allMatch(v -> DataTypeFactory.getDataType(dataType).isValid(v));
67+
}
68+
return ((Collection<?>) value)
69+
.stream().allMatch(v -> DataTypeFactory.getDataType(dataType).isValid(v));
70+
}
3371
};
3472

3573
public abstract <T> Optional<T> getValue(final DataType dataType, final Object value);

src/main/java/com/github/sidhant92/boolparser/constant/NodeType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public enum NodeType {
99
COMPARISON,
1010
NUMERIC_RANGE,
1111
IN,
12+
ARRAY,
1213
UNARY
1314
}

src/main/java/com/github/sidhant92/boolparser/constant/Operator.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ public enum Operator {
2020
LESS_THAN,
2121
LESS_THAN_EQUAL,
2222
NOT_EQUAL,
23-
IN;
23+
IN,
24+
CONTAINS_ALL,
25+
CONTAINS_ANY;
2426

2527
public static Optional<Operator> getOperatorFromSymbol(final String symbol) {
26-
return OperatorFactory.getAllOperators().stream().filter(operator -> operator.getSymbol().equals(symbol)).map(AbstractOperator::getOperator)
27-
.findFirst();
28+
final String symbolLowerCase = symbol.toLowerCase();
29+
return OperatorFactory.getAllOperators()
30+
.stream()
31+
.filter(operator -> operator.getSymbol().toLowerCase().equals(symbolLowerCase))
32+
.map(AbstractOperator::getOperator)
33+
.findFirst();
2834
}
2935
}

src/main/java/com/github/sidhant92/boolparser/datatype/AbstractDataType.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,26 @@ public AbstractDataType(final Class<T> clazz) {
1717
this.clazz = clazz;
1818
}
1919

20-
public boolean defaultIsValid(final Object value, final ObjectMapper objectMapper) {
20+
protected boolean defaultIsValid(final Object value, final ObjectMapper objectMapper) {
21+
return defaultIsValid(value, objectMapper, false);
22+
}
23+
24+
protected boolean defaultIsValid(final Object value, final ObjectMapper objectMapper, final boolean useStrictValidation) {
2125
try {
2226
if (clazz.isInstance(value)) {
2327
return true;
2428
}
29+
if (useStrictValidation) {
30+
return false;
31+
}
2532
return objectMapper.convertValue(value, clazz) != null;
2633
} catch (final Exception ex) {
2734
log.error("Unable to convert value = {} to type = {}", value, clazz);
2835
}
2936
return false;
3037
}
3138

32-
public Optional<T> defaultGetValue(final Object value, final ObjectMapper objectMapper) {
39+
protected Optional<T> defaultGetValue(final Object value, final ObjectMapper objectMapper) {
3340
try {
3441
if (clazz.isInstance(value)) {
3542
return Optional.of(clazz.cast(value));
@@ -45,5 +52,7 @@ public Optional<T> defaultGetValue(final Object value, final ObjectMapper object
4552

4653
public abstract boolean isValid(final Object value);
4754

55+
public abstract boolean isValid(final Object value, final boolean useStrictValidation);
56+
4857
public abstract Optional<T> getValue(final Object value);
4958
}

src/main/java/com/github/sidhant92/boolparser/datatype/BooleanDataType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
2626
return super.defaultIsValid(value, objectMapper);
2727
}
2828

29+
@Override
30+
public boolean isValid(final Object value, final boolean useStrictValidation) {
31+
return super.defaultIsValid(value, objectMapper, useStrictValidation);
32+
}
33+
2934
@Override
3035
public Optional<Boolean> getValue(Object value) {
3136
return defaultGetValue(value, objectMapper);

src/main/java/com/github/sidhant92/boolparser/datatype/DecimalDataType.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ public DecimalDataType(final ObjectMapper objectMapper) {
1818

1919
@Override
2020
public DataType getDataType() {
21-
return DataType.STRING;
21+
return DataType.DECIMAL;
2222
}
2323

2424
@Override
2525
public boolean isValid(final Object value) {
2626
return super.defaultIsValid(value, objectMapper);
2727
}
2828

29+
@Override
30+
public boolean isValid(final Object value, final boolean useStrictValidation) {
31+
return super.defaultIsValid(value, objectMapper, useStrictValidation);
32+
}
33+
2934
@Override
3035
public Optional<Double> getValue(Object value) {
3136
return defaultGetValue(value, objectMapper);

src/main/java/com/github/sidhant92/boolparser/datatype/IntegerDataType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
2626
return super.defaultIsValid(value, objectMapper);
2727
}
2828

29+
@Override
30+
public boolean isValid(final Object value, final boolean useStrictValidation) {
31+
return super.defaultIsValid(value, objectMapper, useStrictValidation);
32+
}
33+
2934
@Override
3035
public Optional<Integer> getValue(Object value) {
3136
return defaultGetValue(value, objectMapper);

src/main/java/com/github/sidhant92/boolparser/datatype/LongDataType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
2626
return super.defaultIsValid(value, objectMapper);
2727
}
2828

29+
@Override
30+
public boolean isValid(final Object value, final boolean useStrictValidation) {
31+
return super.defaultIsValid(value, objectMapper, useStrictValidation);
32+
}
33+
2934
@Override
3035
public Optional<Long> getValue(Object value) {
3136
return defaultGetValue(value, objectMapper);

0 commit comments

Comments
 (0)