Skip to content

Commit

Permalink
Merge pull request #23 from sidhant92/develop
Browse files Browse the repository at this point in the history
Support for Array operations
  • Loading branch information
sidhant92 authored Feb 20, 2024
2 parents 8e52c7c + 896d77b commit 8c00ff3
Show file tree
Hide file tree
Showing 36 changed files with 1,052 additions and 316 deletions.
12 changes: 6 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ sourceCompatibility = 1.8
targetCompatibility = 1.8

group 'com.github.sidhant92'
version = "1.1.1"
version = "1.2.0"

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

Expand All @@ -36,7 +36,7 @@ dependencies {
implementation 'ch.qos.logback.contrib:logback-jackson:0.1.5'
implementation 'net.logstash.logback:logstash-logback-encoder:5.2'
implementation 'org.apache.maven:maven-artifact:3.5.2'
implementation 'org.antlr:antlr4-runtime:4.11.1'
implementation 'org.antlr:antlr4-runtime:4.13.1'
implementation 'io.vavr:vavr:0.10.4'
implementation 'com.github.ben-manes.caffeine:caffeine:2.9.3'
implementation 'org.projectlombok:lombok:1.18.26'
Expand Down Expand Up @@ -91,7 +91,7 @@ publishing {
pom {
name = 'bool-parser'
description = 'Java parser for boolean expressions'
url = 'https://github.com/sidhant92/bool-parser'
url = 'https://github.com/sidhant92/bool-parser-java'
licenses {
license {
name = 'The Apache License, Version 2.0'
Expand All @@ -105,9 +105,9 @@ publishing {
}
}
scm {
url = 'https://github.com/sidhant92/bool-parser'
connection = 'scm:git://github.com/sidhant92/bool-parser.git'
developerConnection = 'scm:git://github.com/sidhant92/bool-parser.git'
url = 'https://github.com/sidhant92/bool-parser-java'
connection = 'scm:git://github.com/sidhant92/bool-parser-java.git'
developerConnection = 'scm:git://github.com/sidhant92/bool-parser-java.git'
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
import com.github.sidhant92.boolparser.constant.ContainerDataType;
import com.github.sidhant92.boolparser.constant.DataType;
import com.github.sidhant92.boolparser.constant.Operator;
import com.github.sidhant92.boolparser.domain.ArrayNode;
import com.github.sidhant92.boolparser.domain.BooleanNode;
import com.github.sidhant92.boolparser.domain.InNode;
import com.github.sidhant92.boolparser.domain.NumericRangeNode;
import com.github.sidhant92.boolparser.domain.ComparisonNode;
import com.github.sidhant92.boolparser.domain.Node;
import com.github.sidhant92.boolparser.domain.UnaryNode;
import com.github.sidhant92.boolparser.exception.DataNotFoundException;
import com.github.sidhant92.boolparser.exception.HeterogeneousArrayException;
import com.github.sidhant92.boolparser.exception.InvalidUnaryOperand;
import com.github.sidhant92.boolparser.operator.OperatorService;
import com.github.sidhant92.boolparser.parser.BoolExpressionParser;
Expand Down Expand Up @@ -51,6 +53,8 @@ private boolean evaluateToken(final Node node, final Map<String, Object> data) {
return evaluateNumericRangeToken((NumericRangeNode) node, data);
case IN:
return evaluateInToken((InNode) node, data);
case ARRAY:
return evaluateArrayToken((ArrayNode) node, data);
case UNARY:
return evaluateUnaryToken((UnaryNode) node, data);
case BOOLEAN:
Expand All @@ -62,15 +66,15 @@ private boolean evaluateToken(final Node node, final Map<String, Object> data) {

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

private boolean evaluateNumericRangeToken(final NumericRangeNode numericRangeToken, final Map<String, Object> data) {
final Object fieldData = ValueUtils.getValueFromMap(numericRangeToken.getField(), data).orElseThrow(DataNotFoundException::new);
return operatorService.evaluate(Operator.GREATER_THAN_EQUAL, ContainerDataType.primitive, numericRangeToken.getFromDataType(), fieldData,
return operatorService.evaluate(Operator.GREATER_THAN_EQUAL, ContainerDataType.PRIMITIVE, numericRangeToken.getFromDataType(), fieldData,
numericRangeToken.getFromValue()) && operatorService.evaluate(Operator.LESS_THAN_EQUAL,
ContainerDataType.primitive,
ContainerDataType.PRIMITIVE,
numericRangeToken.getToDataType(), fieldData,
numericRangeToken.getToValue());
}
Expand All @@ -81,7 +85,21 @@ private boolean evaluateInToken(final InNode inToken, final Map<String, Object>
final Object[] values = inToken.getItems()
.stream()
.map(Pair::getRight).toArray();
return operatorService.evaluate(Operator.IN, ContainerDataType.primitive, dataType, fieldData, values);
return operatorService.evaluate(Operator.IN, ContainerDataType.PRIMITIVE, dataType, fieldData, values);
}

private boolean evaluateArrayToken(final ArrayNode arrayNode, final Map<String, Object> data) {
final Object fieldData = ValueUtils.getValueFromMap(arrayNode.getField(), data).orElseThrow(DataNotFoundException::new);
if (arrayNode.getItems()
.stream()
.map(Pair::getLeft).distinct().count() > 1) {
throw new HeterogeneousArrayException();
}
final DataType dataType = arrayNode.getItems().get(0).getLeft();
final Object[] values = arrayNode.getItems()
.stream()
.map(Pair::getRight).toArray();
return operatorService.evaluate(arrayNode.getOperator(), ContainerDataType.LIST, dataType, fieldData, values);
}

private boolean evaluateUnaryToken(final UnaryNode unaryToken, final Map<String, Object> data) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.github.sidhant92.boolparser.constant;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.github.sidhant92.boolparser.datatype.DataTypeFactory;
import com.github.sidhant92.boolparser.exception.InvalidDataType;
import lombok.AllArgsConstructor;
Expand All @@ -15,7 +20,7 @@
@AllArgsConstructor
@Slf4j
public enum ContainerDataType {
primitive() {
PRIMITIVE() {
@Override
public <T> Optional<T> getValue(final DataType dataType, final Object value) {
final Optional<T> result = DataTypeFactory.getDataType(dataType).getValue(value);
Expand All @@ -30,6 +35,39 @@ public <T> Optional<T> getValue(final DataType dataType, final Object value) {
public boolean isValid(final DataType dataType, final Object value) {
return DataTypeFactory.getDataType(dataType).isValid(value);
}
},
LIST() {
@Override
public Optional<List<?>> getValue(final DataType dataType, final Object value) {
if (Objects.isNull(value) || !(value instanceof Collection<?> || value instanceof Object[])) {
return Optional.empty();
}
if (value instanceof Object[]) {
return Optional.of(Stream.of((Object[]) value)
.map(v -> DataTypeFactory.getDataType(dataType).getValue(v))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList()));
}
return Optional.of(((Collection<?>) value)
.stream()
.map(v -> DataTypeFactory.getDataType(dataType).getValue(v))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList()));
}

@Override
public boolean isValid(final DataType dataType, final Object value) {
if (Objects.isNull(value) || !(value instanceof Collection<?> || value instanceof Object[])) {
return false;
}
if (value instanceof Object[]) {
return Stream.of((Object[]) value).allMatch(v -> DataTypeFactory.getDataType(dataType).isValid(v));
}
return ((Collection<?>) value)
.stream().allMatch(v -> DataTypeFactory.getDataType(dataType).isValid(v));
}
};

public abstract <T> Optional<T> getValue(final DataType dataType, final Object value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public enum NodeType {
COMPARISON,
NUMERIC_RANGE,
IN,
ARRAY,
UNARY
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,16 @@ public enum Operator {
LESS_THAN,
LESS_THAN_EQUAL,
NOT_EQUAL,
IN;
IN,
CONTAINS_ALL,
CONTAINS_ANY;

public static Optional<Operator> getOperatorFromSymbol(final String symbol) {
return OperatorFactory.getAllOperators().stream().filter(operator -> operator.getSymbol().equals(symbol)).map(AbstractOperator::getOperator)
.findFirst();
final String symbolLowerCase = symbol.toLowerCase();
return OperatorFactory.getAllOperators()
.stream()
.filter(operator -> operator.getSymbol().toLowerCase().equals(symbolLowerCase))
.map(AbstractOperator::getOperator)
.findFirst();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@ public AbstractDataType(final Class<T> clazz) {
this.clazz = clazz;
}

public boolean defaultIsValid(final Object value, final ObjectMapper objectMapper) {
protected boolean defaultIsValid(final Object value, final ObjectMapper objectMapper) {
return defaultIsValid(value, objectMapper, false);
}

protected boolean defaultIsValid(final Object value, final ObjectMapper objectMapper, final boolean useStrictValidation) {
try {
if (clazz.isInstance(value)) {
return true;
}
if (useStrictValidation) {
return false;
}
return objectMapper.convertValue(value, clazz) != null;
} catch (final Exception ex) {
log.error("Unable to convert value = {} to type = {}", value, clazz);
}
return false;
}

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

public abstract boolean isValid(final Object value);

public abstract boolean isValid(final Object value, final boolean useStrictValidation);

public abstract Optional<T> getValue(final Object value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
return super.defaultIsValid(value, objectMapper);
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
return super.defaultIsValid(value, objectMapper, useStrictValidation);
}

@Override
public Optional<Boolean> getValue(Object value) {
return defaultGetValue(value, objectMapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ public DecimalDataType(final ObjectMapper objectMapper) {

@Override
public DataType getDataType() {
return DataType.STRING;
return DataType.DECIMAL;
}

@Override
public boolean isValid(final Object value) {
return super.defaultIsValid(value, objectMapper);
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
return super.defaultIsValid(value, objectMapper, useStrictValidation);
}

@Override
public Optional<Double> getValue(Object value) {
return defaultGetValue(value, objectMapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
return super.defaultIsValid(value, objectMapper);
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
return super.defaultIsValid(value, objectMapper, useStrictValidation);
}

@Override
public Optional<Integer> getValue(Object value) {
return defaultGetValue(value, objectMapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
return super.defaultIsValid(value, objectMapper);
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
return super.defaultIsValid(value, objectMapper, useStrictValidation);
}

@Override
public Optional<Long> getValue(Object value) {
return defaultGetValue(value, objectMapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public boolean isValid(final Object value) {
return super.defaultIsValid(value, objectMapper);
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
return super.defaultIsValid(value, objectMapper, useStrictValidation);
}

@Override
public Optional<String> getValue(Object value) {
return defaultGetValue(value, objectMapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public boolean isValid(final Object value) {
return super.defaultIsValid(value, objectMapper);
}

@Override
public boolean isValid(final Object value, final boolean useStrictValidation) {
return super.defaultIsValid(value, objectMapper, useStrictValidation);
}

@Override
public Optional<ComparableVersion> getValue(Object value) {
return defaultGetValue(value, objectMapper);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.sidhant92.boolparser.domain;

import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import com.github.sidhant92.boolparser.constant.DataType;
import com.github.sidhant92.boolparser.constant.NodeType;
import com.github.sidhant92.boolparser.constant.Operator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@AllArgsConstructor
@Getter
@Setter
@Builder
public class ArrayNode extends Node {
private final String field;

private final Operator operator;

private final List<Pair<DataType, Object>> items;
@Override
public NodeType getTokenType() {
return NodeType.ARRAY;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.github.sidhant92.boolparser.exception;

public class HeterogeneousArrayException extends RuntimeException{
@Override
public String getMessage() {
return "Heterogeneous input of array not allowed";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.sidhant92.boolparser.exception;

public class InvalidContainerTypeException extends RuntimeException {
public InvalidContainerTypeException(final String message) {
super(message);
}

public InvalidContainerTypeException() {
super();
}

@Override
public String getMessage() {
return "Invalid Container Type";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.sidhant92.boolparser.operator;

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

public abstract Operator getOperator();

public abstract String getSymbol();

public abstract List<ContainerDataType> getAllowedContainerTypes();

public abstract List<DataType> getAllowedDataTypes();
}
Loading

0 comments on commit 8c00ff3

Please sign in to comment.