Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.github.sidhant92.boolparser.application;

import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
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.StringNode;
import com.github.sidhant92.boolparser.domain.arithmetic.ArithmeticLeafNode;
import com.github.sidhant92.boolparser.domain.arithmetic.ArithmeticNode;
import com.github.sidhant92.boolparser.domain.arithmetic.ArithmeticUnaryNode;
import com.github.sidhant92.boolparser.domain.Node;
import com.github.sidhant92.boolparser.exception.UnsupportedToken;
import com.github.sidhant92.boolparser.operator.OperatorService;
import com.github.sidhant92.boolparser.parser.BoolExpressionParser;
import com.github.sidhant92.boolparser.util.ValueUtils;
import io.vavr.control.Try;
import lombok.extern.slf4j.Slf4j;

/**
* @author sidhant.aggarwal
* @since 15/03/2024
*/
@Slf4j
public class ArithmeticExpressionEvaluator {
private final BoolExpressionParser boolExpressionParser;

private final OperatorService operatorService;

public ArithmeticExpressionEvaluator(final BoolExpressionParser boolExpressionParser) {
this.boolExpressionParser = boolExpressionParser;
operatorService = new OperatorService();
}

public Try<Object> evaluate(final String expression, final Map<String, Object> data) {
final Try<Node> tokenOptional = boolExpressionParser.parseExpression(expression, null);
return tokenOptional.map(node -> evaluateToken(node, data));
}

protected Object evaluate(final Node node, final Map<String, Object> data) {
return evaluateToken(node, data);
}

private Object evaluateToken(final Node node, final Map<String, Object> data) {
switch (node.getTokenType()) {
case ARITHMETIC:
return evaluateArithmeticToken((ArithmeticNode) node, data);
case ARITHMETIC_LEAF:
return evaluateArithmeticLeafToken((ArithmeticLeafNode) node, data);
case ARITHMETIC_UNARY:
return evaluateUnaryArithmeticToken((ArithmeticUnaryNode) node, data);
case STRING:
return evaluateStringToken((StringNode) node, data);
default:
log.error("unsupported token {}", node.getTokenType());
throw new UnsupportedToken();
}
}

private Object evaluateStringToken(final StringNode stringNode, final Map<String, Object> data) {
return ValueUtils.getValueFromMap(stringNode.getField(), data).orElse(stringNode.getField());
}

private Pair<Object, DataType> evaluateArithmeticLeafToken(final ArithmeticLeafNode arithmeticLeafNode, final Map<String, Object> data) {
final Optional<Object> fetchedValue = ValueUtils.getValueFromMap(arithmeticLeafNode.getOperand().toString(), data);
return fetchedValue
.map(o -> Pair.of(o, ValueUtils.getDataType(o)))
.orElseGet(() -> Pair.of(arithmeticLeafNode.getOperand(), arithmeticLeafNode.getDataType()));
}

private Object evaluateUnaryArithmeticToken(final ArithmeticUnaryNode arithmeticUnaryNode, final Map<String, Object> data) {
final Object resolvedValue = evaluateToken(arithmeticUnaryNode.getOperand(), data);
if (resolvedValue instanceof Pair) {
final Pair<Object, DataType> pair = (Pair<Object, DataType>) resolvedValue;
return operatorService.evaluateArithmeticOperator(pair.getLeft(), pair.getRight(), null, null, Operator.UNARY,
ContainerDataType.PRIMITIVE);
}
final DataType dataType = ValueUtils.getDataType(resolvedValue);
return operatorService.evaluateArithmeticOperator(resolvedValue, dataType, null, null, Operator.UNARY, ContainerDataType.PRIMITIVE);
}

private Object evaluateArithmeticToken(final ArithmeticNode arithmeticNode, final Map<String, Object> data) {
final Object leftValue = evaluateToken(arithmeticNode.getLeft(), data);
final Object rightValue = evaluateToken(arithmeticNode.getRight(), data);
if (leftValue instanceof Pair && rightValue instanceof Pair) {
final Pair<Object, DataType> leftPair = (Pair<Object, DataType>) leftValue;
final Pair<Object, DataType> rightPair = (Pair<Object, DataType>) rightValue;
return operatorService.evaluateArithmeticOperator(leftPair.getLeft(), leftPair.getRight(), rightPair.getLeft(), rightPair.getRight(),
arithmeticNode.getOperator(), ContainerDataType.PRIMITIVE);
} else if (leftValue instanceof Pair) {
final Pair<Object, DataType> leftPair = (Pair<Object, DataType>) leftValue;
final DataType rightDataType = ValueUtils.getDataType(rightValue);
return operatorService.evaluateArithmeticOperator(leftPair.getLeft(), leftPair.getRight(), rightValue, rightDataType,
arithmeticNode.getOperator(), ContainerDataType.PRIMITIVE);
} else if (rightValue instanceof Pair) {
final Pair<Object, DataType> rightPair = (Pair<Object, DataType>) rightValue;
final DataType leftDataType = ValueUtils.getDataType(leftValue);
return operatorService.evaluateArithmeticOperator(leftValue, leftDataType, rightPair.getLeft(), rightPair.getRight(),
arithmeticNode.getOperator(), ContainerDataType.PRIMITIVE);
} else {
final DataType leftDataType = ValueUtils.getDataType(leftValue);
final DataType rightDataType = ValueUtils.getDataType(rightValue);
return operatorService.evaluateArithmeticOperator(leftValue, leftDataType, rightValue, rightDataType, arithmeticNode.getOperator(),
ContainerDataType.PRIMITIVE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
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.domain.arithmetic.ArithmeticBaseNode;
import com.github.sidhant92.boolparser.exception.DataNotFoundException;
import com.github.sidhant92.boolparser.exception.HeterogeneousArrayException;
import com.github.sidhant92.boolparser.exception.InvalidUnaryOperand;
Expand All @@ -31,9 +32,12 @@ public class BooleanExpressionEvaluator {

private final OperatorService operatorService;

private final ArithmeticExpressionEvaluator arithmeticExpressionEvaluator;

public BooleanExpressionEvaluator(final BoolExpressionParser boolExpressionParser) {
this.boolExpressionParser = boolExpressionParser;
operatorService = new OperatorService();
arithmeticExpressionEvaluator = new ArithmeticExpressionEvaluator(boolExpressionParser);
}

public Try<Boolean> evaluate(final String expression, final Map<String, Object> data, final String defaultField) {
Expand Down Expand Up @@ -66,17 +70,17 @@ 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,
comparisonToken.getValue());
final Object value = comparisonToken.getValue() instanceof ArithmeticBaseNode ? arithmeticExpressionEvaluator.evaluate(
(Node) comparisonToken.getValue(), data) : comparisonToken.getValue();
return operatorService.evaluateLogicalOperator(comparisonToken.getOperator(), ContainerDataType.PRIMITIVE, comparisonToken.getDataType(),
fieldData, value);
}

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,
numericRangeToken.getFromValue()) && operatorService.evaluate(Operator.LESS_THAN_EQUAL,
ContainerDataType.PRIMITIVE,
numericRangeToken.getToDataType(), fieldData,
numericRangeToken.getToValue());
return operatorService.evaluateLogicalOperator(Operator.GREATER_THAN_EQUAL, ContainerDataType.PRIMITIVE, numericRangeToken.getFromDataType(),
fieldData, numericRangeToken.getFromValue()) && operatorService.evaluateLogicalOperator(
Operator.LESS_THAN_EQUAL, ContainerDataType.PRIMITIVE, numericRangeToken.getToDataType(), fieldData, numericRangeToken.getToValue());
}

private boolean evaluateInToken(final InNode inToken, final Map<String, Object> data) {
Expand All @@ -85,7 +89,7 @@ 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.evaluateLogicalOperator(Operator.IN, ContainerDataType.PRIMITIVE, dataType, fieldData, values);
}

private boolean evaluateArrayToken(final ArrayNode arrayNode, final Map<String, Object> data) {
Expand All @@ -99,7 +103,7 @@ private boolean evaluateArrayToken(final ArrayNode arrayNode, final Map<String,
final Object[] values = arrayNode.getItems()
.stream()
.map(Pair::getRight).toArray();
return operatorService.evaluate(arrayNode.getOperator(), ContainerDataType.LIST, dataType, fieldData, values);
return operatorService.evaluateLogicalOperator(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
Expand Up @@ -10,5 +10,9 @@ public enum NodeType {
NUMERIC_RANGE,
IN,
ARRAY,
UNARY
UNARY,
ARITHMETIC,
ARITHMETIC_LEAF,
ARITHMETIC_UNARY,
STRING
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.sidhant92.boolparser.constant;

import java.util.Optional;
import com.github.sidhant92.boolparser.operator.AbstractOperator;
import com.github.sidhant92.boolparser.operator.logical.AbstractOperator;
import com.github.sidhant92.boolparser.operator.OperatorFactory;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
Expand All @@ -21,15 +21,32 @@ public enum Operator {
LESS_THAN_EQUAL,
NOT_EQUAL,
IN,

ADD,
SUBTRACT,
MULTIPLY,
DIVIDE,
MODULUS,
EXPONENT,
UNARY,

CONTAINS_ALL,
CONTAINS_ANY;

public static Optional<Operator> getOperatorFromSymbol(final String symbol) {
final String symbolLowerCase = symbol.toLowerCase();
return OperatorFactory.getAllOperators()
final Optional<Operator> operator = OperatorFactory.getAllLogicalOperators()
.stream()
.filter(operator -> operator.getSymbol().toLowerCase().equals(symbolLowerCase))
.filter(op -> op.getSymbol().toLowerCase().equals(symbolLowerCase))
.map(AbstractOperator::getOperator)
.findFirst();
if (operator.isPresent()) {
return operator;
}
return OperatorFactory.getAllArithmeticOperators()
.stream()
.filter(op -> op.getSymbol().toLowerCase().equals(symbolLowerCase))
.map(com.github.sidhant92.boolparser.operator.arithmetic.AbstractOperator::getOperator)
.findFirst();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.github.sidhant92.boolparser.domain;

import com.github.sidhant92.boolparser.constant.NodeType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

/**
* @author sidhant.aggarwal
* @since 16/03/2024
*/
@AllArgsConstructor
@Getter
@Setter
@Builder
public class StringNode extends Node {
private final String field;

@Override
public NodeType getTokenType() {
return NodeType.STRING;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.sidhant92.boolparser.domain.arithmetic;

import com.github.sidhant92.boolparser.domain.Node;

/**
* @author sidhant.aggarwal
* @since 16/03/2024
*/
public abstract class ArithmeticBaseNode extends Node {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.github.sidhant92.boolparser.domain.arithmetic;

import com.github.sidhant92.boolparser.constant.DataType;
import com.github.sidhant92.boolparser.constant.NodeType;
import com.github.sidhant92.boolparser.domain.Node;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

/**
* @author sidhant.aggarwal
* @since 15/03/2024
*/
@AllArgsConstructor
@Getter
@Setter
@Builder
public class ArithmeticLeafNode extends ArithmeticBaseNode {
private Object operand;

private DataType dataType;

@Override
public NodeType getTokenType() {
return NodeType.ARITHMETIC_LEAF;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.sidhant92.boolparser.domain.arithmetic;

import com.github.sidhant92.boolparser.constant.NodeType;
import com.github.sidhant92.boolparser.constant.Operator;
import com.github.sidhant92.boolparser.domain.Node;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

/**
* @author sidhant.aggarwal
* @since 15/03/2024
*/
@AllArgsConstructor
@Getter
@Setter
@Builder
public class ArithmeticNode extends ArithmeticBaseNode {
private Node left;

private Node right;

private final Operator operator;

@Override
public NodeType getTokenType() {
return NodeType.ARITHMETIC;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.github.sidhant92.boolparser.domain.arithmetic;

import com.github.sidhant92.boolparser.constant.NodeType;
import com.github.sidhant92.boolparser.domain.Node;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

/**
* @author sidhant.aggarwal
* @since 15/03/2024
*/
@AllArgsConstructor
@Getter
@Setter
@Builder
public class ArithmeticUnaryNode extends ArithmeticBaseNode {
private Node operand;

@Override
public NodeType getTokenType() {
return NodeType.ARITHMETIC_UNARY;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.sidhant92.boolparser.exception;

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

public UnsupportedToken() {
super();
}

@Override
public String getMessage() {
return "Unsupported Token";
}
}
Loading