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

yamer 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 c49848896a [Incubator-kie-issues#2028] Comparing lists using in does 
not work (#6557)
c49848896a is described below

commit c49848896aa6308649b83c20d0285ca2db4924d4
Author: AthiraHari77 <[email protected]>
AuthorDate: Fri Jan 23 15:39:48 2026 +0530

    [Incubator-kie-issues#2028] Comparing lists using in does not work (#6557)
    
    * [incubator-kie-issues#2028] Fix list comparison issue
    
    * [incubator-kie-issues#2028] Fix list comparison issue
    
    * [incubator-kie-issues#2028] updates tests
    
    * [incubator-kie-issues#2028] Fix testcases
    
    * [incubator-kie-issues#2028] Fix testcases
    
    * [incubator-kie-issues#2028] Code refactoring
    
    ---------
    
    Co-authored-by: athira <[email protected]>
---
 .../org/kie/dmn/feel/lang/ast/UnaryTestNode.java   | 64 ++++++++++++++-
 .../dmn/feel/parser/feel11/ASTBuilderVisitor.java  |  4 +-
 .../kie/dmn/feel/lang/ast/UnaryTestNodeTest.java   | 96 ++++++++++++++++++++++
 .../org/kie/dmn/feel/runtime/FEELListsTest.java    | 21 ++++-
 4 files changed, 179 insertions(+), 6 deletions(-)

diff --git 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
index 914e1353b1..79a5e757c7 100644
--- 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
+++ 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
@@ -19,6 +19,7 @@
 package org.kie.dmn.feel.lang.ast;
 
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 
 import org.antlr.v4.runtime.ParserRuleContext;
@@ -158,15 +159,70 @@ public class UnaryTestNode
      * For a Unary Test an = (equal) semantic depends on the RIGHT value.
      * If the RIGHT is NOT a list, then standard equals semantic applies
      * If the RIGHT is a LIST, then the semantic is "right contains left"
+     * When both are Collections:
+     * - Verify that the two objects have the same size
+     * - Verify that the element at each position in the left object equals 
the element at the same position in the right object.
      */
     private Boolean utEqualSemantic(Object left, Object right) {
-        if (right instanceof Collection) {
-            return ((Collection) right).contains(left);
+        if (left instanceof Collection && right instanceof Collection) {
+            return areCollectionsEqual((Collection<?>) left, (Collection<?>) 
right);
+        } else if (right instanceof Collection) {
+            return isElementInCollection((Collection<?>) right, left);
         } else {
-            // evaluate single entity
-            return DefaultDialectHandler.isEqual(left, right, () -> (left == 
null && right == null), () -> Boolean.FALSE);
+            return areElementsEqual(left, right);
+        }
+    }
+
+    /**
+     * Checks if two collections are equal by comparing elements in order.
+     * Both collections must have the same size and each element at position i 
in left
+     * must equal the element at position i in right.
+     *
+     * @param left the left collection
+     * @param right the right collection
+     * @return true if collections have same size and elements match in order, 
false otherwise
+     */
+    static Boolean areCollectionsEqual(Collection<?> left, Collection<?> 
right) {
+        if (left.size() != right.size()) {
+            return false;
+        }
 
+        Iterator<?> leftIterator = left.iterator();
+        Iterator<?> rightIterator = right.iterator();
+        while (leftIterator.hasNext() && rightIterator.hasNext()) {
+            if (!areElementsEqual(leftIterator.next(), rightIterator.next())) {
+                return false;
+            }
         }
+        return true;
+    }
+
+    /**
+     * Checks if a collection contains a specific element.
+     * Uses areElementsEqual() to ensure consistent equality semantics
+     * with custom null handling via DefaultDialectHandler.isEqual().
+     *
+     * @param collection the collection to search in
+     * @param element the element to search for
+     * @return true if collection contains the element, false otherwise
+     */
+    static Boolean isElementInCollection(Collection<?> collection, Object 
element) {
+        return collection.stream().anyMatch(item -> areElementsEqual(item, 
element));
+    }
+
+    /**
+     * Checks if two elements are equal.
+     *
+     * @param left the left element
+     * @param right the right element
+     * @return true if elements are equal, false otherwise
+     */
+    static Boolean areElementsEqual(Object left, Object right) {
+        return Boolean.TRUE.equals(
+                DefaultDialectHandler.isEqual(left, right,
+                        () -> (left == null && right == null),
+                        () -> Boolean.FALSE)
+        );
     }
 
     private UnaryTest createIsEqualUnaryTest() {
diff --git 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java
 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java
index 1e8791f001..ebab9aa4bc 100644
--- 
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java
+++ 
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/parser/feel11/ASTBuilderVisitor.java
@@ -184,7 +184,6 @@ public class ASTBuilderVisitor
     public BaseNode 
visitPositiveUnaryTestIneq(FEEL_1_1Parser.PositiveUnaryTestIneqContext ctx) {
         BaseNode value = visit( ctx.endpoint() );
         String op = ctx.op.getText();
-        UnaryOperator unaryOperator = UnaryOperator.determineOperator(op);
         return ASTBuilderFactory.newUnaryTestNode( ctx, op, value );
     }
 
@@ -192,6 +191,9 @@ public class ASTBuilderVisitor
     public BaseNode 
visitPositiveUnaryTestIneqInterval(FEEL_1_1Parser.PositiveUnaryTestIneqIntervalContext
 ctx) {
         BaseNode value = visit(ctx.endpoint());
         String op = ctx.op.getText();
+        if (value instanceof ListNode) {
+            return ASTBuilderFactory.newUnaryTestNode(ctx, op, value);
+        }
         switch (UnaryOperator.determineOperator(op)) {
             case EQ:
                 return ASTBuilderFactory.newIntervalNode(ctx, 
RangeNode.IntervalBoundary.CLOSED, value, value, 
RangeNode.IntervalBoundary.CLOSED);
diff --git 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/UnaryTestNodeTest.java
 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/UnaryTestNodeTest.java
new file mode 100644
index 0000000000..fcd2b205cc
--- /dev/null
+++ 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/UnaryTestNodeTest.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.kie.dmn.feel.lang.ast;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class UnaryTestNodeTest {
+
+    @ParameterizedTest
+    @MethodSource("provideCollectionsForEqualityTest")
+    void testAreCollectionsEqual(List<?> left, List<?> right, boolean 
expected) {
+        Boolean result = UnaryTestNode.areCollectionsEqual(left, right);
+        assertThat(result).isEqualTo(expected);
+    }
+
+    private static Stream<Arguments> provideCollectionsForEqualityTest() {
+        return Stream.of(
+            Arguments.of(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3), true, 
"Equal collections"),
+            Arguments.of(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3, 4), 
false, "Different sizes"),
+            Arguments.of(Arrays.asList(1, 2, 3), Arrays.asList(1, 5, 3), 
false, "Different elements"),
+            Arguments.of(Arrays.asList(1, 2, 3), Arrays.asList(3, 2, 1), 
false, "Different order"),
+            Arguments.of(Collections.emptyList(), Collections.emptyList(), 
true, "Empty collections"),
+            Arguments.of(Arrays.asList(1, null, 3), Arrays.asList(1, null, 3), 
true, "Null elements"),
+            Arguments.of(Arrays.asList(1, null, 3), Arrays.asList(1, 2, 3), 
false, "Mismatched nulls"),
+            Arguments.of(Arrays.asList("a", "b", "c"), Arrays.asList("a", "b", 
"c"), true, "Equal string collections")
+        );
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideElementInCollectionTestCases")
+    void testIsElementInCollection(List<?> collection, Object element, boolean 
expected) {
+        Boolean result = UnaryTestNode.isElementInCollection(collection, 
element);
+        assertThat(result).isEqualTo(expected);
+    }
+
+    private static Stream<Arguments> provideElementInCollectionTestCases() {
+        return Stream.of(Arguments.of(
+                Arrays.asList(Arrays.asList(1, 2, 3, 4), Arrays.asList(1, 2, 
3)),
+                Arrays.asList(1, 2, 3), true, "List element found in 
collection"),
+            Arguments.of(
+                Arrays.asList(Arrays.asList(1, 2, 3, 4), Arrays.asList(1, 2)),
+                Arrays.asList(1, 2, 3), false, "List element not found in 
collection"),
+            Arguments.of(
+                Arrays.asList(1, 2, 3, 4, 5), 3, true, "Element exists in 
collection"),
+            Arguments.of(
+                Arrays.asList(1, 2, 3, 4, 5), 10, false, "Element does not 
exist in collection"),
+            Arguments.of(
+                Arrays.asList(1, null, 3), null, true, "Null element in 
collection"),
+            Arguments.of(
+                Collections.emptyList(), 1, false, "Empty collection"));
+    }
+
+    @ParameterizedTest
+    @MethodSource("provideElementsForEqualityTest")
+    void testAreElementsEqual(Object left, Object right, boolean expected) {
+        Boolean result = UnaryTestNode.areElementsEqual(left, right);
+        assertThat(result).isEqualTo(expected);
+    }
+
+    private static Stream<Arguments> provideElementsForEqualityTest() {
+        return Stream.of(
+            Arguments.of(42, 42, true, "Equal integers"),
+            Arguments.of(42, 24, false, "Different integers"),
+            Arguments.of("hello", "hello", true, "Equal strings"),
+            Arguments.of("hello", "world", false, "Different strings"),
+            Arguments.of(null, null, true, "Both null"),
+            Arguments.of(42, null, false, "Left non-null, right null"),
+            Arguments.of(null, 42, false, "Left null, right non-null")
+        );
+    }
+}
\ No newline at end of file
diff --git 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELListsTest.java
 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELListsTest.java
index bf72ad0199..6003e470da 100644
--- 
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELListsTest.java
+++ 
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELListsTest.java
@@ -144,7 +144,26 @@ public class FEELListsTest extends BaseFEELTest {
                 {"[ [ duration(\"P1D\") .. duration(\"P10D\") ] ]", 
Collections.singletonList(new RangeImpl(Range.RangeBoundary.CLOSED, 
Duration.parse("P1D"),
                                                                                
                             Duration.parse("P10D"), 
Range.RangeBoundary.CLOSED)), null },
                 {"[ ( duration(\"P1D\") .. duration(\"P10D\") ), ( 
duration(\"P2D\") .. duration(\"P10D\") )][1]",
-                        new RangeImpl( Range.RangeBoundary.OPEN, 
Duration.parse("P1D"), Duration.parse( "P10D" ), Range.RangeBoundary.OPEN ), 
null }
+                        new RangeImpl( Range.RangeBoundary.OPEN, 
Duration.parse("P1D"), Duration.parse( "P10D" ), Range.RangeBoundary.OPEN ), 
null },
+
+                // List equality comparisons
+                {"[1,2,3] = [1,2,3]", Boolean.TRUE, null },
+                {"[1,2,3] = [1,2,3,4]", Boolean.FALSE, null },
+                {"[1,2,3] = [3,2,1]", Boolean.FALSE, null },
+                {"[] = []", Boolean.TRUE, null },
+                {"[1,null,3] = [1,null,3]", Boolean.TRUE, null },
+                {"[1,null,3] = [1,2,3]", Boolean.FALSE, null },
+                {"[\"a\",\"b\",\"c\"] = [\"a\",\"b\",\"c\"]", Boolean.TRUE, 
null },
+                {"[\"a\",\"b\",\"c\"] = [\"a\",\"b\"]", Boolean.FALSE, null },
+
+                {"[1,2,3] in = [1,2,3]", Boolean.TRUE, null },
+                {"[1,2,3] in [[1,2,3], [4,5,6]]", Boolean.TRUE, null },
+                {"[1,2,3] in [[1,2,3,4], [1,2]]", Boolean.FALSE, null },
+                {"3 in [1,2,3,4,5]", Boolean.TRUE, null },
+                {"10 in [1,2,3,4,5]", Boolean.FALSE, null },
+                {"null in [1,null,3]", Boolean.TRUE, null },
+                {"1 in []", Boolean.FALSE, null },
+                {"[\"a\",\"b\"] in [[\"a\",\"b\"], [\"c\",\"d\"]]", 
Boolean.TRUE, null }
         };
         return addAdditionalParameters(cases, false);
     }


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

Reply via email to