From 31d79ad03b5abdb138ab50975cf0d0dfdf959be0 Mon Sep 17 00:00:00 2001 From: Sidhant Aggarwal <10743214+sidhant92@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:26:33 +0530 Subject: [PATCH] null check support --- .../BooleanExpressionEvaluator.java | 10 ++++-- .../boolparser/constant/Operator.java | 6 ++++ .../boolparser/domain/FieldNode.java | 4 +++ .../domain/logical/ComparisonNode.java | 5 +++ .../parser/antlr/CachedBoolParser.java | 4 ++- .../BooleanExpressionEvaluatorTest.java | 36 +++++++++++++++++++ 6 files changed, 61 insertions(+), 4 deletions(-) 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 d9120c4..dd081f8 100644 --- a/src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java +++ b/src/main/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluator.java @@ -2,10 +2,12 @@ import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; 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.FieldNode; import com.github.sidhant92.boolparser.domain.logical.ArrayNode; import com.github.sidhant92.boolparser.domain.logical.BooleanNode; import com.github.sidhant92.boolparser.domain.EvaluatedNode; @@ -69,9 +71,11 @@ private boolean evaluateToken(final Node node, final Map data) { } private boolean evaluateComparisonToken(final ComparisonNode comparisonToken, final Map data) { - final Object fieldData = ValueUtils.getValueFromMap(comparisonToken.getField(), data) - .orElseThrow(() -> new DataNotFoundException(comparisonToken.getField())); - final Object value = comparisonToken.getValue() instanceof ArithmeticBaseNode ? arithmeticExpressionEvaluator.evaluate( + final Optional fieldDataOptional = ValueUtils.getValueFromMap(comparisonToken.getField(), data); + + final Object fieldData = comparisonToken.isNullCheck() ? fieldDataOptional.orElse("null") : fieldDataOptional.orElseThrow( + () -> 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); 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 649199b..237c438 100644 --- a/src/main/java/com/github/sidhant92/boolparser/constant/Operator.java +++ b/src/main/java/com/github/sidhant92/boolparser/constant/Operator.java @@ -1,5 +1,7 @@ package com.github.sidhant92.boolparser.constant; +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.OperatorFactory; @@ -49,4 +51,8 @@ public static Optional getOperatorFromSymbol(final String symbol) { .map(com.github.sidhant92.boolparser.operator.arithmetic.AbstractOperator::getOperator) .findFirst(); } + + public static List getEqualityOperators() { + return Arrays.asList(EQUALS, NOT_EQUAL); + } } diff --git a/src/main/java/com/github/sidhant92/boolparser/domain/FieldNode.java b/src/main/java/com/github/sidhant92/boolparser/domain/FieldNode.java index 442c487..2f9b214 100644 --- a/src/main/java/com/github/sidhant92/boolparser/domain/FieldNode.java +++ b/src/main/java/com/github/sidhant92/boolparser/domain/FieldNode.java @@ -18,4 +18,8 @@ public class FieldNode extends ArithmeticBaseNode { public NodeType getTokenType() { return NodeType.FIELD; } + + public boolean isNull() { + return this.field.equalsIgnoreCase("null"); + } } diff --git a/src/main/java/com/github/sidhant92/boolparser/domain/logical/ComparisonNode.java b/src/main/java/com/github/sidhant92/boolparser/domain/logical/ComparisonNode.java index 81f15a4..383e892 100644 --- a/src/main/java/com/github/sidhant92/boolparser/domain/logical/ComparisonNode.java +++ b/src/main/java/com/github/sidhant92/boolparser/domain/logical/ComparisonNode.java @@ -3,6 +3,7 @@ import com.github.sidhant92.boolparser.constant.DataType; import com.github.sidhant92.boolparser.constant.NodeType; import com.github.sidhant92.boolparser.constant.Operator; +import com.github.sidhant92.boolparser.domain.FieldNode; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -29,4 +30,8 @@ public class ComparisonNode extends Node { public NodeType getTokenType() { return NodeType.COMPARISON; } + + public boolean isNullCheck() { + return Operator.getEqualityOperators().contains(this.operator) && this.value instanceof FieldNode && ((FieldNode) this.value).isNull(); + } } diff --git a/src/main/java/com/github/sidhant92/boolparser/parser/antlr/CachedBoolParser.java b/src/main/java/com/github/sidhant92/boolparser/parser/antlr/CachedBoolParser.java index f8a6b04..cb1dafa 100644 --- a/src/main/java/com/github/sidhant92/boolparser/parser/antlr/CachedBoolParser.java +++ b/src/main/java/com/github/sidhant92/boolparser/parser/antlr/CachedBoolParser.java @@ -1,5 +1,6 @@ package com.github.sidhant92.boolparser.parser.antlr; +import java.util.Optional; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.sidhant92.boolparser.domain.logical.Node; @@ -25,6 +26,7 @@ public Try parseExpression(final String expression) { } private Node getNode(final String expression, final String defaultField) { - return cache.get(expression, ex -> super.parse(ex, defaultField)); + final String cacheKey = expression + "_" + Optional.ofNullable(defaultField).orElse(""); + return cache.get(cacheKey, ex -> super.parse(ex, defaultField)); } } 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 591cc89..755e6c1 100644 --- a/src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java +++ b/src/test/java/com/github/sidhant92/boolparser/application/BooleanExpressionEvaluatorTest.java @@ -586,4 +586,40 @@ public void testComparisonWithArithmeticFalseCondition1() { assertTrue(booleanOptional.isSuccess()); assertFalse(booleanOptional.get()); } + + @Test + public void testNullCheck() { + final Map data = new HashMap<>(); + data.put("a", 2.7); + final Try resultOptional = booleanExpressionEvaluator.evaluate("b = null", data); + assertTrue(resultOptional.isSuccess()); + assertEquals(resultOptional.get(), true); + } + + @Test + public void testNullCheck1() { + final Map data = new HashMap<>(); + data.put("a", 2.7); + final Try resultOptional = booleanExpressionEvaluator.evaluate("a = null", data); + assertTrue(resultOptional.isSuccess()); + assertEquals(resultOptional.get(), false); + } + + @Test + public void testNotNullCheck() { + final Map data = new HashMap<>(); + data.put("a", 2.7); + final Try resultOptional = booleanExpressionEvaluator.evaluate("a != null", data); + assertTrue(resultOptional.isSuccess()); + assertEquals(resultOptional.get(), true); + } + + @Test + public void testBooleanNullCheck() { + final Map data = new HashMap<>(); + data.put("a", 3); + final Try resultOptional = booleanExpressionEvaluator.evaluate("b = null && a > 2", data); + assertTrue(resultOptional.isSuccess()); + assertEquals(resultOptional.get(), true); + } }