Skip to content

Commit e3242ae

Browse files
authored
Merge pull request #22 from sidhant92/array_support
Support For Array's and NOT IN Operator
2 parents 8636afe + 63adb46 commit e3242ae

28 files changed

+989
-310
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.0"
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
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.github.sidhant92.boolparser.domain;
2+
3+
import java.util.List;
4+
import org.apache.commons.lang3.tuple.Pair;
5+
import com.github.sidhant92.boolparser.constant.DataType;
6+
import com.github.sidhant92.boolparser.constant.NodeType;
7+
import com.github.sidhant92.boolparser.constant.Operator;
8+
import lombok.AllArgsConstructor;
9+
import lombok.Builder;
10+
import lombok.Getter;
11+
import lombok.Setter;
12+
13+
@AllArgsConstructor
14+
@Getter
15+
@Setter
16+
@Builder
17+
public class ArrayNode extends Node {
18+
private final String field;
19+
20+
private final Operator operator;
21+
22+
private final List<Pair<DataType, Object>> items;
23+
@Override
24+
public NodeType getTokenType() {
25+
return NodeType.ARRAY;
26+
}
27+
}
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 HeterogeneousArrayException extends RuntimeException{
4+
@Override
5+
public String getMessage() {
6+
return "Heterogeneous input of array not allowed";
7+
}
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.github.sidhant92.boolparser.exception;
2+
3+
public class InvalidContainerTypeException extends RuntimeException {
4+
public InvalidContainerTypeException(final String message) {
5+
super(message);
6+
}
7+
8+
public InvalidContainerTypeException() {
9+
super();
10+
}
11+
12+
@Override
13+
public String getMessage() {
14+
return "Invalid Container Type";
15+
}
16+
}
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.sidhant92.boolparser.operator;
22

3+
import java.util.List;
34
import com.github.sidhant92.boolparser.constant.ContainerDataType;
45
import com.github.sidhant92.boolparser.constant.DataType;
56
import com.github.sidhant92.boolparser.constant.Operator;
@@ -9,10 +10,14 @@
910
* @since 05/03/2023
1011
*/
1112
public abstract class AbstractOperator {
12-
public abstract <T extends Comparable<? super T>> boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, final Object leftOperand,
13-
final Object... rightOperands);
13+
public abstract <T extends Comparable<? super T>> boolean evaluate(final ContainerDataType containerDataType, final DataType dataType,
14+
final Object leftOperand, final Object... rightOperands);
1415

1516
public abstract Operator getOperator();
1617

1718
public abstract String getSymbol();
19+
20+
public abstract List<ContainerDataType> getAllowedContainerTypes();
21+
22+
public abstract List<DataType> getAllowedDataTypes();
1823
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.github.sidhant92.boolparser.operator;
2+
3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import com.github.sidhant92.boolparser.constant.ContainerDataType;
7+
import com.github.sidhant92.boolparser.constant.DataType;
8+
import com.github.sidhant92.boolparser.constant.Operator;
9+
import lombok.AllArgsConstructor;
10+
import lombok.extern.slf4j.Slf4j;
11+
12+
@AllArgsConstructor
13+
@Slf4j
14+
public class ContainsAllOperator extends AbstractOperator {
15+
private final InOperator inOperator;
16+
17+
@Override
18+
public <T extends Comparable<? super T>> boolean evaluate(final ContainerDataType containerDataType, final DataType dataType,
19+
final Object leftOperand, final Object... rightOperands) {
20+
if (!containerDataType.isValid(dataType, leftOperand)) {
21+
log.error("Validation failed for any operator for the operand {}", leftOperand);
22+
return false;
23+
}
24+
final Object[] leftOperandArray = ((List<?>) leftOperand).toArray();
25+
return Arrays
26+
.stream(rightOperands)
27+
.allMatch(rightOperand -> inOperator.evaluate(ContainerDataType.PRIMITIVE, dataType, rightOperand, leftOperandArray));
28+
}
29+
30+
@Override
31+
public Operator getOperator() {
32+
return Operator.CONTAINS_ALL;
33+
}
34+
35+
@Override
36+
public String getSymbol() {
37+
return "CONTAINS_ALL";
38+
}
39+
40+
@Override
41+
public List<ContainerDataType> getAllowedContainerTypes() {
42+
return Collections.singletonList(ContainerDataType.LIST);
43+
}
44+
45+
@Override
46+
public List<DataType> getAllowedDataTypes() {
47+
return Arrays.asList(DataType.values());
48+
}
49+
}

0 commit comments

Comments
 (0)