From f5452b7ff17171278d26c6756292789a6e225459 Mon Sep 17 00:00:00 2001 From: AthiraHari77 Date: Tue, 21 Jan 2025 12:14:43 +0530 Subject: [PATCH] [Incubator kie issues#1743]Return a list of dates instead of a range (#6223) * [incubator-kie-issues-162105] Code fix to return list of dates * [incubator-kie-issues-162105] WIP * [incubator-kie-issues-162105] WIP * [incubator-kie-issues#1743] Updated test cases * [incubator-kie-issues#1743] Code refactoring * [incubator-kie-issues#1743] Code refactoring * [incubator-kie-issues#1743] Code clean up * [incubator-kie-issues#1743] Updated test cases * [incubator-kie-issues#1743] Code fixes * [incubator-kie-issues#1743] Code clean up * [incubator-kie-issues#1743] Fix review comments * [incubator-kie-issues#1743] Code fix * [incubator-kie-issues#1743] Fix review comments * [incubator-kie-issues#1743] Fix merge conflicts * [incubator-kie-issues#1743] Fix broken testcases and remove unused classes --------- Co-authored-by: athira --- ...OfForIterationDifferentTypeException.java} | 2 +- ...tOfForIterationNotValidTypeException.java} | 2 +- .../EndpointOfRangeNotOfNumberException.java | 22 ---- .../dmn/feel/lang/ast/ForExpressionNode.java | 18 ++- .../ForIterationUtils.java | 17 +-- .../java/org/kie/dmn/feel/runtime/Range.java | 8 ++ .../kie/dmn/feel/runtime/impl/RangeImpl.java | 30 +++++ .../main/java/org/kie/dmn/feel/util/Msg.java | 1 + .../feel/lang/ast/ForExpressionNodeTest.java | 41 ++----- .../ForIterationUtilsTest.java | 20 +-- .../dmn/feel/runtime/impl/RangeImplTest.java | 116 ++++++++++++++++++ .../feel/util/ExpressionNodeFactoryUtils.java | 94 ++++++++++++++ 12 files changed, 295 insertions(+), 76 deletions(-) rename kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/{EndpointOfRangeOfDifferentTypeException.java => EndpointOfForIterationDifferentTypeException.java} (91%) rename kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/{EndpointOfRangeNotValidTypeException.java => EndpointOfForIterationNotValidTypeException.java} (91%) delete mode 100644 kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotOfNumberException.java create mode 100644 kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/ExpressionNodeFactoryUtils.java diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeOfDifferentTypeException.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfForIterationDifferentTypeException.java similarity index 91% rename from kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeOfDifferentTypeException.java rename to kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfForIterationDifferentTypeException.java index 4c62799beae..fd762a0e7af 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeOfDifferentTypeException.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfForIterationDifferentTypeException.java @@ -18,6 +18,6 @@ */ package org.kie.dmn.feel.exceptions; -public class EndpointOfRangeOfDifferentTypeException extends RuntimeException { +public class EndpointOfForIterationDifferentTypeException extends RuntimeException { private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotValidTypeException.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfForIterationNotValidTypeException.java similarity index 91% rename from kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotValidTypeException.java rename to kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfForIterationNotValidTypeException.java index f49115f618f..9e1b3d69e6c 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotValidTypeException.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfForIterationNotValidTypeException.java @@ -18,6 +18,6 @@ */ package org.kie.dmn.feel.exceptions; -public class EndpointOfRangeNotValidTypeException extends RuntimeException { +public class EndpointOfForIterationNotValidTypeException extends RuntimeException { private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotOfNumberException.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotOfNumberException.java deleted file mode 100644 index 3c6ce52b788..00000000000 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/exceptions/EndpointOfRangeNotOfNumberException.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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; - -public class EndpointOfRangeNotOfNumberException extends RuntimeException { -} \ 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 1229c354457..e8b32efdbff 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 @@ -19,12 +19,13 @@ package org.kie.dmn.feel.lang.ast; import org.antlr.v4.runtime.ParserRuleContext; -import org.kie.dmn.feel.exceptions.EndpointOfRangeNotValidTypeException; -import org.kie.dmn.feel.exceptions.EndpointOfRangeOfDifferentTypeException; +import org.kie.dmn.feel.exceptions.EndpointOfForIterationNotValidTypeException; +import org.kie.dmn.feel.exceptions.EndpointOfForIterationDifferentTypeException; import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.lang.Type; import org.kie.dmn.feel.lang.ast.forexpressioniterators.ForIteration; import org.kie.dmn.feel.lang.types.BuiltInType; +import org.kie.dmn.feel.runtime.Range; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -82,7 +83,7 @@ public Object evaluate(EvaluationContext ctx) { populateToReturn(0, ctx, toReturn); LOG.trace("returning {}", toReturn); return toReturn; - } catch (EndpointOfRangeNotValidTypeException | EndpointOfRangeOfDifferentTypeException e) { + } catch (EndpointOfForIterationNotValidTypeException | EndpointOfForIterationDifferentTypeException e) { // ast error already reported return null; } finally { @@ -127,13 +128,18 @@ public Type getResultType() { private ForIteration createForIteration(EvaluationContext ctx, IterationContextNode iterationContextNode) { LOG.trace("Creating ForIteration for {}", iterationContextNode); - ForIteration toReturn; + ForIteration toReturn = null; String name = iterationContextNode.evaluateName(ctx); Object result = iterationContextNode.evaluate(ctx); Object rangeEnd = iterationContextNode.evaluateRangeEnd(ctx); if (rangeEnd == null) { - Iterable values = result instanceof Iterable iterable? iterable : Collections.singletonList(result); - toReturn = new ForIteration(name, values); + 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()); + } else { + toReturn = new ForIteration(name, Collections.singletonList(result)); + } } else { toReturn = getForIteration(ctx, name, result, rangeEnd); } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtils.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtils.java index 2e02c5df99c..2c53802edbf 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtils.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtils.java @@ -22,8 +22,8 @@ import java.time.LocalDate; import org.kie.dmn.api.feel.runtime.events.FEELEvent; -import org.kie.dmn.feel.exceptions.EndpointOfRangeNotValidTypeException; -import org.kie.dmn.feel.exceptions.EndpointOfRangeOfDifferentTypeException; +import org.kie.dmn.feel.exceptions.EndpointOfForIterationDifferentTypeException; +import org.kie.dmn.feel.exceptions.EndpointOfForIterationNotValidTypeException; import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.runtime.events.ASTEventBase; import org.kie.dmn.feel.util.Msg; @@ -42,15 +42,15 @@ public static ForIteration getForIteration(EvaluationContext ctx, String name, O return new ForIteration(name, localDate, (LocalDate) end); } ctx.notifyEvt(() -> new ASTEventBase(FEELEvent.Severity.ERROR, - Msg.createMessage(Msg.VALUE_X_NOT_A_VALID_ENDPOINT_FOR_RANGE_BECAUSE_NOT_A_NUMBER_NOT_A_DATE, start), null)); - throw new EndpointOfRangeOfDifferentTypeException(); + Msg.createMessage(Msg.VALUE_X_NOT_A_VALID_ENDPOINT_FOR_FORITERATION_BECAUSE_NOT_A_NUMBER_NOT_A_DATE, start), null)); + throw new EndpointOfForIterationDifferentTypeException(); } static void validateValues(EvaluationContext ctx, Object start, Object end) { if (start.getClass() != end.getClass()) { ctx.notifyEvt(() -> new ASTEventBase(FEELEvent.Severity.ERROR, Msg.createMessage(Msg.X_TYPE_INCOMPATIBLE_WITH_Y_TYPE, start, end), null)); - throw new EndpointOfRangeOfDifferentTypeException(); + throw new EndpointOfForIterationDifferentTypeException(); } valueMustBeValid(ctx, start); valueMustBeValid(ctx, end); @@ -58,9 +58,10 @@ static void validateValues(EvaluationContext ctx, Object start, Object end) { static void valueMustBeValid(EvaluationContext ctx, Object value) { if (!(value instanceof BigDecimal) && !(value instanceof LocalDate)) { - ctx.notifyEvt(() -> new ASTEventBase(FEELEvent.Severity.ERROR, Msg.createMessage(Msg.VALUE_X_NOT_A_VALID_ENDPOINT_FOR_RANGE_BECAUSE_NOT_A_NUMBER_NOT_A_DATE, value), null)); - throw new EndpointOfRangeNotValidTypeException(); + ctx.notifyEvt(() -> new ASTEventBase(FEELEvent.Severity.ERROR, Msg.createMessage(Msg.VALUE_X_NOT_A_VALID_ENDPOINT_FOR_FORITERATION_BECAUSE_NOT_A_NUMBER_NOT_A_DATE, value), null)); + throw new EndpointOfForIterationNotValidTypeException(); } } - } + + diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/Range.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/Range.java index 6cf3c539c16..e9d73c23c3d 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/Range.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/Range.java @@ -20,6 +20,9 @@ import org.kie.dmn.feel.lang.FEELDialect; +import java.math.BigDecimal; +import java.time.LocalDate; + public interface Range { enum RangeBoundary { @@ -38,4 +41,9 @@ enum RangeBoundary { boolean isWithUndefined(); + Comparable getStart(); + + Comparable getEnd(); + + } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/impl/RangeImpl.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/impl/RangeImpl.java index 32208d87245..ce49c8cf6d7 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/impl/RangeImpl.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/impl/RangeImpl.java @@ -18,6 +18,8 @@ */ package org.kie.dmn.feel.runtime.impl; +import java.math.BigDecimal; +import java.time.LocalDate; import java.util.function.BiPredicate; import org.kie.dmn.feel.lang.FEELDialect; @@ -98,6 +100,34 @@ public boolean isWithUndefined() { return withUndefined; } + @Override + public Comparable getStart() { + if(lowEndPoint instanceof BigDecimal) { + BigDecimal start = (BigDecimal) lowEndPoint; + start = lowBoundary == Range.RangeBoundary.OPEN ? start.add(BigDecimal.ONE) : start; + return start; + } else if (lowEndPoint instanceof LocalDate) { + LocalDate start = (LocalDate) lowEndPoint; + start = lowBoundary == Range.RangeBoundary.OPEN ? start.plusDays(1) : start; + return start; + } + return lowEndPoint; + } + + @Override + public Comparable getEnd() { + if (highEndPoint instanceof BigDecimal) { + BigDecimal end = (BigDecimal) highEndPoint; + end = highBoundary == Range.RangeBoundary.OPEN ? end.subtract(BigDecimal.ONE) : end; + return end; + } else if (highEndPoint instanceof LocalDate) { + LocalDate end = (LocalDate) highEndPoint; + end = highBoundary == Range.RangeBoundary.OPEN ? end.minusDays(1) : end; + return end; + } + return highEndPoint; + } + private Boolean finiteRangeIncludes(FEELDialect feelDialect, Object param) { if (lowBoundary == RangeBoundary.OPEN && highBoundary == RangeBoundary.OPEN) { return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l, r) -> l.compareTo(r) < 0) , compare(feelDialect, highEndPoint, param, (l, r) -> l.compareTo(r) > 0), param); diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/Msg.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/Msg.java index 1eb8b5a01df..f1715d3cc01 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/Msg.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/Msg.java @@ -39,6 +39,7 @@ public final class Msg { public static final Message2 X_TYPE_INCOMPATIBLE_WITH_Y_TYPE = new Message2("%s type incompatible with %s type"); public static final Message1 INCOMPATIBLE_TYPE_FOR_RANGE = new Message1("Type %s can not be used in a range unary test"); public static final Message1 VALUE_X_NOT_A_VALID_ENDPOINT_FOR_RANGE_BECAUSE_NOT_A_NUMBER_NOT_A_DATE = new Message1("Value %s is not a valid endpoint for range, because neither a feel:number nor a feel:date"); + public static final Message1 VALUE_X_NOT_A_VALID_ENDPOINT_FOR_FORITERATION_BECAUSE_NOT_A_NUMBER_NOT_A_DATE = new Message1("Value %s is not a valid value for forIteration, because neither a feel:number nor a feel:date"); public static final Message1 EVALUATED_TO_NULL = new Message1("%s evaluated to null"); public static final Message1 IS_NULL = new Message1("%s is null"); public static final Message0 BASE_NODE_EVALUATE_CALLED = new Message0("BaseNode evaluate called"); 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 f09f03caa66..fc7e9b17d0d 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 @@ -19,6 +19,7 @@ package org.kie.dmn.feel.lang.ast; import java.math.BigDecimal; +import java.time.LocalDate; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; @@ -27,10 +28,14 @@ import org.junit.jupiter.api.Test; import org.kie.dmn.feel.util.EvaluationContextTestUtil; -import org.kie.dmn.feel.lang.Type; import org.kie.dmn.feel.lang.types.BuiltInType; import static org.assertj.core.api.Assertions.assertThat; +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; class ForExpressionNodeTest { @@ -58,32 +63,12 @@ void evaluateNestedArray() { } - private IterationContextNode getIterationContextNode(String variableName, BaseNode expression, String text) { - return new IterationContextNode(getNameDefNode(variableName), expression, null, text); - } - - private NameDefNode getNameDefNode(String text) { - return new NameDefNode(Collections.singletonList(text), null, text); - } - - private NameRefNode getNameRefNode(Type type, String text) { - return new NameRefNode(type, text); - } - - private ListNode getNestedListNode(String text, Map> values) { - List elements = values.entrySet() - .stream() - .map(entry -> getListNode(entry.getKey(), entry.getValue())) - .map(BaseNode.class::cast) - .toList(); - return new ListNode(elements, text); - } - - private ListNode getListNode(String text, List values) { - List elements = values.stream() - .map(value -> new NumberNode(new BigDecimal(value), value)) - .map(BaseNode.class::cast) - .toList(); - return new ListNode(elements, text); + @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]"); + 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), + LocalDate.of(1980, 1, 2), LocalDate.of(1980, 1, 3)); } } \ No newline at end of file diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java index 852ed9e2057..7b068a1d4c0 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java @@ -25,8 +25,8 @@ import org.junit.jupiter.api.Test; import org.kie.dmn.api.feel.runtime.events.FEELEvent; import org.kie.dmn.api.feel.runtime.events.FEELEventListener; -import org.kie.dmn.feel.exceptions.EndpointOfRangeNotValidTypeException; -import org.kie.dmn.feel.exceptions.EndpointOfRangeOfDifferentTypeException; +import org.kie.dmn.feel.exceptions.EndpointOfForIterationDifferentTypeException; +import org.kie.dmn.feel.exceptions.EndpointOfForIterationNotValidTypeException; import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.lang.impl.FEELEventListenersManager; import org.mockito.ArgumentCaptor; @@ -71,7 +71,7 @@ void getForIterationNotValidTest() { try { getForIteration(ctx, "iteration", "NOT", "VALID"); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeNotValidTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationNotValidTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); reset(listener); @@ -79,7 +79,7 @@ void getForIterationNotValidTest() { try { getForIteration(ctx, "iteration", BigDecimal.valueOf(1), LocalDate.of(2021, 1, 1)); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeOfDifferentTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationDifferentTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); reset(listener); @@ -87,7 +87,7 @@ void getForIterationNotValidTest() { try { getForIteration(ctx, "iteration", LocalDate.of(2021, 1, 1), BigDecimal.valueOf(1)); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeOfDifferentTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationDifferentTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); reset(listener); @@ -107,7 +107,7 @@ void valueMustBeValidFalseTest() { try { valueMustBeValid(ctx, "INVALID"); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeNotValidTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationNotValidTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); } @@ -126,7 +126,7 @@ void validateValuesFalseTest() { try { validateValues(ctx, "INVALID", "INVALID"); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeNotValidTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationNotValidTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); reset(listener); @@ -134,7 +134,7 @@ void validateValuesFalseTest() { try { validateValues(ctx, BigDecimal.valueOf(1), LocalDate.of(2021, 1, 1)); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeOfDifferentTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationDifferentTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); reset(listener); @@ -142,10 +142,10 @@ void validateValuesFalseTest() { try { validateValues(ctx, LocalDate.of(2021, 1, 1), BigDecimal.valueOf(1)); } catch (Exception e) { - assertThat(e).isInstanceOf(EndpointOfRangeOfDifferentTypeException.class); + assertThat(e).isInstanceOf(EndpointOfForIterationDifferentTypeException.class); final ArgumentCaptor captor = ArgumentCaptor.forClass(FEELEvent.class); verify(listener, times(1)).onEvent(captor.capture()); reset(listener); } } -} \ No newline at end of file +} diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/impl/RangeImplTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/impl/RangeImplTest.java index f81ddb68f7d..83aefa0d8b1 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/impl/RangeImplTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/impl/RangeImplTest.java @@ -22,6 +22,10 @@ import org.kie.dmn.feel.lang.FEELDialect; import org.kie.dmn.feel.runtime.Range; +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; + import static org.assertj.core.api.Assertions.assertThat; class RangeImplTest { @@ -160,4 +164,116 @@ void hashCodeTest() { rangeImpl2 = new RangeImpl(Range.RangeBoundary.CLOSED, 12, 17, Range.RangeBoundary.CLOSED); assertThat(rangeImpl2).doesNotHaveSameHashCodeAs(rangeImpl); } + + @Test + void getStartForBigDecimalRangeOpenBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, BigDecimal.TEN, BigDecimal.valueOf(20), Range.RangeBoundary.OPEN); + + Comparable expectedResult = BigDecimal.valueOf(11); + Comparable actualResult = rangeImpl.getStart(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getStartForBigDecimalRangeClosedBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, BigDecimal.TEN, BigDecimal.valueOf(20), Range.RangeBoundary.OPEN); + + Comparable expectedResult = BigDecimal.TEN; + Comparable actualResult = rangeImpl.getStart(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getEndForBigDecimalRangeOpenBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, BigDecimal.TEN, BigDecimal.valueOf(20), Range.RangeBoundary.OPEN); + + Comparable expectedResult = BigDecimal.valueOf(19); + Comparable actualResult = rangeImpl.getEnd(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getEndForBigDecimalRangeClosedBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, BigDecimal.TEN, BigDecimal.valueOf(20), Range.RangeBoundary.CLOSED); + + Comparable expectedResult = BigDecimal.valueOf(20); + Comparable actualResult = rangeImpl.getEnd(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getStartForLocalDateRangeOpenBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, LocalDate.of(2025, 1, 1), LocalDate.of(2025, 1, 7), Range.RangeBoundary.OPEN); + + Comparable expectedResult = LocalDate.of(2025, 1, 2); + Comparable actualResult = rangeImpl.getStart(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getStartForLocalDateRangeClosedBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, LocalDate.of(2025, 1, 1), LocalDate.of(2025, 1, 7), Range.RangeBoundary.OPEN); + + Comparable expectedResult = LocalDate.of(2025, 1, 1); + Comparable actualResult = rangeImpl.getStart(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getEndForLocalDateRangeOpenBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, LocalDate.of(2025, 1, 1), LocalDate.of(2025, 1, 7), Range.RangeBoundary.OPEN); + + Comparable expectedResult = LocalDate.of(2025, 1, 6); + Comparable actualResult = rangeImpl.getEnd(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getEndForLocalDateRangeClosedBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, LocalDate.of(2025, 1, 1), LocalDate.of(2025, 1, 7), Range.RangeBoundary.CLOSED); + + Comparable expectedResult = LocalDate.of(2025, 1, 7); + Comparable actualResult = rangeImpl.getEnd(); + assertThat(actualResult).isEqualTo(expectedResult); + } + + @Test + void getStartForStringRangeClosedBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, "a", "z", Range.RangeBoundary.OPEN); + + Comparable expectedResult = "a"; + Comparable actualResult = rangeImpl.getStart(); + assertThat(actualResult).isEqualTo(expectedResult); + + } + + @Test + void getEndForStringRangeOpenBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, "a", "z", Range.RangeBoundary.OPEN); + + Comparable expectedResult = "z"; + Comparable actualResult = rangeImpl.getEnd(); + assertThat(actualResult).isEqualTo(expectedResult); + + } + + @Test + void getStartForDurationRangeOpenBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, Duration.parse("P2DT20H14M"), Duration.parse("P3DT20H14M"), Range.RangeBoundary.CLOSED); + + Comparable expectedResult = Duration.parse("P2DT20H14M"); + Comparable actualResult = rangeImpl.getStart(); + assertThat(actualResult).isEqualTo(expectedResult); + + } + + @Test + void getEndForDurationRangeClosedBoundary() { + RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, Duration.parse("P2DT20H14M"), Duration.parse("P3DT20H14M"), Range.RangeBoundary.CLOSED); + + Comparable expectedResult = Duration.parse("P3DT20H14M"); + Comparable actualResult = rangeImpl.getEnd(); + assertThat(actualResult).isEqualTo(expectedResult); + + } } \ No newline at end of file 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 new file mode 100644 index 00000000000..f7afe8f0c78 --- /dev/null +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/ExpressionNodeFactoryUtils.java @@ -0,0 +1,94 @@ +/** + * 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.util; + +import org.kie.dmn.feel.lang.Type; +import org.kie.dmn.feel.lang.ast.BaseNode; +import org.kie.dmn.feel.lang.ast.FunctionInvocationNode; +import org.kie.dmn.feel.lang.ast.IterationContextNode; +import org.kie.dmn.feel.lang.ast.ListNode; +import org.kie.dmn.feel.lang.ast.NameDefNode; +import org.kie.dmn.feel.lang.ast.NameRefNode; +import org.kie.dmn.feel.lang.ast.NumberNode; +import org.kie.dmn.feel.lang.ast.RangeNode; +import org.kie.dmn.feel.lang.ast.StringNode; +import org.kie.dmn.feel.lang.ast.TemporalConstantNode; +import org.kie.dmn.feel.lang.types.BuiltInType; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class ExpressionNodeFactoryUtils { + + public static IterationContextNode getIterationContextNode(String variableName, BaseNode expression, String text) { + return new IterationContextNode(getNameDefNode(variableName), expression, null, text); + } + + public static NameDefNode getNameDefNode(String text) { + return new NameDefNode(Collections.singletonList(text), null, text); + } + + public static NameRefNode getNameRefNode(Type type, String text) { + return new NameRefNode(type, text); + } + + public static ListNode getNestedListNode(String text, Map> values) { + List elements = values.entrySet() + .stream() + .map(entry -> getListNode(entry.getKey(), entry.getValue())) + .map(BaseNode.class::cast) + .toList(); + return new ListNode(elements, text); + } + + public static ListNode getListNode(String text, List values) { + List elements = values.stream() + .map(value -> { + if (value.matches("-?\\d+(\\.\\d+)?")) { + return new NumberNode(new BigDecimal(value), value); + } else if (value.matches("\\d{4}-\\d{2}-\\d{2}")) { + return new StringNode(value); + } else { + return new StringNode(value); + } + }) + .map(BaseNode.class::cast) + .toList(); + + return new ListNode(elements, text); + } + + public static TemporalConstantNode getTemporalConstantNode(Object value) { + return new TemporalConstantNode(value, null, null, null); + } + + public static RangeNode getRangeNode(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); + } + +}