This is an automated email from the ASF dual-hosted git repository.

mariofusco pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/main by this push:
     new fe2dbacafe [KIE-775] drools executable-model fails with a bind 
variable to a calculation result of int and BigDecimal (#5636)
fe2dbacafe is described below

commit fe2dbacafed4f7c4809e91f829442cc6da348df5
Author: Toshiya Kobayashi <[email protected]>
AuthorDate: Wed Jan 10 00:13:53 2024 +0900

    [KIE-775] drools executable-model fails with a bind variable to a 
calculation result of int and BigDecimal (#5636)
---
 .../drlxparse/ArithmeticCoercedExpression.java     |  4 ++
 .../generator/drlxparse/ConstraintParser.java      | 50 ++++++++++++++++++----
 .../generator/expressiontyper/ExpressionTyper.java | 30 ++++++++++---
 .../execmodel/bigdecimaltest/BigDecimalTest.java   | 41 ++++++++++++++++++
 4 files changed, 110 insertions(+), 15 deletions(-)

diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ArithmeticCoercedExpression.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ArithmeticCoercedExpression.java
index 3cdea08ab3..765a6b313e 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ArithmeticCoercedExpression.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ArithmeticCoercedExpression.java
@@ -52,6 +52,10 @@ public class ArithmeticCoercedExpression {
         this.operator = operator;
     }
 
+    /*
+     * This coercion only deals with String vs Numeric types.
+     * BigDecimal arithmetic operation is handled by 
ExpressionTyper.convertArithmeticBinaryToMethodCall()
+     */
     public ArithmeticCoercedExpressionResult coerce() {
 
         if (!requiresCoercion()) {
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ConstraintParser.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ConstraintParser.java
index 2a85da6e2f..36b8f67a29 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ConstraintParser.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/drlxparse/ConstraintParser.java
@@ -95,6 +95,9 @@ import static 
org.drools.model.codegen.execmodel.generator.ConstraintUtil.GREATE
 import static 
org.drools.model.codegen.execmodel.generator.ConstraintUtil.GREATER_THAN_PREFIX;
 import static 
org.drools.model.codegen.execmodel.generator.ConstraintUtil.LESS_OR_EQUAL_PREFIX;
 import static 
org.drools.model.codegen.execmodel.generator.ConstraintUtil.LESS_THAN_PREFIX;
+import static 
org.drools.model.codegen.execmodel.generator.expressiontyper.ExpressionTyper.convertArithmeticBinaryToMethodCall;
+import static 
org.drools.model.codegen.execmodel.generator.expressiontyper.ExpressionTyper.getBinaryTypeAfterConversion;
+import static 
org.drools.model.codegen.execmodel.generator.expressiontyper.ExpressionTyper.shouldConvertArithmeticBinaryToMethodCall;
 import static org.drools.util.StringUtils.lcFirstForBean;
 import static 
org.drools.model.codegen.execmodel.generator.DrlxParseUtil.THIS_PLACEHOLDER;
 import static 
org.drools.model.codegen.execmodel.generator.DrlxParseUtil.createConstraintCompiler;
@@ -197,11 +200,23 @@ public class ConstraintParser {
     }
 
     private void addDeclaration(DrlxExpression drlx, SingleDrlxParseSuccess 
singleResult, String bindId) {
-        DeclarationSpec decl = context.addDeclaration( bindId, 
singleResult.getLeftExprTypeBeforeCoercion() );
+        DeclarationSpec decl = context.addDeclaration(bindId, 
getDeclarationType(drlx, singleResult));
         if (drlx.getExpr() instanceof NameExpr) {
             decl.setBoundVariable( PrintUtil.printNode(drlx.getExpr()) );
         } else if (drlx.getExpr() instanceof BinaryExpr) {
-            
decl.setBoundVariable(PrintUtil.printNode(drlx.getExpr().asBinaryExpr().getLeft()));
+            Expression leftMostExpression = 
getLeftMostExpression(drlx.getExpr().asBinaryExpr());
+            decl.setBoundVariable(PrintUtil.printNode(leftMostExpression));
+            if (singleResult.getExpr() instanceof MethodCallExpr) {
+                // BinaryExpr was converted to MethodCallExpr. Create a 
TypedExpression for the leftmost expression of the BinaryExpr
+                ExpressionTyperContext expressionTyperContext = new 
ExpressionTyperContext();
+                ExpressionTyper expressionTyper = new ExpressionTyper(context, 
singleResult.getPatternType(), bindId, false, expressionTyperContext);
+                TypedExpressionResult leftTypedExpressionResult = 
expressionTyper.toTypedExpression(leftMostExpression);
+                Optional<TypedExpression> optLeft = 
leftTypedExpressionResult.getTypedExpression();
+                if (!optLeft.isPresent()) {
+                    throw new IllegalStateException("Cannot create 
TypedExpression for " + drlx.getExpr().asBinaryExpr().getLeft());
+                }
+                singleResult.setBoundExpr(optLeft.get());
+            }
         }
         decl.setBelongingPatternDescr(context.getCurrentPatternDescr());
         singleResult.setExprBinding( bindId );
@@ -211,6 +226,24 @@ public class ConstraintParser {
         }
     }
 
+    private static Class<?> getDeclarationType(DrlxExpression drlx, 
SingleDrlxParseSuccess singleResult) {
+        if (drlx.getBind() != null && drlx.getExpr() instanceof EnclosedExpr) {
+            // in case of enclosed, bind type should be the calculation result 
type
+            // If drlx.getBind() == null, a bind variable is inside the 
enclosed expression, leave it to the default behavior
+            return (Class<?>)singleResult.getExprType();
+        } else {
+            return singleResult.getLeftExprTypeBeforeCoercion();
+        }
+    }
+
+    private Expression getLeftMostExpression(BinaryExpr binaryExpr) {
+        Expression left = binaryExpr.getLeft();
+        if (left instanceof BinaryExpr) {
+            return getLeftMostExpression((BinaryExpr) left);
+        }
+        return left;
+    }
+
     /*
         This is the entry point for Constraint Transformation from a parsed 
MVEL constraint
         to a Java Expression
@@ -657,17 +690,16 @@ public class ConstraintParser {
 
         Expression combo;
 
-        boolean arithmeticExpr = isArithmeticOperator(operator);
         boolean isBetaConstraint = right.getExpression() != null && 
hasDeclarationFromOtherPattern( expressionTyperContext );
         boolean requiresSplit = operator == BinaryExpr.Operator.AND && 
binaryExpr.getRight() instanceof HalfBinaryExpr && !isBetaConstraint;
 
+        Type exprType = isBooleanOperator( operator ) ? boolean.class : 
left.getType();
+
         if (equalityExpr) {
             combo = getEqualityExpression( left, right, operator ).expression;
-        } else if (arithmeticExpr && (left.isBigDecimal())) {
-            ConstraintCompiler constraintCompiler = 
createConstraintCompiler(this.context, of(patternType));
-            CompiledExpressionResult compiledExpressionResult = 
constraintCompiler.compileExpression(binaryExpr);
-
-            combo = compiledExpressionResult.getExpression();
+        } else if (shouldConvertArithmeticBinaryToMethodCall(operator, 
left.getType(), right.getType())) {
+            combo = convertArithmeticBinaryToMethodCall(binaryExpr, 
of(patternType), this.context);
+            exprType = getBinaryTypeAfterConversion(left.getType(), 
right.getType());
         } else {
             if (left.getExpression() == null || right.getExpression() == null) 
{
                 return new DrlxParseFail(new 
ParseExpressionErrorResult(drlxExpr));
@@ -695,7 +727,7 @@ public class ConstraintParser {
             constraintType = Index.ConstraintType.FORALL_SELF_JOIN;
         }
 
-        return new SingleDrlxParseSuccess(patternType, bindingId, combo, 
isBooleanOperator( operator ) ? boolean.class : left.getType())
+        return new SingleDrlxParseSuccess(patternType, bindingId, combo, 
exprType)
                 .setDecodeConstraintType( constraintType )
                 .setUsedDeclarations( 
expressionTyperContext.getUsedDeclarations() )
                 .setUsedDeclarationsOnLeft( usedDeclarationsOnLeft )
diff --git 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
index 328aa5466e..8fdfb696c4 100644
--- 
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
+++ 
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/expressiontyper/ExpressionTyper.java
@@ -235,7 +235,14 @@ public class ExpressionTyper {
             right = coerced.getCoercedRight();
 
             final BinaryExpr combo = new BinaryExpr(left.getExpression(), 
right.getExpression(), operator);
-            return of(new TypedExpression(combo, left.getType()));
+
+            if (shouldConvertArithmeticBinaryToMethodCall(operator, 
left.getType(), right.getType())) {
+                Expression expression = 
convertArithmeticBinaryToMethodCall(combo, of(typeCursor), ruleContext);
+                java.lang.reflect.Type binaryType = 
getBinaryTypeAfterConversion(left.getType(), right.getType());
+                return of(new TypedExpression(expression, binaryType));
+            } else {
+                return of(new TypedExpression(combo, left.getType()));
+            }
         }
 
         if (drlxExpr instanceof HalfBinaryExpr) {
@@ -806,11 +813,12 @@ public class ExpressionTyper {
         TypedExpression rightTypedExpression = right.getTypedExpression()
                                                     .orElseThrow(() -> new 
NoSuchElementException("TypedExpressionResult doesn't contain 
TypedExpression!"));
         binaryExpr.setRight(rightTypedExpression.getExpression());
-        java.lang.reflect.Type binaryType = getBinaryType(leftTypedExpression, 
rightTypedExpression, binaryExpr.getOperator());
-        if 
(shouldConvertArithmeticBinaryToMethodCall(binaryExpr.getOperator(), 
binaryType)) {
+        if 
(shouldConvertArithmeticBinaryToMethodCall(binaryExpr.getOperator(), 
leftTypedExpression.getType(), rightTypedExpression.getType())) {
             Expression compiledExpression = 
convertArithmeticBinaryToMethodCall(binaryExpr, 
leftTypedExpression.getOriginalPatternType(), ruleContext);
+            java.lang.reflect.Type binaryType = 
getBinaryTypeAfterConversion(leftTypedExpression.getType(), 
rightTypedExpression.getType());
             return new TypedExpressionCursor(compiledExpression, binaryType);
         } else {
+            java.lang.reflect.Type binaryType = 
getBinaryType(leftTypedExpression, rightTypedExpression, 
binaryExpr.getOperator());
             return new TypedExpressionCursor(binaryExpr, binaryType);
         }
     }
@@ -819,7 +827,7 @@ public class ExpressionTyper {
      * Converts arithmetic binary expression (including coercion) to method 
call using ConstraintCompiler.
      * This method can be generic, so we may centralize the calls in 
drools-model
      */
-    private static Expression convertArithmeticBinaryToMethodCall(BinaryExpr 
binaryExpr,  Optional<Class<?>> originalPatternType, RuleContext ruleContext) {
+    public static Expression convertArithmeticBinaryToMethodCall(BinaryExpr 
binaryExpr,  Optional<Class<?>> originalPatternType, RuleContext ruleContext) {
         ConstraintCompiler constraintCompiler = 
createConstraintCompiler(ruleContext, originalPatternType);
         CompiledExpressionResult compiledExpressionResult = 
constraintCompiler.compileExpression(printNode(binaryExpr));
         return compiledExpressionResult.getExpression();
@@ -828,8 +836,15 @@ public class ExpressionTyper {
     /*
      * BigDecimal arithmetic operations should be converted to method calls. 
We may also apply this to BigInteger.
      */
-    private static boolean 
shouldConvertArithmeticBinaryToMethodCall(BinaryExpr.Operator operator, 
java.lang.reflect.Type type) {
-        return isArithmeticOperator(operator) && type.equals(BigDecimal.class);
+    public static boolean 
shouldConvertArithmeticBinaryToMethodCall(BinaryExpr.Operator operator, 
java.lang.reflect.Type leftType, java.lang.reflect.Type rightType) {
+        return isArithmeticOperator(operator) && 
(leftType.equals(BigDecimal.class) || rightType.equals(BigDecimal.class));
+    }
+
+    /*
+     * After arithmetic to method call conversion, BigDecimal should take 
precedence regardless of left or right. We may also apply this to BigInteger.
+     */
+    public static java.lang.reflect.Type 
getBinaryTypeAfterConversion(java.lang.reflect.Type leftType, 
java.lang.reflect.Type rightType) {
+        return (leftType.equals(BigDecimal.class) || 
rightType.equals(BigDecimal.class)) ? BigDecimal.class : leftType;
     }
 
     private java.lang.reflect.Type getBinaryType(TypedExpression 
leftTypedExpression, TypedExpression rightTypedExpression, Operator operator) {
@@ -936,6 +951,9 @@ public class ExpressionTyper {
                 Expression argumentExpression = methodCallExpr.getArgument(i);
 
                 if (argumentType != actualArgumentType) {
+                    // unbind the original argumentExpression first, otherwise 
setArgument() will remove the argumentExpression from 
coercedExpression.childrenNodes
+                    // It will result in failing to find DrlNameExpr in AST at 
DrlsParseUtil.transformDrlNameExprToNameExpr()
+                    methodCallExpr.replace(argumentExpression, new 
NameExpr("placeholder"));
                     Expression coercedExpression = new 
BigDecimalArgumentCoercion().coercedArgument(argumentType, actualArgumentType, 
argumentExpression);
                     methodCallExpr.setArgument(i, coercedExpression);
                 }
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/bigdecimaltest/BigDecimalTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/bigdecimaltest/BigDecimalTest.java
index 0cc90ca317..407462de39 100644
--- 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/bigdecimaltest/BigDecimalTest.java
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/bigdecimaltest/BigDecimalTest.java
@@ -772,4 +772,45 @@ public class BigDecimalTest extends BaseModelTest {
     public static String intToString(int value) {
         return Integer.toString(value);
     }
+
+    @Test
+    public void 
bindVariableToBigDecimalCoercion2Operands_shouldBindCorrectResult() {
+        bindVariableToBigDecimalCoercion("$var : (1000 * value1)");
+    }
+
+    @Test
+    public void 
bindVariableToBigDecimalCoercion3Operands_shouldBindCorrectResult() {
+        bindVariableToBigDecimalCoercion("$var : (100000 * value1 / 100)");
+    }
+
+    @Test
+    public void 
bindVariableToBigDecimalCoercion3OperandsWithParentheses_shouldBindCorrectResult()
 {
+        bindVariableToBigDecimalCoercion("$var : ((100000 * value1) / 100)");
+    }
+
+    private void bindVariableToBigDecimalCoercion(String binding) {
+        // KIE-775
+        String str =
+                "package org.drools.modelcompiler.bigdecimals\n" +
+                        "import " + BDFact.class.getCanonicalName() + ";\n" +
+                        "global java.util.List result;\n" +
+                        "rule R1\n" +
+                        "    when\n" +
+                        "        BDFact( " + binding +  " )\n" +
+                        "    then\n" +
+                        "        result.add($var);\n" +
+                        "end";
+
+        KieSession ksession = getKieSession(str);
+        List<BigDecimal> result = new ArrayList<>();
+        ksession.setGlobal("result", result);
+
+        BDFact bdFact = new BDFact();
+        bdFact.setValue1(new BigDecimal("80"));
+
+        ksession.insert(bdFact);
+        ksession.fireAllRules();
+
+        assertThat(result).contains(new BigDecimal("80000"));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to