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]