diff --git a/src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java b/src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java index dd081f8..07a1478 100644 --- a/src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java +++ b/src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java @@ -1,9 +1,11 @@ package com.github.sidhant92.boolparser.application; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +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; @@ -77,16 +79,20 @@ private boolean evaluateComparisonToken(final ComparisonNode comparisonToken, fi () -> new DataNotFoundException(comparisonToken.getField())); final Object value = comparisonToken.isNullCheck() ? "null" : comparisonToken.getValue() instanceof ArithmeticBaseNode ? arithmeticExpressionEvaluator.evaluate( comparisonToken.getValue(), data) : comparisonToken.getValue(); - return operatorService.evaluateLogicalOperator(comparisonToken.getOperator(), ContainerDataType.PRIMITIVE, comparisonToken.getDataType(), - fieldData, value); + final DataType dataType = ValueUtils.getDataType(value); + final DataType fieldDataType = ValueUtils.getDataType(fieldData); + return operatorService.evaluateLogicalOperator(comparisonToken.getOperator(), ContainerDataType.PRIMITIVE, fieldData, + fieldDataType, Collections.singletonList(Pair.of(value, dataType))); } private boolean evaluateNumericRangeToken(final NumericRangeNode numericRangeToken, final Map data) { final Object fieldData = ValueUtils.getValueFromMap(numericRangeToken.getField(), data) .orElseThrow(() -> new DataNotFoundException(numericRangeToken.getField())); - 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()); + return operatorService.evaluateLogicalOperator(Operator.GREATER_THAN_EQUAL, ContainerDataType.PRIMITIVE, fieldData, + numericRangeToken.getFromDataType(), Collections.singletonList( + Pair.of(numericRangeToken.getFromValue(), numericRangeToken.getFromDataType()))) && operatorService.evaluateLogicalOperator( + Operator.LESS_THAN_EQUAL, ContainerDataType.PRIMITIVE, fieldData, numericRangeToken.getToDataType(), + Collections.singletonList(Pair.of(numericRangeToken.getToValue(), numericRangeToken.getToDataType()))); } private boolean evaluateInToken(final InNode inToken, final Map data) { @@ -94,10 +100,11 @@ private boolean evaluateInToken(final InNode inToken, final Map .orElseThrow(() -> new DataNotFoundException(inToken.getField())); final List items = resolveArrayElements(inToken.getItems(), data); final DataType dataType = ValueUtils.getDataType(fieldData); - final Object[] values = items + final List> values = items .stream() - .map(EvaluatedNode::getValue).toArray(); - return operatorService.evaluateLogicalOperator(Operator.IN, ContainerDataType.PRIMITIVE, dataType, fieldData, values); + .map(item -> Pair.of(item.getValue(), item.getDataType())) + .collect(Collectors.toList()); + return operatorService.evaluateLogicalOperator(Operator.IN, ContainerDataType.PRIMITIVE, fieldData, dataType, values); } private List resolveArrayElements(final List items, final Map data) { @@ -123,10 +130,11 @@ private boolean evaluateArrayToken(final ArrayNode arrayNode, final Map> values = items .stream() - .map(EvaluatedNode::getValue).toArray(); - return operatorService.evaluateLogicalOperator(arrayNode.getOperator(), ContainerDataType.LIST, dataType, fieldData, values); + .map(item -> Pair.of(item.getValue(), item.getDataType())) + .collect(Collectors.toList()); + return operatorService.evaluateLogicalOperator(arrayNode.getOperator(), ContainerDataType.LIST, fieldData, dataType, values); } private boolean evaluateUnaryToken(final UnaryNode unaryToken, final Map data) { diff --git a/src/main/java/com/github/sidhant92/boolparser/constant/DataType.java b/src/main/java/com/github/sidhant92/boolparser/constant/DataType.java index 6c216a2..03c9165 100644 --- a/src/main/java/com/github/sidhant92/boolparser/constant/DataType.java +++ b/src/main/java/com/github/sidhant92/boolparser/constant/DataType.java @@ -1,14 +1,21 @@ package com.github.sidhant92.boolparser.constant; +import lombok.AllArgsConstructor; + /** * @author sidhant.aggarwal * @since 05/03/2023 */ +@AllArgsConstructor public enum DataType { - STRING, - INTEGER, - LONG, - DECIMAL, - VERSION, - BOOLEAN + STRING(6, false), + INTEGER(3, true), + LONG(4, true), + DECIMAL(5, true), + VERSION(2, true), + BOOLEAN(1, false); + + public final int priority; + + public final boolean numeric; } diff --git a/src/main/java/com/github/sidhant92/boolparser/constant/Operator.java b/src/main/java/com/github/sidhant92/boolparser/constant/Operator.java index 237c438..2ec58fd 100644 --- a/src/main/java/com/github/sidhant92/boolparser/constant/Operator.java +++ b/src/main/java/com/github/sidhant92/boolparser/constant/Operator.java @@ -3,7 +3,7 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import com.github.sidhant92.boolparser.operator.logical.AbstractOperator; +import com.github.sidhant92.boolparser.operator.comparison.AbstractOperator; import com.github.sidhant92.boolparser.operator.OperatorFactory; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/OperatorFactory.java b/src/main/java/com/github/sidhant92/boolparser/operator/OperatorFactory.java index 6ec24ac..f636993 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/OperatorFactory.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/OperatorFactory.java @@ -12,16 +12,16 @@ import com.github.sidhant92.boolparser.operator.arithmetic.MultiplyOperator; import com.github.sidhant92.boolparser.operator.arithmetic.SubtractOperator; import com.github.sidhant92.boolparser.operator.arithmetic.UnaryOperator; -import com.github.sidhant92.boolparser.operator.logical.AbstractOperator; -import com.github.sidhant92.boolparser.operator.logical.ContainsAllOperator; -import com.github.sidhant92.boolparser.operator.logical.ContainsAnyOperator; -import com.github.sidhant92.boolparser.operator.logical.EqualsOperator; -import com.github.sidhant92.boolparser.operator.logical.GreaterThanEqualOperator; -import com.github.sidhant92.boolparser.operator.logical.GreaterThanOperator; -import com.github.sidhant92.boolparser.operator.logical.InOperator; -import com.github.sidhant92.boolparser.operator.logical.LessThanEqualOperator; -import com.github.sidhant92.boolparser.operator.logical.LessThanOperator; -import com.github.sidhant92.boolparser.operator.logical.NotEqualsOperator; +import com.github.sidhant92.boolparser.operator.comparison.AbstractOperator; +import com.github.sidhant92.boolparser.operator.comparison.ContainsAllOperator; +import com.github.sidhant92.boolparser.operator.comparison.ContainsAnyOperator; +import com.github.sidhant92.boolparser.operator.comparison.EqualsOperator; +import com.github.sidhant92.boolparser.operator.comparison.GreaterThanEqualOperator; +import com.github.sidhant92.boolparser.operator.comparison.GreaterThanOperator; +import com.github.sidhant92.boolparser.operator.comparison.InOperator; +import com.github.sidhant92.boolparser.operator.comparison.LessThanEqualOperator; +import com.github.sidhant92.boolparser.operator.comparison.LessThanOperator; +import com.github.sidhant92.boolparser.operator.comparison.NotEqualsOperator; /** * @author sidhant.aggarwal diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/OperatorService.java b/src/main/java/com/github/sidhant92/boolparser/operator/OperatorService.java index a685ae0..6c78ad3 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/OperatorService.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/OperatorService.java @@ -1,13 +1,15 @@ package com.github.sidhant92.boolparser.operator; +import java.util.List; import java.util.Objects; +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.datatype.DataTypeFactory; import com.github.sidhant92.boolparser.exception.InvalidContainerTypeException; import com.github.sidhant92.boolparser.exception.InvalidDataType; -import com.github.sidhant92.boolparser.operator.logical.AbstractOperator; +import com.github.sidhant92.boolparser.operator.comparison.AbstractOperator; /** * @author sidhant.aggarwal @@ -19,19 +21,24 @@ public OperatorService() { OperatorFactory.initialize(); } - public boolean evaluateLogicalOperator(final Operator operator, final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { + public boolean evaluateLogicalOperator(final Operator operator, final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { final AbstractOperator abstractOperator = OperatorFactory.getLogicalOperator(operator); if (!abstractOperator.getAllowedContainerTypes().contains(containerDataType)) { throw new InvalidContainerTypeException(String.format("Invalid left container type %s for operator %s", containerDataType, operator)); } - if (!abstractOperator.getAllowedDataTypes().contains(dataType)) { - throw new InvalidDataType(String.format("Invalid left operand data type %s for operator %s", dataType, operator)); + if (!abstractOperator.getAllowedDataTypes().contains(leftOperandDataType)) { + throw new InvalidDataType(String.format("Invalid left operand data type %s for operator %s", leftOperandDataType, operator)); } - if (!containerDataType.isValid(dataType, leftOperand)) { + if (!containerDataType.isValid(leftOperandDataType, leftOperand)) { throw new InvalidDataType(String.format("Validation failed for the operator %s for the operand %s", operator, leftOperand)); } - return OperatorFactory.getLogicalOperator(operator).evaluate(containerDataType, dataType, leftOperand, rightOperands); + rightOperands.forEach(rightOperand -> { + if (!abstractOperator.getAllowedDataTypes().contains(rightOperand.getRight())) { + throw new InvalidDataType(String.format("Invalid right operand data type %s for operator %s", rightOperand.getRight(), operator)); + } + }); + return OperatorFactory.getLogicalOperator(operator).evaluate(containerDataType, leftOperand, leftOperandDataType, rightOperands); } public Object evaluateArithmeticOperator(final Object leftOperand, final DataType leftDataType, final Object rightOperand, diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/AbstractOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/AbstractOperator.java similarity index 65% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/AbstractOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/AbstractOperator.java index eaa00cb..a5570bd 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/AbstractOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/AbstractOperator.java @@ -1,6 +1,7 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.List; +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; @@ -10,8 +11,9 @@ * @since 05/03/2023 */ public abstract class AbstractOperator { - public abstract > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands); + public abstract > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, + final List> rightOperands); public abstract Operator getOperator(); diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/ContainsAllOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/ContainsAllOperator.java similarity index 54% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/ContainsAllOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/ContainsAllOperator.java index c4315e8..c87b0b8 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/ContainsAllOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/ContainsAllOperator.java @@ -1,8 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +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; @@ -13,15 +15,18 @@ public class ContainsAllOperator extends AbstractOperator { private final InOperator inOperator; @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - if (!containerDataType.isValid(dataType, leftOperand)) { + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + if (!containerDataType.isValid(leftOperandDataType, leftOperand)) { return false; } - final Object[] leftOperandArray = ((List) leftOperand).toArray(); - return Arrays - .stream(rightOperands) - .allMatch(rightOperand -> inOperator.evaluate(ContainerDataType.PRIMITIVE, dataType, rightOperand, leftOperandArray)); + final List> leftOperandArray = Arrays + .stream(((List) leftOperand).toArray()) + .map(a -> Pair.of(a, leftOperandDataType)) + .collect(Collectors.toList()); + return rightOperands + .stream().allMatch(rightOperand -> inOperator.evaluate(ContainerDataType.PRIMITIVE, rightOperand.getLeft(), rightOperand.getRight(), + leftOperandArray)); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/ContainsAnyOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/ContainsAnyOperator.java similarity index 54% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/ContainsAnyOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/ContainsAnyOperator.java index d5039d1..917135a 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/ContainsAnyOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/ContainsAnyOperator.java @@ -1,8 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +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; @@ -13,15 +15,18 @@ public class ContainsAnyOperator extends AbstractOperator { private final InOperator inOperator; @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - if (!containerDataType.isValid(dataType, leftOperand)) { + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + if (!containerDataType.isValid(leftOperandDataType, leftOperand)) { return false; } - final Object[] leftOperandArray = ((List) leftOperand).toArray(); - return Arrays - .stream(rightOperands) - .anyMatch(rightOperand -> inOperator.evaluate(ContainerDataType.PRIMITIVE, dataType, rightOperand, leftOperandArray)); + final List> leftOperandArray = Arrays + .stream(((List) leftOperand).toArray()) + .map(a -> Pair.of(a, leftOperandDataType)) + .collect(Collectors.toList()); + return rightOperands + .stream().anyMatch(rightOperand -> inOperator.evaluate(ContainerDataType.PRIMITIVE, rightOperand.getLeft(), rightOperand.getRight(), + leftOperandArray)); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/EqualsOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/EqualsOperator.java similarity index 50% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/EqualsOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/EqualsOperator.java index 92dfa97..d2befb8 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/EqualsOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/EqualsOperator.java @@ -1,9 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; 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; @@ -14,11 +15,22 @@ */ public class EqualsOperator extends AbstractOperator { @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - final Optional leftValueOptional = containerDataType.getValue(dataType, leftOperand); - final Optional rightValueOptional = containerDataType.getValue(dataType, rightOperands[0]); - return leftValueOptional.flatMap(leftValue -> rightValueOptional.map(leftValue::compareTo).map(a -> a == 0)).orElse(false); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + final DataType comparisonType = getComparableDataType(leftOperandDataType, rightOperands.get(0).getRight()); + final Optional leftValueOptional = containerDataType.getValue(comparisonType, leftOperand); + final Optional rightValueOptional = containerDataType.getValue(comparisonType, rightOperands.get(0).getLeft()); + return leftValueOptional + .flatMap(leftValue -> rightValueOptional + .map(leftValue::compareTo) + .map(a -> a == 0)).orElse(false); + } + + private DataType getComparableDataType(final DataType leftOperandDataType, final DataType rightOperandDataType) { + if (leftOperandDataType.priority > rightOperandDataType.priority) { + return leftOperandDataType; + } + return rightOperandDataType; } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/GreaterThanEqualOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/GreaterThanEqualOperator.java similarity index 52% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/GreaterThanEqualOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/GreaterThanEqualOperator.java index 8268430..588ab10 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/GreaterThanEqualOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/GreaterThanEqualOperator.java @@ -1,9 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; 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; @@ -12,13 +13,18 @@ * @author sidhant.aggarwal * @since 05/03/2023 */ -public class GreaterThanEqualOperator extends AbstractOperator { +public class GreaterThanEqualOperator extends AbstractOperator implements NumericOperator { @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - final Optional leftValueOptional = containerDataType.getValue(dataType, leftOperand); - final Optional rightValueOptional = containerDataType.getValue(dataType, rightOperands[0]); - return leftValueOptional.flatMap(leftValue -> rightValueOptional.map(leftValue::compareTo).map(a -> a >= 0)).orElse(false); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + validate(leftOperand, leftOperandDataType, rightOperands.get(0).getLeft(), rightOperands.get(0).getRight(), containerDataType); + final DataType comparisonType = getComparableDataType(leftOperandDataType, rightOperands.get(0).getRight()); + final Optional leftValueOptional = containerDataType.getValue(comparisonType, leftOperand); + final Optional rightValueOptional = containerDataType.getValue(comparisonType, rightOperands.get(0).getLeft()); + return leftValueOptional + .flatMap(leftValue -> rightValueOptional + .map(leftValue::compareTo) + .map(a -> a >= 0)).orElse(false); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/GreaterThanOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/GreaterThanOperator.java similarity index 52% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/GreaterThanOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/GreaterThanOperator.java index c26f279..cf1f9a8 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/GreaterThanOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/GreaterThanOperator.java @@ -1,9 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; 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; @@ -12,13 +13,18 @@ * @author sidhant.aggarwal * @since 05/03/2023 */ -public class GreaterThanOperator extends AbstractOperator { +public class GreaterThanOperator extends AbstractOperator implements NumericOperator { @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - final Optional leftValueOptional = containerDataType.getValue(dataType, leftOperand); - final Optional rightValueOptional = containerDataType.getValue(dataType, rightOperands[0]); - return leftValueOptional.flatMap(leftValue -> rightValueOptional.map(leftValue::compareTo).map(a -> a > 0)).orElse(false); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + validate(leftOperand, leftOperandDataType, rightOperands.get(0).getLeft(), rightOperands.get(0).getRight(), containerDataType); + final DataType comparisonType = getComparableDataType(leftOperandDataType, rightOperands.get(0).getRight()); + final Optional leftValueOptional = containerDataType.getValue(comparisonType, leftOperand); + final Optional rightValueOptional = containerDataType.getValue(comparisonType, rightOperands.get(0).getLeft()); + return leftValueOptional + .flatMap(leftValue -> rightValueOptional + .map(leftValue::compareTo) + .map(a -> a > 0)).orElse(false); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/InOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/InOperator.java similarity index 70% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/InOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/InOperator.java index d605923..9689947 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/InOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/InOperator.java @@ -1,8 +1,9 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; +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; @@ -17,10 +18,10 @@ public class InOperator extends AbstractOperator { private final EqualsOperator equalsOperator; @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - return Arrays - .stream(rightOperands).anyMatch(a -> equalsOperator.evaluate(containerDataType, dataType, leftOperand, a)); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + return rightOperands + .stream().anyMatch(a -> equalsOperator.evaluate(containerDataType, leftOperand, leftOperandDataType, Collections.singletonList(a))); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/LessThanEqualOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/LessThanEqualOperator.java similarity index 52% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/LessThanEqualOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/LessThanEqualOperator.java index 1e802d9..00320dc 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/LessThanEqualOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/LessThanEqualOperator.java @@ -1,9 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; 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; @@ -12,13 +13,18 @@ * @author sidhant.aggarwal * @since 05/03/2023 */ -public class LessThanEqualOperator extends AbstractOperator { +public class LessThanEqualOperator extends AbstractOperator implements NumericOperator { @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - final Optional leftValueOptional = containerDataType.getValue(dataType, leftOperand); - final Optional rightValueOptional = containerDataType.getValue(dataType, rightOperands[0]); - return leftValueOptional.flatMap(leftValue -> rightValueOptional.map(leftValue::compareTo).map(a -> a <= 0)).orElse(false); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + validate(leftOperand, leftOperandDataType, rightOperands.get(0).getLeft(), rightOperands.get(0).getRight(), containerDataType); + final DataType comparisonType = getComparableDataType(leftOperandDataType, rightOperands.get(0).getRight()); + final Optional leftValueOptional = containerDataType.getValue(comparisonType, leftOperand); + final Optional rightValueOptional = containerDataType.getValue(comparisonType, rightOperands.get(0).getLeft()); + return leftValueOptional + .flatMap(leftValue -> rightValueOptional + .map(leftValue::compareTo) + .map(a -> a <= 0)).orElse(false); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/LessThanOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/LessThanOperator.java similarity index 52% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/LessThanOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/LessThanOperator.java index 8ce9578..684e981 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/LessThanOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/LessThanOperator.java @@ -1,9 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; 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; @@ -12,13 +13,18 @@ * @author sidhant.aggarwal * @since 05/03/2023 */ -public class LessThanOperator extends AbstractOperator { +public class LessThanOperator extends AbstractOperator implements NumericOperator { @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - final Optional leftValueOptional = containerDataType.getValue(dataType, leftOperand); - final Optional rightValueOptional = containerDataType.getValue(dataType, rightOperands[0]); - return leftValueOptional.flatMap(leftValue -> rightValueOptional.map(leftValue::compareTo).map(a -> a < 0)).orElse(false); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + validate(leftOperand, leftOperandDataType, rightOperands.get(0).getLeft(), rightOperands.get(0).getRight(), containerDataType); + final DataType comparisonType = getComparableDataType(leftOperandDataType, rightOperands.get(0).getRight()); + final Optional leftValueOptional = containerDataType.getValue(comparisonType, leftOperand); + final Optional rightValueOptional = containerDataType.getValue(comparisonType, rightOperands.get(0).getLeft()); + return leftValueOptional + .flatMap(leftValue -> rightValueOptional + .map(leftValue::compareTo) + .map(a -> a < 0)).orElse(false); } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/logical/NotEqualsOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/NotEqualsOperator.java similarity index 50% rename from src/main/java/com/github/sidhant92/boolparser/operator/logical/NotEqualsOperator.java rename to src/main/java/com/github/sidhant92/boolparser/operator/comparison/NotEqualsOperator.java index 6283153..8e1da31 100644 --- a/src/main/java/com/github/sidhant92/boolparser/operator/logical/NotEqualsOperator.java +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/NotEqualsOperator.java @@ -1,9 +1,10 @@ -package com.github.sidhant92.boolparser.operator.logical; +package com.github.sidhant92.boolparser.operator.comparison; import java.util.Arrays; import java.util.Collections; import java.util.List; 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; @@ -14,11 +15,22 @@ */ public class NotEqualsOperator extends AbstractOperator { @Override - public > boolean evaluate(final ContainerDataType containerDataType, final DataType dataType, - final Object leftOperand, final Object... rightOperands) { - final Optional leftValueOptional = containerDataType.getValue(dataType, leftOperand); - final Optional rightValueOptional = containerDataType.getValue(dataType, rightOperands[0]); - return leftValueOptional.flatMap(leftValue -> rightValueOptional.map(leftValue::compareTo).map(a -> a != 0)).orElse(false); + public > boolean evaluate(final ContainerDataType containerDataType, final Object leftOperand, + final DataType leftOperandDataType, final List> rightOperands) { + final DataType comparisonType = getComparableDataType(leftOperandDataType, rightOperands.get(0).getRight()); + final Optional leftValueOptional = containerDataType.getValue(comparisonType, leftOperand); + final Optional rightValueOptional = containerDataType.getValue(comparisonType, rightOperands.get(0).getLeft()); + return leftValueOptional + .flatMap(leftValue -> rightValueOptional + .map(leftValue::compareTo) + .map(a -> a != 0)).orElse(false); + } + + private DataType getComparableDataType(final DataType leftOperandDataType, final DataType rightOperandDataType) { + if (leftOperandDataType.priority > rightOperandDataType.priority) { + return leftOperandDataType; + } + return rightOperandDataType; } @Override diff --git a/src/main/java/com/github/sidhant92/boolparser/operator/comparison/NumericOperator.java b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/NumericOperator.java new file mode 100644 index 0000000..ce3df5f --- /dev/null +++ b/src/main/java/com/github/sidhant92/boolparser/operator/comparison/NumericOperator.java @@ -0,0 +1,33 @@ +package com.github.sidhant92.boolparser.operator.comparison; + +import com.github.sidhant92.boolparser.constant.ContainerDataType; +import com.github.sidhant92.boolparser.constant.DataType; +import com.github.sidhant92.boolparser.exception.InvalidDataType; + +public interface NumericOperator { + default DataType getComparableDataType(final DataType leftOperandDataType, final DataType rightOperandDataType) { + if ((leftOperandDataType.numeric && !rightOperandDataType.numeric) || (!leftOperandDataType.numeric && rightOperandDataType.numeric)) { + return leftOperandDataType.numeric ? leftOperandDataType : rightOperandDataType; + } + if (leftOperandDataType.priority > rightOperandDataType.priority) { + return leftOperandDataType; + } + return rightOperandDataType; + } + + default void validate(final Object leftOperand, final DataType leftOperandDataType, final Object rightOperand, + final DataType rightOperandDataType, final ContainerDataType containerDataType) { + if (!leftOperandDataType.numeric && !rightOperandDataType.numeric) { + return; + } + if (leftOperandDataType.numeric && rightOperandDataType.numeric) { + return; + } + if (!leftOperandDataType.numeric && !containerDataType.isValid(rightOperandDataType, leftOperand)) { + throw new InvalidDataType(String.format("Incompatible data types %s and %s", leftOperandDataType, rightOperandDataType)); + } + if (!rightOperandDataType.numeric && !containerDataType.isValid(leftOperandDataType, rightOperand)) { + throw new InvalidDataType(String.format("Incompatible data types %s and %s", leftOperandDataType, rightOperandDataType)); + } + } +} diff --git a/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java b/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java index 5523469..144f5f5 100644 --- a/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java +++ b/src/main/java/com/github/sidhant92/boolparser/util/ValueUtils.java @@ -65,7 +65,7 @@ public static Object convertValue(final String value, final DataType dataType) { case BOOLEAN: return Boolean.parseBoolean(value); case VERSION: - new ComparableVersion(value); + return new ComparableVersion(value); default: if (value.startsWith("'") && value.endsWith("'")) { return value.substring(1, value.length() - 1); @@ -110,6 +110,9 @@ public static DataType getDataType(final Object value) { if (value instanceof Long) { return DataType.LONG; } + if (value instanceof ComparableVersion) { + return DataType.VERSION; + } return DataType.STRING; } } diff --git a/src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java b/src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java index 80947a2..2772608 100644 --- a/src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java +++ b/src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java @@ -166,6 +166,15 @@ public void testNumericGreaterThanIncorrectExpression() { assertFalse(booleanOptional.get()); } + @Test + public void testDifferentDataTypesComparison() { + final Map data = new HashMap<>(); + data.put("age", 26.6); + final Try booleanOptional = booleanExpressionEvaluator.evaluate("age > 20", data); + assertTrue(booleanOptional.isSuccess()); + assertTrue(booleanOptional.get()); + } + @Test public void testNumericGreaterThanEqualCorrectExpression() { final Map data = new HashMap<>(); @@ -389,8 +398,8 @@ public void testWrongDataType1() { final Map data = new HashMap<>(); data.put("age", "sf"); final Try booleanOptional = booleanExpressionEvaluator.evaluate("age = 24", data); - assertTrue(booleanOptional.isFailure()); - assertTrue(booleanOptional.getCause() instanceof InvalidDataType); + assertTrue(booleanOptional.isSuccess()); + assertFalse(booleanOptional.get()); } @Test