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 3f8d93f15d [incubator-kie issues#1794] Fix range' related TCKs
failures (#6244)
3f8d93f15d is described below
commit 3f8d93f15db370af1334ae131916d957dd5a4795
Author: Gabriele Cardosi <[email protected]>
AuthorDate: Wed Feb 5 17:32:07 2025 +0100
[incubator-kie issues#1794] Fix range' related TCKs failures (#6244)
* [incubator-kie-issues#1794] Descending ranges should return null (#1795)
* [incubator-kie-issues#1794] Range with both endpoint null should return
null (#1796)
* [incubator-kie-issues#1794] Range with mismatching null endpoint and open
closed flag should return null (#1797)
* [incubator-kie-issues#1794] Range with whitespaces should be correctly
parsed (#1798)
---------
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../NullContentInsideForIterationException.java | 27 ++++++
.../kie/dmn/feel/lang/ast/ForExpressionNode.java | 11 ++-
.../java/org/kie/dmn/feel/lang/ast/RangeNode.java | 11 ++-
.../dmn/feel/runtime/functions/RangeFunction.java | 44 +++++----
.../dmn/feel/lang/ast/ForExpressionNodeTest.java | 16 +++-
.../org/kie/dmn/feel/lang/ast/RangeNodeTest.java | 105 +++++++++++++++++++++
.../feel/runtime/FEELConditionsAndLoopsTest.java | 2 +
.../kie/dmn/feel/runtime/FEELFunctionsTest.java | 11 ++-
.../org/kie/dmn/feel/runtime/FEELRangesTest.java | 4 +-
.../feel/runtime/functions/RangeFunctionTest.java | 36 +++++--
.../dmn/feel/util/ExpressionNodeFactoryUtils.java | 22 ++++-
11 files changed, 251 insertions(+), 38 deletions(-)
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/NullContentInsideForIterationException.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/NullContentInsideForIterationException.java
new file mode 100644
index 0000000000..95db8ff228
--- /dev/null
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/NullContentInsideForIterationException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.exceptions;
+
+import java.io.Serial;
+
+public class NullContentInsideForIterationException extends RuntimeException {
+
+ @Serial
+ private static final long serialVersionUID = -1400399311677539243L;
+}
\ No newline at end of file
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 e8b32efdbf..9b1aae2a97 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
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -21,6 +21,7 @@ package org.kie.dmn.feel.lang.ast;
import org.antlr.v4.runtime.ParserRuleContext;
import org.kie.dmn.feel.exceptions.EndpointOfForIterationNotValidTypeException;
import
org.kie.dmn.feel.exceptions.EndpointOfForIterationDifferentTypeException;
+import org.kie.dmn.feel.exceptions.NullContentInsideForIterationException;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.Type;
import org.kie.dmn.feel.lang.ast.forexpressioniterators.ForIteration;
@@ -83,7 +84,7 @@ public class ForExpressionNode
populateToReturn(0, ctx, toReturn);
LOG.trace("returning {}", toReturn);
return toReturn;
- } catch (EndpointOfForIterationNotValidTypeException |
EndpointOfForIterationDifferentTypeException e) {
+ } catch (EndpointOfForIterationNotValidTypeException |
EndpointOfForIterationDifferentTypeException |
NullContentInsideForIterationException e) {
// ast error already reported
return null;
} finally {
@@ -126,14 +127,16 @@ public class ForExpressionNode
return BuiltInType.LIST;
}
- private ForIteration createForIteration(EvaluationContext ctx,
IterationContextNode iterationContextNode) {
+ private ForIteration createForIteration(EvaluationContext ctx,
IterationContextNode iterationContextNode) throws
NullContentInsideForIterationException {
LOG.trace("Creating ForIteration for {}", iterationContextNode);
ForIteration toReturn = null;
String name = iterationContextNode.evaluateName(ctx);
Object result = iterationContextNode.evaluate(ctx);
Object rangeEnd = iterationContextNode.evaluateRangeEnd(ctx);
if (rangeEnd == null) {
- if (result instanceof Iterable iterable) {
+ if (result == null) {
+ throw new NullContentInsideForIterationException();
+ } else if (result instanceof Iterable iterable) {
toReturn = new ForIteration(name, iterable);
} else if (result instanceof Range) {
toReturn = getForIteration(ctx, name, ((Range)
result).getStart(), ((Range) result).getEnd());
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/RangeNode.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/RangeNode.java
index 0d44d025d7..5ef569e051 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/RangeNode.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/RangeNode.java
@@ -111,6 +111,9 @@ public class RangeNode
public Range evaluate(EvaluationContext ctx) {
Object s = start.evaluate( ctx );
Object e = end.evaluate( ctx );
+ if (s == null && e == null) {
+ return null;
+ }
Type sType = BuiltInType.determineTypeFromInstance(s);
Type eType = BuiltInType.determineTypeFromInstance(e);
@@ -125,13 +128,17 @@ public class RangeNode
Comparable start = s instanceof UndefinedValueComparable ?
(Comparable) s : convertToComparable(ctx, s );
Comparable end = e instanceof UndefinedValueComparable ? (Comparable)
e : convertToComparable( ctx, e );
-
- return new RangeImpl( lowerBound==IntervalBoundary.OPEN ?
Range.RangeBoundary.OPEN : Range.RangeBoundary.CLOSED,
+ return isDescendingRange(start, end) ? null :
+ new RangeImpl( lowerBound==IntervalBoundary.OPEN ?
Range.RangeBoundary.OPEN : Range.RangeBoundary.CLOSED,
start,
end,
upperBound==IntervalBoundary.OPEN ?
Range.RangeBoundary.OPEN : Range.RangeBoundary.CLOSED );
}
+ static boolean isDescendingRange(Comparable start, Comparable end) {
+ return (start == null || start instanceof UndefinedValueComparable ||
end == null || end instanceof UndefinedValueComparable) ? false :
start.compareTo(end) > 0;
+ }
+
private Comparable convertToComparable(EvaluationContext ctx, Object s) {
Comparable start;
if (s == null) {
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/RangeFunction.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/RangeFunction.java
index b6601c462b..fe4719167f 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/RangeFunction.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/RangeFunction.java
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -79,33 +79,33 @@ public class RangeFunction extends BaseFEELFunction {
object -> object instanceof OffsetTime,
object -> object instanceof LocalTime);
-
private RangeFunction() {
super("range");
}
public FEELFnResult<Range> invoke(@ParameterName("from") String from) {
- if (from == null || from.isEmpty() || from.isBlank()) {
+ if (from == null || from.isBlank()) {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "cannot be null"));
}
+ String fromToUse = from.trim();
Range.RangeBoundary startBoundary;
- if (from.startsWith("(") || from.startsWith("]")) {
+ if (fromToUse.startsWith("(") || fromToUse.startsWith("]")) {
startBoundary = RangeBoundary.OPEN;
- } else if (from.startsWith("[")) {
+ } else if (fromToUse.startsWith("[")) {
startBoundary = RangeBoundary.CLOSED;
} else {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "does not start with a
valid character"));
}
Range.RangeBoundary endBoundary;
- if (from.endsWith(")") || from.endsWith("[")) {
+ if (fromToUse.endsWith(")") || fromToUse.endsWith("[")) {
endBoundary = RangeBoundary.OPEN;
- } else if (from.endsWith("]")) {
+ } else if (fromToUse.endsWith("]")) {
endBoundary = RangeBoundary.CLOSED;
} else {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "does not end with a
valid character"));
}
- String[] split = from.split("\\.\\.");
+ String[] split = fromToUse.split("\\.\\.");
if (split.length != 2) {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "does not include two
literals separated by `..` two dots characters"));
}
@@ -115,11 +115,11 @@ public class RangeFunction extends BaseFEELFunction {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "at least one endpoint
must not be null"));
}
BaseNode leftNode = parse(leftString);
- if (!nodeIsAllowed(leftNode)) {
+ if (!nodeIsAllowed(leftNode, startBoundary)) {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "left endpoint is not
a recognised valid literal"));
}
BaseNode rightNode = parse(rightString);
- if (!nodeIsAllowed(rightNode)) {
+ if (!nodeIsAllowed(rightNode, endBoundary)) {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "right endpoint is not
a recognised valid literal"));
}
Object left = leftNode.evaluate(getStubbed());
@@ -135,9 +135,10 @@ public class RangeFunction extends BaseFEELFunction {
if (!nodesReturnsSameType(left, right)) {
return FEELFnResult.ofError(new
InvalidParametersEvent(FEELEvent.Severity.ERROR, "from", "endpoints must be of
equivalent types"));
}
-
- // Boundary values need to be always defined in range string. They can
be undefined only in unary test, that represents range, e.g. (<10).
- return FEELFnResult.ofResult(new RangeImpl(startBoundary, (Comparable)
left, (Comparable) right, endBoundary));
+ Range toReturn = getReturnedValue(left, right, startBoundary,
endBoundary);
+ // Boundary values need to be always defined in range string. They can
be undefined only in unary test, that
+ // represents range, e.g. (<10).
+ return FEELFnResult.ofResult(toReturn);
}
@Override
@@ -145,8 +146,18 @@ public class RangeFunction extends BaseFEELFunction {
return DEFAULT_VALUE;
}
- protected boolean nodeIsAllowed(BaseNode node) {
- return ALLOWED_NODES.stream().anyMatch(baseNodePredicate ->
baseNodePredicate.test(node));
+ static Range getReturnedValue(Object left, Object right,
Range.RangeBoundary startBoundary,
+ Range.RangeBoundary endBoundary) {
+ return (left == null && right == null) ? null :
+ new RangeImpl(startBoundary, (Comparable) left, (Comparable)
right, endBoundary);
+ }
+
+ protected boolean nodeIsAllowed(BaseNode node, RangeBoundary boundary) {
+ if (node instanceof NullNode && boundary.equals(RangeBoundary.CLOSED))
{
+ return false;
+ } else {
+ return ALLOWED_NODES.stream().anyMatch(baseNodePredicate ->
baseNodePredicate.test(node));
+ }
}
protected boolean nodeValueIsAllowed(Object value) {
@@ -171,7 +182,7 @@ public class RangeFunction extends BaseFEELFunction {
}
protected BaseNode parse(String input) {
- return input.isEmpty() || input.isBlank() ? getNullNode() :
parseNotEmptyInput(input);
+ return input.isEmpty() || input.isBlank() ? getNullNode() :
parseNotEmptyInput(input);
}
protected BaseNode getNullNode() {
@@ -194,5 +205,4 @@ public class RangeFunction extends BaseFEELFunction {
}
return STUBBED;
}
-
}
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 fc7e9b17d0..2b28ee57bf 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
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -31,11 +31,12 @@ import org.kie.dmn.feel.util.EvaluationContextTestUtil;
import org.kie.dmn.feel.lang.types.BuiltInType;
import static org.assertj.core.api.Assertions.assertThat;
+import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getAtLiteralRangeNode;
import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getIterationContextNode;
import static org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getListNode;
import static org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getNameRefNode;
import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getNestedListNode;
-import static org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getRangeNode;
+import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getLocalDateRangeNode;
class ForExpressionNodeTest {
@@ -49,6 +50,15 @@ class ForExpressionNodeTest {
containsExactly(BigDecimal.ONE, BigDecimal.valueOf(2),
BigDecimal.valueOf(3), BigDecimal.valueOf(4));
}
+ @Test
+ void evaluateDescendingRange() {
+ RangeNode rangeNode =
getAtLiteralRangeNode("[@\"1980-01-03T00:00:00\"..@\"1980-01-01T00:00:00\"]",
"1980-01-03T00:00:00", "1980-01-01T00:00:00",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED);
+ IterationContextNode i = getIterationContextNode("i", rangeNode, "i in
[@\"1980-01-03T00:00:00\"..@\"1980-01-01T00:00:00\"]");
+ ForExpressionNode forExpressionNode = new
ForExpressionNode(Collections.singletonList(i),
getNameRefNode(BuiltInType.UNKNOWN, "i"), "for i in
[@\"1980-01-03T00:00:00\"..@\"1980-01-01T00:00:00\"] return i");
+ Object retrieved =
forExpressionNode.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext());
+ assertThat(retrieved).isNull();
+ }
+
@Test
void evaluateNestedArray() {
Map<String, List<String>> firstIterationContext = new
LinkedHashMap<>();
@@ -65,7 +75,7 @@ class ForExpressionNodeTest {
@Test
void evaluateRange() {
- IterationContextNode x = getIterationContextNode("x",
getRangeNode("[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]");
+ 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");
Object retrieved =
forExpressionNode.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext());
assertThat(retrieved).isInstanceOf(List.class).asList().containsExactly(LocalDate.of(1980,
1, 1),
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/RangeNodeTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/RangeNodeTest.java
new file mode 100644
index 0000000000..d7f9e7e174
--- /dev/null
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/RangeNodeTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.kie.dmn.feel.runtime.impl.RangeImpl;
+import org.kie.dmn.feel.runtime.impl.UndefinedValueComparable;
+import org.kie.dmn.feel.util.EvaluationContextTestUtil;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.kie.dmn.feel.lang.ast.RangeNode.isDescendingRange;
+import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getAtLiteralRangeNode;
+import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getLocalDateRangeNode;
+import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getNumericRangeNode;
+import static
org.kie.dmn.feel.util.ExpressionNodeFactoryUtils.getStringRangeNode;
+
+class RangeNodeTest {
+
+ @ParameterizedTest
+ @MethodSource("ascendingRanges")
+ void evaluateAscendingRanges(RangeNode toTest) {
+ assertThat(toTest).isNotNull();
+
assertThat(toTest.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext()))
+ .isNotNull()
+ .isInstanceOf(RangeImpl.class);
+ }
+
+ @ParameterizedTest
+ @MethodSource("descendingRanges")
+ void evaluateDescendingRanges(RangeNode toTest) {
+ assertThat(toTest).isNotNull();
+
assertThat(toTest.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext())).isNull();
+ }
+
+ @Test
+ void isDescendingRangeTest() {
+ assertThat(isDescendingRange(BigDecimal.ONE,
BigDecimal.TEN)).isFalse();
+ assertThat(isDescendingRange(new UndefinedValueComparable(),
BigDecimal.TEN)).isFalse();
+ assertThat(isDescendingRange(BigDecimal.ONE, new
UndefinedValueComparable())).isFalse();
+ assertThat(isDescendingRange(new UndefinedValueComparable(), new
UndefinedValueComparable())).isFalse();
+ assertThat(isDescendingRange(BigDecimal.ONE,
BigDecimal.ONE)).isFalse();
+ assertThat(isDescendingRange(BigDecimal.TEN, BigDecimal.ONE)).isTrue();
+
+ assertThat(isDescendingRange(null, BigDecimal.TEN)).isFalse();
+ assertThat(isDescendingRange(BigDecimal.ONE, null)).isFalse();
+ assertThat(isDescendingRange(null, null)).isFalse();
+ assertThat(isDescendingRange(BigDecimal.ONE,
BigDecimal.ONE)).isFalse();
+ assertThat(isDescendingRange(BigDecimal.TEN, BigDecimal.ONE)).isTrue();
+ }
+
+ @Test
+ void isNullNullRangeTest() {
+ BaseNode startNode = new NullNode("null");
+ BaseNode endNode = new NullNode("null");
+ RangeNode rangeNode = new RangeNode(RangeNode.IntervalBoundary.CLOSED,
RangeNode.IntervalBoundary.CLOSED,
+ startNode, endNode,
"[null..null]");
+ assertThat(rangeNode).isNotNull();
+
assertThat(rangeNode.evaluate(EvaluationContextTestUtil.newEmptyEvaluationContext())).isNull();
+ }
+
+ private static Collection<RangeNode> ascendingRanges() {
+ return Arrays.asList(
+ getNumericRangeNode("[1..3]", BigDecimal.ONE,
BigDecimal.valueOf(3), RangeNode.IntervalBoundary.CLOSED,
RangeNode.IntervalBoundary.CLOSED),
+ getLocalDateRangeNode("[@\"1970-01-01\"..@\"1970-01-02\"]",
LocalDate.of(1970, 1, 1), LocalDate.of(1970, 1, 2),
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getStringRangeNode("[\"a\"..\"z\"]", "a", "z",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getAtLiteralRangeNode("[@\"P1D\"..@\"P2D\"]", "P1D", "P2D",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getAtLiteralRangeNode("[@\"P1Y\"..@\"P2Y\"]", "P1Y", "P2Y",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getAtLiteralRangeNode("[@\"01:00:00\"..@\"02:00:00\"]",
"01:00:00", "02:00:00", RangeNode.IntervalBoundary.CLOSED,
RangeNode.IntervalBoundary.CLOSED)
+ );
+ }
+
+ private static Collection<RangeNode> descendingRanges() {
+ return Arrays.asList(
+ getNumericRangeNode("[3..1]", BigDecimal.valueOf(3),
BigDecimal.ONE, RangeNode.IntervalBoundary.CLOSED,
RangeNode.IntervalBoundary.CLOSED),
+ getLocalDateRangeNode("[@\"1970-01-02\"..@\"1970-01-01\"]",
LocalDate.of(1970, 1, 2), LocalDate.of(1970, 1, 1),
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getStringRangeNode("[\"z\"..\"a\"]", "z", "a",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getAtLiteralRangeNode("[@\"P2D\"..@\"P1D\"]", "P2D", "P1D",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getAtLiteralRangeNode("[@\"P2Y\"..@\"P1Y\"]", "P2Y", "P1Y",
RangeNode.IntervalBoundary.CLOSED, RangeNode.IntervalBoundary.CLOSED),
+ getAtLiteralRangeNode("[@\"02:00:00\"..@\"01:00:00\"]",
"02:00:00", "01:00:00", RangeNode.IntervalBoundary.CLOSED,
RangeNode.IntervalBoundary.CLOSED)
+ );
+ }
+}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELConditionsAndLoopsTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELConditionsAndLoopsTest.java
index eca0fe4ef5..46719d5cab 100644
---
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELConditionsAndLoopsTest.java
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELConditionsAndLoopsTest.java
@@ -51,6 +51,8 @@ public class FEELConditionsAndLoopsTest extends BaseFEELTest {
{"for x in [ 10, 20, 30 ], y in [ 1, 2, 3 ] return x * y",
Stream.of(10, 20, 30, 20, 40, 60, 30, 60, 90
).map(BigDecimal::valueOf).collect(Collectors.toList() ),
null },
+ {"for i in
[@\"1980-01-03T00:00:00\"..@\"1980-01-01T00:00:00\"] return i", null,
+ null },
{"count( for x in [1, 2, 3] return x+1 )", BigDecimal.valueOf(
3 ), null},
// quantified
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
index a199e8f1f6..08d3d22cd4 100644
---
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELFunctionsTest.java
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -30,6 +30,7 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.kie.dmn.api.feel.runtime.events.FEELEvent;
import org.kie.dmn.feel.lang.FEELDialect;
import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
+import org.kie.dmn.feel.runtime.impl.RangeImpl;
public class FEELFunctionsTest extends BaseFEELTest {
@@ -306,7 +307,13 @@ public class FEELFunctionsTest extends BaseFEELTest {
{"list replace ( [2, 4, 7, 8], -3, 6)",
Arrays.asList(BigDecimal.valueOf(2), BigDecimal.valueOf(6),
BigDecimal.valueOf(7), BigDecimal.valueOf(8)), null},
{"list replace ( [2, 4, 7, 8], function(item, newItem) item +
newItem, 6)", null , FEELEvent.Severity.ERROR},
{"list replace ( [\"El-1\", \"El-2\", \"El-3\", \"El-4\"],
function(item, newItem) item = \"El-2\", null)", Arrays.asList("El-1", null,
"El-3", "El-4"), null},
- {"list replace ( [2, 4, 7, 8], function(item, newItem) item <
newItem, 5)", Arrays.asList(BigDecimal.valueOf(5), BigDecimal.valueOf(5),
BigDecimal.valueOf(7), BigDecimal.valueOf(8)), null}
+ {"list replace ( [2, 4, 7, 8], function(item, newItem) item <
newItem, 5)", Arrays.asList(BigDecimal.valueOf(5), BigDecimal.valueOf(5),
BigDecimal.valueOf(7), BigDecimal.valueOf(8)), null},
+
+ // ranges
+ {"range( \"[ 1 .. 3 ]\" )", new
RangeImpl(Range.RangeBoundary.CLOSED, BigDecimal.ONE, BigDecimal.valueOf(3),
Range.RangeBoundary.CLOSED), null},
+ {"range(\"[null..null]\")", null, FEELEvent.Severity.ERROR},
+ {"range(\"[null..2]\")", null, FEELEvent.Severity.ERROR},
+ {"range(\"[1..null]\")", null, FEELEvent.Severity.ERROR},
};
return addAdditionalParameters(cases, false);
}
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELRangesTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELRangesTest.java
index 601bd26f84..506ae00c07 100644
---
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELRangesTest.java
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/FEELRangesTest.java
@@ -51,13 +51,15 @@ public class FEELRangesTest extends BaseFEELTest {
{"(<=1)", new RangeImpl(Range.RangeBoundary.OPEN, new
UndefinedValueComparable(), BigDecimal.ONE, Range.RangeBoundary.CLOSED), null},
{"(null..10)", new RangeImpl(Range.RangeBoundary.OPEN, null,
BigDecimal.valueOf(10), Range.RangeBoundary.OPEN), null},
+ {"[null..null]", null, null},
{"[1..2]", new RangeImpl(Range.RangeBoundary.CLOSED,
BigDecimal.ONE, BigDecimal.valueOf(2), Range.RangeBoundary.CLOSED), null},
- {"[2..1]", new RangeImpl(Range.RangeBoundary.CLOSED,
BigDecimal.valueOf(2), BigDecimal.ONE, Range.RangeBoundary.CLOSED), null},
+ {"[2..1]", null, null},
{"[1..2)", new RangeImpl(Range.RangeBoundary.CLOSED,
BigDecimal.ONE, BigDecimal.valueOf(2), Range.RangeBoundary.OPEN), null},
{"(1..2]", new RangeImpl(Range.RangeBoundary.OPEN,
BigDecimal.ONE, BigDecimal.valueOf(2), Range.RangeBoundary.CLOSED), null},
{"(1..2)", new RangeImpl(Range.RangeBoundary.OPEN,
BigDecimal.ONE, BigDecimal.valueOf(2), Range.RangeBoundary.OPEN), null},
{"[\"a\"..\"z\"]", new RangeImpl(Range.RangeBoundary.CLOSED,
"a", "z", Range.RangeBoundary.CLOSED), null},
+ {"[\"z\"..\"a\"]", null, null},
{"[\"a\"..\"z\")", new RangeImpl(Range.RangeBoundary.CLOSED,
"a", "z", Range.RangeBoundary.OPEN), null},
{"(\"a\"..\"z\"]", new RangeImpl(Range.RangeBoundary.OPEN,
"a", "z", Range.RangeBoundary.CLOSED), null},
{"(\"a\"..\"z\")", new RangeImpl(Range.RangeBoundary.OPEN,
"a", "z", Range.RangeBoundary.OPEN), null},
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/RangeFunctionTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/RangeFunctionTest.java
index 2ed5fc7d4a..93f05e4378 100644
---
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/RangeFunctionTest.java
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/RangeFunctionTest.java
@@ -103,6 +103,10 @@ class RangeFunctionTest {
new
ComparablePeriod(Period.parse("P2Y6M")),
Range.RangeBoundary.OPEN),
from);
+ from = "[..2]";
+ FunctionTestUtil.assertResultError(rangeFunction.invoke(from),
+ InvalidParametersEvent.class,
+ from);
}
@Test
@@ -137,6 +141,10 @@ class RangeFunctionTest {
null,
Range.RangeBoundary.OPEN),
from);
+ from = "[1..]";
+ FunctionTestUtil.assertResultError(rangeFunction.invoke(from),
+ InvalidParametersEvent.class,
+ from);
}
@Test
@@ -260,26 +268,40 @@ class RangeFunctionTest {
from);
}
+ @Test
+ void invoke_WithWhiteSpaces() {
+ String from = "[ 1 .. 3 ]";
+ FunctionTestUtil.assertResult(rangeFunction.invoke(from),
+ new
RangeImpl(Range.RangeBoundary.CLOSED, BigDecimal.ONE, BigDecimal.valueOf(3),
Range.RangeBoundary.CLOSED),
+ from);
+ from = " [ 1 .. 3 ] ";
+ FunctionTestUtil.assertResult(rangeFunction.invoke(from),
+ new
RangeImpl(Range.RangeBoundary.CLOSED, BigDecimal.ONE, BigDecimal.valueOf(3),
Range.RangeBoundary.CLOSED),
+ from);
+ }
+
@Test
void nodeIsAllowed_True() {
BaseNode node = rangeFunction.getNullNode();
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isTrue();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isTrue();
node = getNumberNode();
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isTrue();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isTrue();
node = getStringNode();
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isTrue();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isTrue();
node = getAtLiteralNode();
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isTrue();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isTrue();
node = getFunctionInvocationNodeA();
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isTrue();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isTrue();
}
@Test
void nodeIsAllowed_False() {
BaseNode node = rangeFunction.parse("if(true)");
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isFalse();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isFalse();
node = getBooleanNode();
-
assertThat(rangeFunction.nodeIsAllowed(node)).withFailMessage(node.getText()).isFalse();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.OPEN)).withFailMessage(node.getText()).isFalse();
+ node = rangeFunction.getNullNode();
+ assertThat(rangeFunction.nodeIsAllowed(node,
Range.RangeBoundary.CLOSED)).withFailMessage(node.getText()).isFalse();
}
@Test
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/ExpressionNodeFactoryUtils.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/ExpressionNodeFactoryUtils.java
index f7afe8f0c7..1720e061f7 100644
---
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/ExpressionNodeFactoryUtils.java
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/ExpressionNodeFactoryUtils.java
@@ -1,4 +1,4 @@
-/**
+/*
* 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
@@ -19,6 +19,7 @@
package org.kie.dmn.feel.util;
import org.kie.dmn.feel.lang.Type;
+import org.kie.dmn.feel.lang.ast.AtLiteralNode;
import org.kie.dmn.feel.lang.ast.BaseNode;
import org.kie.dmn.feel.lang.ast.FunctionInvocationNode;
import org.kie.dmn.feel.lang.ast.IterationContextNode;
@@ -81,13 +82,30 @@ public class ExpressionNodeFactoryUtils {
return new TemporalConstantNode(value, null, null, null);
}
- public static RangeNode getRangeNode(String text, LocalDate start,
LocalDate end, RangeNode.IntervalBoundary lowerBound,
RangeNode.IntervalBoundary upperBound) {
+ public static RangeNode getLocalDateRangeNode(String text, LocalDate
start, LocalDate end, RangeNode.IntervalBoundary lowerBound,
RangeNode.IntervalBoundary upperBound) {
BaseNode nameRefNode = getNameRefNode(BuiltInType.DATE, "x");
ListNode startParams = getListNode(start.toString(),
List.of(start.toString()));
ListNode endParams = getListNode(end.toString(),
List.of(end.toString()));
BaseNode startNode = new FunctionInvocationNode(nameRefNode,
startParams, getTemporalConstantNode(start), start.toString());
BaseNode endNode = new FunctionInvocationNode(nameRefNode, endParams,
getTemporalConstantNode(end), end.toString());
+ return new RangeNode(lowerBound, upperBound, startNode, endNode, text);
+ }
+
+ public static RangeNode getNumericRangeNode(String text, BigDecimal start,
BigDecimal end, RangeNode.IntervalBoundary lowerBound,
RangeNode.IntervalBoundary upperBound) {
+ BaseNode startNode = new NumberNode(start, start.toString());
+ BaseNode endNode = new NumberNode(end, end.toString());
+ return new RangeNode(lowerBound, upperBound, startNode, endNode, text);
+ }
+
+ public static RangeNode getStringRangeNode(String text, String start,
String end, RangeNode.IntervalBoundary lowerBound, RangeNode.IntervalBoundary
upperBound) {
+ BaseNode startNode = new StringNode(start);
+ BaseNode endNode = new StringNode(end);
+ return new RangeNode(lowerBound, upperBound, startNode, endNode, text);
+ }
+ public static RangeNode getAtLiteralRangeNode(String text, String start,
String end, RangeNode.IntervalBoundary lowerBound, RangeNode.IntervalBoundary
upperBound) {
+ BaseNode startNode = new AtLiteralNode(new StringNode(start), start);
+ BaseNode endNode = new AtLiteralNode(new StringNode(end), end);
return new RangeNode(lowerBound, upperBound, startNode, endNode, text);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]