You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by gi...@apache.org on 2026/06/04 10:17:23 UTC

(incubator-kie-drools) branch main updated: [incubator-kie-issues#2336] Fix broken behavior involving ForIterationNode (#6746)

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

gitgabrio 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 d3346eed41 [incubator-kie-issues#2336] Fix broken behavior involving ForIterationNode (#6746)
d3346eed41 is described below

commit d3346eed41e74139e1e5050a31c99793d6f7a161
Author: Gabriele Cardosi <ga...@gmail.com>
AuthorDate: Thu Jun 4 10:17:18 2026 +0000

    [incubator-kie-issues#2336] Fix broken behavior involving ForIterationNode (#6746)
    
    * [incubator-kie-issues#2336] Inside ForExpressionNode, exit context frame inside the while loop to prevent exiting the wrong one.
    
    * [incubator-kie-issues#2336] Implemented unit tests. Minor refactoring to simplify testing
    
    ---------
    
    Co-authored-by: Gabriele-Cardosi <ga...@ibm.com>
---
 .../kie/dmn/feel/lang/ast/ForExpressionNode.java   | 64 ++++++++++++--------
 .../dmn/feel/lang/ast/ForExpressionNodeTest.java   | 70 ++++++++++++++++++++++
 2 files changed, 109 insertions(+), 25 deletions(-)

diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/ForExpressionNode.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/ForExpressionNode.java
index 9b1aae2a97..5b2219aa6f 100644
--- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/ForExpressionNode.java
+++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/ForExpressionNode.java
@@ -92,7 +92,36 @@ public class ForExpressionNode
         }
     }
 
-    private void populateToReturn(int k, EvaluationContext ctx, List<Object> toPopulate) {
+    static void setValueIntoContext(EvaluationContext ctx, String name, Object value) {
+        ctx.setValue(name, value);
+    }
+
+    @Override
+    public Type getResultType() {
+        return BuiltInType.LIST;
+    }
+
+    @Override
+    public ASTNode[] getChildrenNode() {
+        ASTNode[] children = new ASTNode[iterationContexts.size() + 1];
+        System.arraycopy(iterationContexts.toArray(new ASTNode[]{}), 0, children, 0, iterationContexts.size());
+        children[children.length - 1] = expression;
+        return children;
+    }
+
+    @Override
+    public <T> T accept(Visitor<T> v) {
+        return v.visit(this);
+    }
+
+    /**
+     * Populate the given <code>List&lt;Object&gt;</code> with the result of evaluating the expressions retrieved from the
+     * <code>IterationContextNode</code> at the <b>k</b> index of <code>iterationContexts</code>.
+     * @param k
+     * @param ctx
+     * @param toPopulate
+     */
+    void populateToReturn(int k, EvaluationContext ctx, List<Object> toPopulate) {
         LOG.trace("populateToReturn at index {}", k);
         if (k > iterationContexts.size() - 1) {
             LOG.trace("Index {} out of range, returning", k);
@@ -114,20 +143,18 @@ public class ForExpressionNode
             } else if (k < iterationContexts.size() - 1) {
                 populateToReturn(k + 1, ctx, toPopulate);
             }
+            ctx.exitFrame();
         }
-        ctx.exitFrame();
     }
 
-    static void setValueIntoContext(EvaluationContext ctx, String name, Object value) {
-        ctx.setValue(name, value);
-    }
-
-    @Override
-    public Type getResultType() {
-        return BuiltInType.LIST;
-    }
-
-    private ForIteration createForIteration(EvaluationContext ctx, IterationContextNode iterationContextNode) throws NullContentInsideForIterationException {
+    /**
+     * Instantiate a <code>ForIteration</code> object based on the evaluation of the given <code>IterationContextNode</code>.
+     * @param ctx
+     * @param iterationContextNode
+     * @return
+     * @throws NullContentInsideForIterationException
+     */
+    static ForIteration createForIteration(EvaluationContext ctx, IterationContextNode iterationContextNode) throws NullContentInsideForIterationException {
         LOG.trace("Creating ForIteration for {}", iterationContextNode);
         ForIteration toReturn = null;
         String name = iterationContextNode.evaluateName(ctx);
@@ -148,17 +175,4 @@ public class ForExpressionNode
         }
         return toReturn;
     }
-
-    @Override
-    public ASTNode[] getChildrenNode() {
-        ASTNode[] children = new ASTNode[iterationContexts.size() + 1];
-        System.arraycopy(iterationContexts.toArray(new ASTNode[]{}), 0, children, 0, iterationContexts.size());
-        children[children.length - 1] = expression;
-        return children;
-    }
-
-    @Override
-    public <T> T accept(Visitor<T> v) {
-        return v.visit(this);
-    }
 }
diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/ForExpressionNodeTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/ForExpressionNodeTest.java
index 2b28ee57bf..810b2077e7 100644
--- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/ForExpressionNodeTest.java
+++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/ForExpressionNodeTest.java
@@ -20,6 +20,7 @@ package org.kie.dmn.feel.lang.ast;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -27,6 +28,10 @@ import java.util.List;
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
+import org.kie.dmn.feel.lang.EvaluationContext;
+import org.kie.dmn.feel.lang.ast.forexpressioniterators.ForIteration;
+import org.kie.dmn.feel.lang.impl.EvaluationContextImpl;
+import org.kie.dmn.feel.lang.impl.ExecutionFrame;
 import org.kie.dmn.feel.util.EvaluationContextTestUtil;
 import org.kie.dmn.feel.lang.types.BuiltInType;
 
@@ -81,4 +86,69 @@ class ForExpressionNodeTest {
         assertThat(retrieved).isInstanceOf(List.class).asList().containsExactly(LocalDate.of(1980, 1, 1),
                 LocalDate.of(1980, 1, 2), LocalDate.of(1980, 1, 3));
     }
+
+    @Test
+    void populateToReturnFromRange() {
+        String iterationName = "x";
+        IterationContextNode iterationContextNode = getIterationContextNode(iterationName, getLocalDateRangeNode("[1980-01-01 .. 1980-01-03]", LocalDate.of(1980, 1, 1), LocalDate.of(1980, 1, 3), RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED ), iterationName + " in [1980-01-01 .. 1980-01-03]");
+        ForExpressionNode forExpressionNode = new ForExpressionNode(Collections.singletonList(iterationContextNode), getNameRefNode(BuiltInType.DATE, iterationName), "for " + iterationName + " in [1980-01-01 .. 1980-01-03] return " + iterationName);
+        List<Object> toPopulate = new ArrayList<>();
+        EvaluationContextImpl ctx = (EvaluationContextImpl) EvaluationContextTestUtil.newEmptyEvaluationContext();
+        ExecutionFrame peeked = ctx.peek();
+        forExpressionNode.populateToReturn(0, ctx, toPopulate);
+        assertThat(toPopulate).containsExactly(LocalDate.of(1980, 1, 1),
+                                               LocalDate.of(1980, 1, 2),
+                                               LocalDate.of(1980, 1, 3));
+        assertThat(ctx.getAllValues()).doesNotContainKeys(iterationName);
+        assertThat(ctx.peek()).isEqualTo(peeked);
+    }
+
+    @Test
+    void populateToReturnFromArray() {
+        String iterationName = "x";
+        IterationContextNode iterationContextNode = getIterationContextNode("x", getListNode("[ 1, 2, 3, 4 ]", Arrays.asList("1", "2", "3", "4")), "x in [ 1, 2, 3, 4 ]");
+        ForExpressionNode forExpressionNode = new ForExpressionNode(Collections.singletonList(iterationContextNode), getNameRefNode(BuiltInType.UNKNOWN, iterationName), "for " + iterationName + " in [ 1, 2, 3, 4 ] return " + iterationName);
+        List<Object> toPopulate = new ArrayList<>();
+        EvaluationContextImpl ctx = (EvaluationContextImpl) EvaluationContextTestUtil.newEmptyEvaluationContext();
+        ExecutionFrame peeked = ctx.peek();
+        forExpressionNode.populateToReturn(0, ctx, toPopulate);
+        assertThat(toPopulate).containsExactly(BigDecimal.ONE,
+                                               BigDecimal.valueOf(2),
+                                               BigDecimal.valueOf(3),
+                                               BigDecimal.valueOf(4));
+        assertThat(ctx.getAllValues()).doesNotContainKeys(iterationName);
+        assertThat(ctx.peek()).isEqualTo(peeked);
+    }
+
+    @Test
+    void populateToReturnOutsideBoundaries() {
+        IterationContextNode x = getIterationContextNode("x", getLocalDateRangeNode("[1980-01-01 .. 1980-01-03]", LocalDate.of(1980, 1, 1), LocalDate.of(1980, 1, 3), RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED ), "x in [1980-01-01 .. 1980-01-03]");
+        ForExpressionNode forExpressionNode = new ForExpressionNode(Collections.singletonList(x), getNameRefNode(BuiltInType.DATE, "x"), "for x in [1980-01-01 .. 1980-01-03] return x");
+        List<Object> toPopulate = new ArrayList<>();
+        EvaluationContext ctx = EvaluationContextTestUtil.newEmptyEvaluationContext();
+        forExpressionNode.populateToReturn(1, ctx, toPopulate);
+        assertThat(toPopulate).isEmpty();
+    }
+
+    @Test
+    void createForIterationFromRange() {
+        String iterationName = "x";
+        IterationContextNode iterationContextNode = getIterationContextNode(iterationName, getLocalDateRangeNode("[1980-01-01 .. 1980-01-03]", LocalDate.of(1980, 1, 1), LocalDate.of(1980, 1, 3), RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED ), "x in [1980-01-01 .. 1980-01-03]");
+        ForIteration retrieved = ForExpressionNode.createForIteration(EvaluationContextTestUtil.newEmptyEvaluationContext(), iterationContextNode);
+        assertThat(retrieved).isNotNull();
+        assertThat(retrieved.getName()).isEqualTo(iterationName);
+        assertThat(retrieved.hasNextValue()).isTrue();
+        assertThat(retrieved.getNextValue()).isEqualTo(LocalDate.of(1980, 1, 1));
+    }
+
+    @Test
+    void createForIterationFromArray() {
+        String iterationName = "x";
+        IterationContextNode iterationContextNode = getIterationContextNode("x", getListNode("[ 1, 2, 3, 4 ]", Arrays.asList("1", "2", "3", "4")), "x in [ 1, 2, 3, 4 ]");
+        ForIteration retrieved = ForExpressionNode.createForIteration(EvaluationContextTestUtil.newEmptyEvaluationContext(), iterationContextNode);
+        assertThat(retrieved).isNotNull();
+        assertThat(retrieved.getName()).isEqualTo(iterationName);
+        assertThat(retrieved.hasNextValue()).isTrue();
+        assertThat(retrieved.getNextValue()).isEqualTo(BigDecimal.ONE);
+    }
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org