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 3d6058675d [incubator-kie-issues#854] DMN - Add implicit type
conversion of date to date and time when necessary (#5664)
3d6058675d is described below
commit 3d6058675d3bd2c6d1b8f07257fa9b614a7fb91f
Author: Gabriele Cardosi <[email protected]>
AuthorDate: Wed Feb 7 15:23:16 2024 +0100
[incubator-kie-issues#854] DMN - Add implicit type conversion of date to
date and time when necessary (#5664)
* [private-bamoe-issues#244] DMN - Add implicit type conversion of date to
date and time when necessary
* [private-bamoe-issues#244] WIP
* [private-bamoe-issues#244] WIP
* [incubator-kie-issues#854] Experiment implicit date -> date-time
conversion
* [incubator-kie-issues#854] Remove implicit date -> date-time conversion
* [incubator-kie-issues#854] Remove implicit date -> date-time conversion
* [incubator-kie-issues#854] Converting value after its evaluation, when
needed
* [incubator-kie-issues#854] Minor modification on test
* [incubator-kie-issues#854] WIP
* [incubator-kie-issues#854] Centralize Coerce behaviour
* [incubator-kie-issues#854] Add tests. Removed strong assumption where
Date is always to be treated as DateTime (not true).
Removed redundant code on DMNType
* [incubator-kie-issues#854] Enforcing coercion inside lists
* [incubator-kie-issues#854] Enforcing coercion inside collections
---------
Co-authored-by: BAMOE CI <[email protected]>
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../org/kie/dmn/core/ast/DMNContextEvaluator.java | 12 +-
.../core/ast/DMNFunctionDefinitionEvaluator.java | 7 +-
.../org/kie/dmn/core/impl/CompositeTypeImpl.java | 2 +-
.../java/org/kie/dmn/core/impl/DMNRuntimeImpl.java | 12 +-
.../java/org/kie/dmn/core/util/CoerceUtil.java | 58 +++++++
.../test/java/org/kie/dmn/core/DMNRuntimeTest.java | 20 +++
.../kie/dmn/core/DMNStronglyTypedSupportTest.java | 1 +
.../kie/dmn/core/ast/DMNContextEvaluatorTest.java | 76 +++++++++
.../java/org/kie/dmn/core/util/CoerceUtilTest.java | 176 +++++++++++++++++++++
.../java/org/kie/dmn/core/util/DMNRuntimeUtil.java | 18 ++-
.../resources/org/kie/dmn/core/0007-date-time.dmn | 6 +
.../org/kie/dmn/core/DateToDateTimeFunction.dmn | 24 +++
.../feel/runtime/functions/BaseFEELFunction.java | 31 ++--
.../dmn/feel/runtime/functions/DateFunction.java | 6 +-
.../java/org/kie/dmn/feel/util/CoerceUtil.java | 90 +++++++++++
.../java/org/kie/dmn/feel/util/EvalHelper.java | 6 +
.../kie/dmn/feel/runtime/FEELFunctionsTest.java | 20 +--
.../java/org/kie/dmn/feel/util/CoerceUtilTest.java | 148 +++++++++++++++++
18 files changed, 655 insertions(+), 58 deletions(-)
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNContextEvaluator.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNContextEvaluator.java
index 9e710d761f..4fbb5c0c64 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNContextEvaluator.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNContextEvaluator.java
@@ -19,7 +19,6 @@
package org.kie.dmn.core.ast;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -45,6 +44,8 @@ import org.kie.dmn.model.api.FunctionDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.kie.dmn.core.util.CoerceUtil.coerceValue;
+
public class DMNContextEvaluator
implements DMNExpressionEvaluator {
private static final Logger logger = LoggerFactory.getLogger(
DMNContextEvaluator.class );
@@ -98,14 +99,7 @@ public class DMNContextEvaluator
}
EvaluatorResult er = ed.getEvaluator().evaluate(
eventManager, result );
if ( er.getResultType() == ResultType.SUCCESS ) {
- Object value = er.getResult();
- if( ! ed.getType().isCollection() && value instanceof
Collection &&
- ((Collection)value).size()==1 ) {
- // spec defines that "a=[a]", i.e., singleton
collections should be treated as the single element
- // and vice-versa
- value = ((Collection)value).toArray()[0];
- }
-
+ Object value = coerceValue(ed.getType(),
er.getResult());
if (((DMNRuntimeImpl)
eventManager.getRuntime()).performRuntimeTypeCheck(result.getModel())) {
if (!(ed.getContextEntry().getExpression()
instanceof FunctionDefinition)) {
// checking directly the result type...
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionDefinitionEvaluator.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionDefinitionEvaluator.java
index ebb45e7e28..c7d6469aac 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionDefinitionEvaluator.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNFunctionDefinitionEvaluator.java
@@ -48,6 +48,8 @@ import org.kie.dmn.model.api.FunctionDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.kie.dmn.core.util.CoerceUtil.coerceValue;
+
public class DMNFunctionDefinitionEvaluator
implements DMNExpressionEvaluator {
private static final Logger logger = LoggerFactory.getLogger(
DMNFunctionDefinitionEvaluator.class );
@@ -157,8 +159,9 @@ public class DMNFunctionDefinitionEvaluator
closureContext.getAll().forEach(dmnContext::set);
for( int i = 0; i < params.length; i++ ) {
final String paramName = parameters.get(i).name;
- if ((!performRuntimeTypeCheck) ||
parameters.get(i).type.isAssignableValue(params[i])) {
- ctx.setValue(paramName, params[i]);
+ Object coercedObject =
coerceValue(parameters.get(i).type, params[i]);
+ if ((!performRuntimeTypeCheck) ||
parameters.get(i).type.isAssignableValue(coercedObject)) {
+ ctx.setValue(paramName, coercedObject);
} else {
ctx.setValue(paramName, null);
MsgUtil.reportMessage(logger,
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/CompositeTypeImpl.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/CompositeTypeImpl.java
index 491e7676dc..3b15131b80 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/CompositeTypeImpl.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/CompositeTypeImpl.java
@@ -92,7 +92,7 @@ public class CompositeTypeImpl
public CompositeTypeImpl clone() {
return new CompositeTypeImpl( getNamespace(), getName(), getId(),
isCollection(), new LinkedHashMap<>( fields), getBaseType(), getFeelType() );
}
-
+
@Override
protected boolean internalIsInstanceOf(Object o) {
if (getBaseType() != null) {
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
index a0dc822fc7..a8f205084f 100644
---
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
+++
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeImpl.java
@@ -65,6 +65,7 @@ import org.slf4j.LoggerFactory;
import static
org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.EVALUATING;
import static
org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.FAILED;
import static
org.kie.dmn.api.core.DMNDecisionResult.DecisionEvaluationStatus.SKIPPED;
+import static org.kie.dmn.core.util.CoerceUtil.coerceValue;
public class DMNRuntimeImpl
implements DMNRuntime {
@@ -661,16 +662,9 @@ public class DMNRuntimeImpl
return false;
}
try {
- EvaluatorResult er = decision.getEvaluator().evaluate( this,
result );
+ EvaluatorResult er = decision.getEvaluator().evaluate( this,
result);
if( er.getResultType() == EvaluatorResult.ResultType.SUCCESS )
{
- Object value = er.getResult();
- if( ! decision.getResultType().isCollection() && value
instanceof Collection &&
- ((Collection)value).size()==1 ) {
- // spec defines that "a=[a]", i.e., singleton
collections should be treated as the single element
- // and vice-versa
- value = ((Collection)value).toArray()[0];
- }
-
+ Object value = coerceValue(decision.getResultType(),
er.getResult());
try {
if (typeCheck &&
!d.getResultType().isAssignableValue(value)) {
DMNMessage message = MsgUtil.reportMessage( logger,
diff --git
a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/util/CoerceUtil.java
b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/util/CoerceUtil.java
new file mode 100644
index 0000000000..0d59e4563f
--- /dev/null
+++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/util/CoerceUtil.java
@@ -0,0 +1,58 @@
+/**
+ * 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.core.util;
+
+import java.time.LocalDate;
+import java.util.Collection;
+
+import org.kie.dmn.api.core.DMNType;
+import org.kie.dmn.core.impl.SimpleTypeImpl;
+import org.kie.dmn.feel.lang.types.BuiltInType;
+import org.kie.dmn.feel.util.EvalHelper;
+
+/**
+ * Class used to centralize all coercion-related behavior
+ */
+public class CoerceUtil {
+
+ private CoerceUtil() {
+ // singleton class
+ }
+
+ public static Object coerceValue(DMNType requiredType, Object
valueToCoerce) {
+ return (requiredType != null && valueToCoerce != null) ?
actualCoerceValue(requiredType, valueToCoerce) :
+ valueToCoerce;
+ }
+
+ static Object actualCoerceValue(DMNType requiredType, Object
valueToCoerce) {
+ Object toReturn = valueToCoerce;
+ if (!requiredType.isCollection() && valueToCoerce instanceof
Collection &&
+ ((Collection) valueToCoerce).size() == 1) {
+ // spec defines that "a=[a]", i.e., singleton collections should
be treated as the single element
+ // and vice-versa
+ return ((Collection) valueToCoerce).toArray()[0];
+ }
+ if (valueToCoerce instanceof LocalDate localDate &&
+ requiredType instanceof SimpleTypeImpl simpleType &&
+ simpleType.getFeelType() == BuiltInType.DATE_TIME) {
+ return EvalHelper.coerceDateTime(localDate);
+ }
+ return toReturn;
+ }
+}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java
index d8ea262221..a5198f90a2 100644
--- a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java
+++ b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNRuntimeTest.java
@@ -25,6 +25,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetTime;
import java.time.Period;
+import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.chrono.ChronoPeriod;
@@ -634,6 +635,7 @@ public class DMNRuntimeTest extends
BaseInterpretedVsCompiledTest {
assertThat(ctx.get("Date-Time")).isEqualTo( ZonedDateTime.of( 2016,
12, 24, 23, 59, 0, 0, ZoneOffset.ofHours( -5 )));
assertThat(ctx.get("Date")).isEqualTo( new HashMap<String, Object>( )
{{
put( "fromString", LocalDate.of( 2015, 12, 24 ) );
+ put( "fromStringToDateTime", ZonedDateTime.of( 2015, 12, 24, 0, 0,
0, 0, ZoneOffset.UTC) );
put( "fromDateTime", LocalDate.of( 2016, 12, 24 ) );
put( "fromYearMonthDay", LocalDate.of( 1999, 11, 22 ) );
}});
@@ -658,6 +660,24 @@ public class DMNRuntimeTest extends
BaseInterpretedVsCompiledTest {
}
+ @Test
+ public void testDateToDateTimeFunction() {
+ final DMNRuntime runtime =
DMNRuntimeUtil.createRuntime("DateToDateTimeFunction.dmn", this.getClass());
+ final DMNModel dmnModel =
runtime.getModel("https://kiegroup.org/dmn/_A7F17D7B-F0AB-4C0B-B521-02EA26C2FBEE",
+ "new-file");
+ assertThat(dmnModel).isNotNull();
+
assertThat(dmnModel.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnModel.getMessages())).isFalse();
+
+ final DMNContext ctx = runtime.newContext();
+
+ final DMNResult dmnResult = runtime.evaluateAll(dmnModel, ctx);
+ LOG.debug("{}", dmnResult);
+
assertThat(dmnResult.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnResult.getMessages())).isFalse();
+
+ ZonedDateTime expected = ZonedDateTime.of(LocalDate.of(2021, 05, 31),
LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC);
+
assertThat(dmnResult.getDecisionResultByName("usingNormal").getResult()).isEqualTo(expected);
+ }
+
@Test
public void testFiltering() {
final DMNRuntime runtime =
DMNRuntimeUtil.createRuntime("Person_filtering_by_age.dmn", getClass() );
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNStronglyTypedSupportTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNStronglyTypedSupportTest.java
index 24dc9de7b4..bb9ab591bd 100644
---
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNStronglyTypedSupportTest.java
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNStronglyTypedSupportTest.java
@@ -165,6 +165,7 @@ public class DMNStronglyTypedSupportTest extends
BaseVariantTest {
assertThat(ctx.get("Date-Time")).isEqualTo(ZonedDateTime.of(2016,
12, 24, 23, 59, 0, 0, ZoneOffset.ofHours(-5)));
assertThat(ctx.get("Date")).isEqualTo(new HashMap<String,
Object>() {{
put("fromString", LocalDate.of(2015, 12, 24));
+ put( "fromStringToDateTime", ZonedDateTime.of( 2015, 12, 24,
0, 0, 0, 0, ZoneOffset.UTC) );
put("fromDateTime", LocalDate.of(2016, 12, 24));
put("fromYearMonthDay", LocalDate.of(1999, 11, 22));
}});
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/ast/DMNContextEvaluatorTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/ast/DMNContextEvaluatorTest.java
new file mode 100644
index 0000000000..a7183d95c0
--- /dev/null
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/ast/DMNContextEvaluatorTest.java
@@ -0,0 +1,76 @@
+package org.kie.dmn.core.ast;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.util.stream.Collectors;
+
+import org.drools.util.FileUtils;
+import org.junit.Test;
+import org.kie.dmn.api.core.DMNContext;
+import org.kie.dmn.api.core.DMNModel;
+import org.kie.dmn.api.core.DMNRuntime;
+import org.kie.dmn.api.core.ast.DecisionNode;
+import org.kie.dmn.core.api.DMNExpressionEvaluator;
+import org.kie.dmn.core.api.DMNFactory;
+import org.kie.dmn.core.api.EvaluatorResult;
+import org.kie.dmn.core.impl.DMNDecisionResultImpl;
+import org.kie.dmn.core.impl.DMNResultImpl;
+import org.kie.dmn.core.impl.DMNResultImplFactory;
+import org.kie.dmn.core.util.DMNRuntimeUtil;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class DMNContextEvaluatorTest {
+
+ private DMNResultImplFactory dmnResultFactory = new DMNResultImplFactory();
+
+ @Test
+ public void dateToDateTime() {
+ File file = FileUtils.getFile("0007-date-time.dmn");
+ final DMNRuntime runtime = DMNRuntimeUtil.createRuntime(file);
+ final DMNModel dmnModel =
runtime.getModel("http://www.trisotech.com/definitions/_69430b3e-17b8-430d-b760-c505bf6469f9",
"dateTime Table 58");
+ assertThat(dmnModel).isNotNull();
+
assertThat(dmnModel.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnModel.getMessages())).isFalse();
+
+ DecisionNode date = dmnModel.getDecisionByName("Date");
+ DMNExpressionEvaluator dateDecisionEvaluator = ((DecisionNodeImpl)
date).getEvaluator();
+ DMNContextEvaluator.ContextEntryDef ed = ((DMNContextEvaluator)
dateDecisionEvaluator).getEntries().stream()
+ .filter(entry ->
entry.getName().equals("fromStringToDateTime"))
+ .findFirst()
+ .orElseThrow(() -> new RuntimeException("Failed to find
fromStringToDateTime ContextEntryDef"));
+ final DMNContext context = DMNFactory.newContext();
+ context.set( "dateString", "2015-12-24" );
+ context.set( "timeString", "00:00:01-01:00" );
+ context.set( "dateTimeString", "2016-12-24T23:59:00-05:00" );
+ context.set( "Hours", 12 );
+ context.set( "Minutes", 59 );
+ context.set( "Seconds", new BigDecimal("1.3" ) );
+ context.set( "Timezone", "PT-1H" );
+ context.set( "Year", 1999 );
+ context.set( "Month", 11 );
+ context.set( "Day", 22 );
+ context.set( "durationString", "P13DT2H14S" ); // <variable
name="durationString" typeRef="feel:string"/>
+ DMNResultImpl result = createResult(dmnModel, context );
+ DMNExpressionEvaluator evaluator = ed.getEvaluator();
+ EvaluatorResult evaluated = evaluator.evaluate(runtime, result);
+ assertNotNull(evaluated);
+ assertEquals(EvaluatorResult.ResultType.SUCCESS,
evaluated.getResultType());
+ }
+
+ private DMNResultImpl createResult(DMNModel model, DMNContext context) {
+ DMNResultImpl result = createResultImpl(model, context);
+
+ for (DecisionNode decision : model.getDecisions().stream().filter(d ->
d.getModelNamespace().equals(model.getNamespace())).collect(Collectors.toSet()))
{
+ result.addDecisionResult(new
DMNDecisionResultImpl(decision.getId(), decision.getName()));
+ }
+ return result;
+ }
+
+ private DMNResultImpl createResultImpl(DMNModel model, DMNContext context)
{
+ DMNResultImpl result = dmnResultFactory.newDMNResultImpl(model);
+ result.setContext(context.clone());
+ return result;
+ }
+}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/CoerceUtilTest.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/CoerceUtilTest.java
new file mode 100644
index 0000000000..73c60e35dc
--- /dev/null
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/CoerceUtilTest.java
@@ -0,0 +1,176 @@
+package org.kie.dmn.core.util;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Collections;
+
+import org.junit.Test;
+import org.kie.dmn.api.core.DMNType;
+import org.kie.dmn.core.impl.SimpleTypeImpl;
+import org.kie.dmn.feel.lang.types.BuiltInType;
+
+import static org.junit.Assert.*;
+
+public class CoerceUtilTest {
+
+ @Test
+ public void coerceValueCollectionToArrayConverted() {
+ Object item = "TESTED_OBJECT";
+ Object value = Collections.singleton(item);
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "string",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.STRING);
+ Object retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(item, retrieved);
+ }
+
+ @Test
+ public void coerceValueCollectionToArrayNotConverted() {
+ Object item = "TESTED_OBJECT";
+ Object value = Collections.singleton(item);
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "string",
+ null,
+ true,
+ null,
+ null,
+ BuiltInType.STRING);
+ Object retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+
+ value = "TESTED_OBJECT";
+ requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "string",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.STRING);
+ retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+
+ requiredType = null;
+ retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertEquals(value, retrieved);
+
+ value = null;
+ requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "string",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.STRING);
+ retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertEquals(value, retrieved);
+
+ }
+
+ @Test
+ public void coerceValueDateToDateTimeConverted() {
+ Object value = LocalDate.now();
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "date and time",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.DATE_TIME);
+ Object retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertTrue(retrieved instanceof ZonedDateTime);
+ ZonedDateTime zdtRetrieved = (ZonedDateTime)retrieved;
+ assertEquals(value, zdtRetrieved.toLocalDate());
+ assertEquals(ZoneOffset.UTC, zdtRetrieved.getOffset());
+ assertEquals(0, zdtRetrieved.getHour());
+ assertEquals(0, zdtRetrieved.getMinute());
+ assertEquals(0, zdtRetrieved.getSecond());
+ }
+
+ @Test
+ public void coerceValueDateToDateTimeNotConverted() {
+ Object value = "TEST_OBJECT";
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "date and time",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.DATE_TIME);
+ Object retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+ value = LocalDate.now();
+ requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "date",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.DATE);
+ retrieved = CoerceUtil.coerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+ }
+
+ @Test
+ public void actualCoerceValueCollectionToArray() {
+ Object item = "TESTED_OBJECT";
+ Object value = Collections.singleton(item);
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "string",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.STRING);
+ Object retrieved = CoerceUtil.actualCoerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(item, retrieved);
+ }
+
+ @Test
+ public void actualCoerceValueDateToDateTime() {
+ Object value = LocalDate.now();
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "date and time",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.DATE_TIME);
+ Object retrieved = CoerceUtil.actualCoerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertTrue(retrieved instanceof ZonedDateTime);
+ ZonedDateTime zdtRetrieved = (ZonedDateTime)retrieved;
+ assertEquals(value, zdtRetrieved.toLocalDate());
+ assertEquals(ZoneOffset.UTC, zdtRetrieved.getOffset());
+ assertEquals(0, zdtRetrieved.getHour());
+ assertEquals(0, zdtRetrieved.getMinute());
+ assertEquals(0, zdtRetrieved.getSecond());
+ }
+
+ @Test
+ public void actualCoerceValueNotConverted() {
+ Object value = BigDecimal.valueOf(1L);
+ DMNType requiredType = new
SimpleTypeImpl("http://www.omg.org/spec/DMN/20180521/FEEL/",
+ "number",
+ null,
+ false,
+ null,
+ null,
+ BuiltInType.NUMBER);
+ Object retrieved = CoerceUtil.actualCoerceValue(requiredType, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+ }
+}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/DMNRuntimeUtil.java
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/DMNRuntimeUtil.java
index 93624b4e07..102b8ba3de 100644
---
a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/DMNRuntimeUtil.java
+++
b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/util/DMNRuntimeUtil.java
@@ -18,6 +18,7 @@
*/
package org.kie.dmn.core.util;
+import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -72,10 +73,15 @@ public final class DMNRuntimeUtil {
final KieContainer kieContainer = KieHelper.getKieContainer(
ks.newReleaseId("org.kie", "dmn-test-"+UUID.randomUUID(),
"1.0"),
ks.getResources().newClassPathResource(resourceName,
testClass));
+ return createRuntime(kieContainer);
+ }
- final DMNRuntime runtime = typeSafeGetKieRuntime(kieContainer);
- assertThat(runtime).isNotNull();
- return runtime;
+ public static DMNRuntime createRuntime(final File resourceFile) {
+ final KieServices ks = KieServices.Factory.get();
+ final KieContainer kieContainer = KieHelper.getKieContainer(
+ ks.newReleaseId("org.kie", "dmn-test-"+UUID.randomUUID(),
"1.0"),
+ ks.getResources().newFileSystemResource(resourceFile));
+ return createRuntime(kieContainer);
}
public static List<DMNMessage> createExpectingDMNMessages(final String
resourceName, final Class testClass) {
@@ -192,4 +198,10 @@ public final class DMNRuntimeUtil {
byte[] jar = kieModule.getBytes();
return jar;
}
+
+ static DMNRuntime createRuntime(KieContainer kieContainer) {
+ final DMNRuntime runtime = typeSafeGetKieRuntime(kieContainer);
+ assertThat(runtime).isNotNull();
+ return runtime;
+ }
}
diff --git
a/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/0007-date-time.dmn
b/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/0007-date-time.dmn
index 103fabd942..8f765d0114 100644
---
a/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/0007-date-time.dmn
+++
b/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/0007-date-time.dmn
@@ -72,6 +72,12 @@
<semantic:text>date(dateString)</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
+ <semantic:contextEntry>
+ <semantic:variable name="fromStringToDateTime" typeRef="date and
time"/>
+ <semantic:literalExpression>
+ <semantic:text>date(dateString)</semantic:text>
+ </semantic:literalExpression>
+ </semantic:contextEntry>
<semantic:contextEntry>
<semantic:variable name="fromDateTime" typeRef="date"/>
<semantic:literalExpression>
diff --git
a/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/DateToDateTimeFunction.dmn
b/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/DateToDateTimeFunction.dmn
new file mode 100644
index 0000000000..b947dfd7f7
--- /dev/null
+++
b/kie-dmn/kie-dmn-core/src/test/resources/org/kie/dmn/core/DateToDateTimeFunction.dmn
@@ -0,0 +1,24 @@
+<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/"
xmlns="https://kiegroup.org/dmn/_A7F17D7B-F0AB-4C0B-B521-02EA26C2FBEE"
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"
xmlns:kie="http://www.drools.org/kie/dmn/1.2"
xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/"
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"
xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/"
id="_14BDA5CA-C87F-448D-AF75-F976A9E0EF83" name="new-file"
typeLanguage="http://www. [...]
+ <dmn:extensionElements/>
+ <dmn:businessKnowledgeModel id="_80847A66-F03E-4B81-B406-0E3B8D177EC8"
name="normal">
+ <dmn:extensionElements/>
+ <dmn:variable id="_1D5A641F-31EE-42FF-8DAD-D229A9A998BD" name="normal"/>
+ <dmn:encapsulatedLogic id="_4A6C9D35-03DE-47F8-8ADD-6F15A27077A5"
kind="FEEL">
+ <dmn:formalParameter id="_3984C4F0-4E60-481A-9702-EAC914F1C63B" name="a"
typeRef="date and time"/>
+ <dmn:formalParameter id="_C419BFC7-53E0-4B2C-A2B1-BA5A22D2971D" name="b"
typeRef="duration"/>
+ <dmn:literalExpression id="_88CC7676-B609-4430-8670-5B5735D5C6D5">
+ <dmn:text>a+b</dmn:text>
+ </dmn:literalExpression>
+ </dmn:encapsulatedLogic>
+ </dmn:businessKnowledgeModel>
+ <dmn:decision id="_3151179A-E33D-4A7E-9D9C-8CFE388E8640" name="usingNormal">
+ <dmn:extensionElements/>
+ <dmn:variable id="_C1455FFB-C4C4-4ABA-A9DF-2E7A92F2588B"
name="usingNormal" typeRef="date and time"/>
+ <dmn:knowledgeRequirement id="_A1C84873-809C-44CD-90BE-AD32BFB8F79F">
+ <dmn:requiredKnowledge href="#_80847A66-F03E-4B81-B406-0E3B8D177EC8"/>
+ </dmn:knowledgeRequirement>
+ <dmn:literalExpression id="_B29EE21C-702B-40BB-A98A-1717BBC83C8C">
+ <dmn:text>normal( @"2019-03-31", duration( "P26M" ))</dmn:text>
+ </dmn:literalExpression>
+ </dmn:decision>
+</dmn:definitions>
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/BaseFEELFunction.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/BaseFEELFunction.java
index 64c76d89bb..ae1fbf5e5f 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/BaseFEELFunction.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/BaseFEELFunction.java
@@ -24,9 +24,9 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -46,6 +46,8 @@ import org.kie.dmn.feel.util.EvalHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.kie.dmn.feel.util.CoerceUtil.coerceParams;
+
public abstract class BaseFEELFunction
implements FEELFunction {
@@ -153,10 +155,7 @@ public abstract class BaseFEELFunction
}
} catch ( Exception e ) {
logger.error( "Error trying to call function " + getName() + ".",
e );
- ctx.notifyEvt( () -> {
- return new FEELEventBase(
Severity.ERROR, "Error trying to call function " + getName() + ".", e );
- }
- );
+ ctx.notifyEvt( () -> new FEELEventBase( Severity.ERROR, "Error
trying to call function " + getName() + ".", e ));
}
return null;
}
@@ -238,22 +237,12 @@ public abstract class BaseFEELFunction
boolean found = true;
for ( int i = 0; i < parameterTypes.length; i++ ) {
Class<?> currentIdxActualParameterType =
cm.getActualClasses()[i];
- if ( currentIdxActualParameterType != null &&
!parameterTypes[i].isAssignableFrom( currentIdxActualParameterType ) ) {
- // singleton list spec defines that "a=[a]", i.e.,
singleton collections should be treated as the single element
- // and vice-versa
- if ( Collection.class.isAssignableFrom(
currentIdxActualParameterType ) ) {
- Collection<?> valueCollection = (Collection<?>)
actualParams[i];
- if ( valueCollection.size() == 1 ) {
- Object singletonValue =
valueCollection.iterator().next();
- // re-perform the assignable-from check, this time
using the element itself the singleton value from the original parameter list
- if ( singletonValue != null &&
parameterTypes[i].isAssignableFrom( singletonValue.getClass() ) ) {
- Object[] newParams = new
Object[cm.getActualParams().length];
- System.arraycopy( cm.getActualParams(), 0,
newParams, 0, cm.getActualParams().length ); // can't rely on
adjustForVariableParameters() have actually copied
- newParams[i] = singletonValue;
- cm.setActualParams(newParams);
- continue;
- }
- }
+ Class<?> expectedParameterType = parameterTypes[i];
+ if ( currentIdxActualParameterType != null &&
!expectedParameterType.isAssignableFrom( currentIdxActualParameterType ) ) {
+ Optional<Object[]> coercedParams =
coerceParams(currentIdxActualParameterType, expectedParameterType,
actualParams, i);
+ if (coercedParams.isPresent()) {
+ cm.setActualParams(coercedParams.get());
+ continue;
}
found = false;
break;
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DateFunction.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DateFunction.java
index 37cd0b3bf2..05f28b8bfd 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DateFunction.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DateFunction.java
@@ -62,7 +62,7 @@ public class DateFunction
if (!BEGIN_YEAR.matcher(val).find()) { // please notice the regex
strictly requires the beginning, so we can use find.
return FEELFnResult.ofError(new
InvalidParametersEvent(Severity.ERROR, "from", "year not compliant with XML
Schema Part 2 Datatypes"));
}
-
+
try {
return FEELFnResult.ofResult(LocalDate.from(FEEL_DATE.parse(val)));
} catch (DateTimeException e) {
@@ -80,7 +80,7 @@ public class DateFunction
if ( day == null ) {
return FEELFnResult.ofError(new
InvalidParametersEvent(Severity.ERROR, "day", "cannot be null"));
}
-
+
try {
return FEELFnResult.ofResult( LocalDate.of( year.intValue(),
month.intValue(), day.intValue() ) );
} catch (DateTimeException e) {
@@ -92,7 +92,7 @@ public class DateFunction
if ( date == null ) {
return FEELFnResult.ofError(new
InvalidParametersEvent(Severity.ERROR, "from", "cannot be null"));
}
-
+
try {
return FEELFnResult.ofResult( LocalDate.from( date ) );
} catch (DateTimeException e) {
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/CoerceUtil.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/CoerceUtil.java
new file mode 100644
index 0000000000..4672de1b39
--- /dev/null
+++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/CoerceUtil.java
@@ -0,0 +1,90 @@
+/**
+ * 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 java.time.LocalDate;
+import java.time.ZonedDateTime;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.kie.dmn.feel.lang.Type;
+import org.kie.dmn.feel.lang.types.BuiltInType;
+
+/**
+ * Class used to centralize all coercion-related behavior
+ */
+public class CoerceUtil {
+
+ private CoerceUtil() {
+ // singleton class
+ }
+
+ public static Object coerceParameter(Type requiredType, Object
valueToCoerce) {
+ return (requiredType != null && valueToCoerce != null) ?
actualCoerceParameter(requiredType, valueToCoerce) :
+ valueToCoerce;
+ }
+
+ public static Optional<Object[]> coerceParams(Class<?>
currentIdxActualParameterType, Class<?> expectedParameterType, Object[]
actualParams, int i) {
+ Object actualObject = actualParams[i];
+ Optional<Object> coercedObject =
coerceParam(currentIdxActualParameterType, expectedParameterType,
+ actualObject);
+ return coercedObject.map(o -> actualCoerceParams(actualParams, o, i));
+ }
+
+ static Optional<Object> coerceParam(Class<?>
currentIdxActualParameterType, Class<?> expectedParameterType,
+ Object actualObject) {
+ if (Collection.class.isAssignableFrom(currentIdxActualParameterType)) {
+ Collection<?> valueCollection = (Collection<?>) actualObject;
+ if (valueCollection.size() == 1) {
+ Object singletonValue = valueCollection.iterator().next();
+ // re-perform the assignable-from check, this time using the
element itself the singleton value from
+ // the original parameter list
+ if (singletonValue != null) {
+ return
expectedParameterType.isAssignableFrom(singletonValue.getClass()) ?
+ Optional.of(singletonValue) :
+ coerceParam(singletonValue.getClass(),
expectedParameterType, singletonValue);
+ }
+ }
+ }
+ if (actualObject instanceof LocalDate localDate &&
+ ZonedDateTime.class.isAssignableFrom(expectedParameterType)) {
+ Object coercedObject = EvalHelper.coerceDateTime(localDate);
+ return Optional.of(coercedObject);
+ }
+ return Optional.empty();
+ }
+
+ static Object actualCoerceParameter(Type requiredType, Object
valueToCoerce) {
+ Object toReturn = valueToCoerce;
+ if (valueToCoerce instanceof LocalDate localDate &&
+ requiredType == BuiltInType.DATE_TIME) {
+ return EvalHelper.coerceDateTime(localDate);
+ }
+ return toReturn;
+ }
+
+ static Object[] actualCoerceParams(Object[] actualParams, Object
coercedObject, int i) {
+ Object[] toReturn = new Object[actualParams.length];
+ System.arraycopy( actualParams, 0, toReturn, 0, actualParams.length );
+ toReturn[i] = coercedObject;
+ return toReturn;
+ }
+
+
+}
\ No newline at end of file
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/EvalHelper.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/EvalHelper.java
index 3e4a3e00da..4fa56ed075 100644
--- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/EvalHelper.java
+++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/EvalHelper.java
@@ -24,7 +24,9 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.time.Duration;
+import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
@@ -191,6 +193,10 @@ public class EvalHelper {
}
}
+ public static ZonedDateTime coerceDateTime(final LocalDate value) {
+ return ZonedDateTime.of(value, LocalTime.of(0, 0, 0, 0),
ZoneOffset.UTC);
+ }
+
public static Boolean getBooleanOrNull(Object value) {
if (!(value instanceof Boolean)) {
return null;
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 5478e9a575..a84998a33c 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
@@ -187,7 +187,7 @@ public class FEELFunctionsTest extends BaseFEELTest {
{ "all( true, true )", true, null},
{ "all( [true, false] )", false, null},
{ "all( [true, true] )", true, null},
- { "all( [false,null,true] )", false, null},
+ { "all( [false,null,true] )", false, null},
{ "all( [] )", true, null},
{ "all( 0 )", null, FEELEvent.Severity.ERROR},
{ "all( )", null, FEELEvent.Severity.ERROR},
@@ -199,32 +199,32 @@ public class FEELFunctionsTest extends BaseFEELTest {
{ "any( true, true )", true, null},
{ "any( [true, false] )", true, null},
{ "any( [true, true] )", true, null},
- { "any( [false,null,true] )", true, null},
+ { "any( [false,null,true] )", true, null},
{ "any( [] )", false, null},
{ "any( 0 )", null, FEELEvent.Severity.ERROR},
{ "any( )", null, FEELEvent.Severity.ERROR},
-
+
{ "day of year( date(2019, 9, 17) )", BigDecimal.valueOf( 260
), null},
{ "day of week( date(2019, 9, 17) )", "Tuesday", null},
{ "month of year( date(2019, 9, 17) )", "September", null},
{ "week of year( date(2019, 9, 17) )", BigDecimal.valueOf( 38
), null},
{ "week of year( date(2003, 12, 29) )", BigDecimal.valueOf( 1
), null}, // ISO defs.
- { "week of year( date(2004, 1, 4) )", BigDecimal.valueOf( 1 ),
null},
- { "week of year( date(2005, 1, 3) )", BigDecimal.valueOf( 1 ),
null},
- { "week of year( date(2005, 1, 9) )", BigDecimal.valueOf( 1 ),
null},
- { "week of year( date(2005, 1, 1) )", BigDecimal.valueOf( 53
), null},
+ { "week of year( date(2004, 1, 4) )", BigDecimal.valueOf( 1 ),
null},
+ { "week of year( date(2005, 1, 3) )", BigDecimal.valueOf( 1 ),
null},
+ { "week of year( date(2005, 1, 9) )", BigDecimal.valueOf( 1 ),
null},
+ { "week of year( date(2005, 1, 1) )", BigDecimal.valueOf( 53
), null},
{ "median( 8, 2, 5, 3, 4 )", new BigDecimal("4") , null},
{ "median( [6, 1, 2, 3] )", new BigDecimal("2.5") , null},
{ "median( [ ] ) ", null, null}, // DMN spec, Table 69:
Semantics of list functions
-
+
{ "0-max( 1, 2, 3 )", BigDecimal.valueOf( -3 ) , null},
{ "-max( 1, 2, 3 )", BigDecimal.valueOf( -3 ) , null}, //
DROOLS-5981
{ "0-sum( 1, 2, 3 )", BigDecimal.valueOf( -6 ) , null},
{ "-sum( 1, 2, 3 )", BigDecimal.valueOf( -6 ) , null},
{ "0-abs( 10 )", new BigDecimal("-10") , null},
- { "-abs( 10 )", new BigDecimal("-10") , null},
+ { "-abs( 10 )", new BigDecimal("-10") , null},
{ "0-max( 1, abs(-2), 3 )", BigDecimal.valueOf( -3 ) , null},
- { "-max( 1, abs(-2), 3 )", BigDecimal.valueOf( -3 ) , null},
+ { "-max( 1, abs(-2), 3 )", BigDecimal.valueOf( -3 ) , null},
{ "0-max( 1, -abs(-2), 3 )", BigDecimal.valueOf( -3 ) , null},
{ "-max( 1, -abs(-2), 3 )", BigDecimal.valueOf( -3 ) , null},
{ "{a: 2, r: 0-sum( 1, a, 3 )}.r", BigDecimal.valueOf( -6 ) ,
null},
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/CoerceUtilTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/CoerceUtilTest.java
new file mode 100644
index 0000000000..28413e6d1a
--- /dev/null
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/CoerceUtilTest.java
@@ -0,0 +1,148 @@
+package org.kie.dmn.feel.util;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Set;
+
+import org.junit.Test;
+import org.kie.dmn.feel.lang.types.BuiltInType;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class CoerceUtilTest {
+
+ @Test
+ public void coerceParameterDateToDateTimeConverted() {
+ Object value = LocalDate.now();
+ Object retrieved = CoerceUtil.coerceParameter(BuiltInType.DATE_TIME,
value);
+ assertNotNull(retrieved);
+ assertTrue(retrieved instanceof ZonedDateTime);
+ ZonedDateTime zdtRetrieved = (ZonedDateTime) retrieved;
+ assertEquals(value, zdtRetrieved.toLocalDate());
+ assertEquals(ZoneOffset.UTC, zdtRetrieved.getOffset());
+ assertEquals(0, zdtRetrieved.getHour());
+ assertEquals(0, zdtRetrieved.getMinute());
+ assertEquals(0, zdtRetrieved.getSecond());
+ }
+
+ @Test
+ public void coerceParameterDateToDateTimeNotConverted() {
+ Object value = "TEST_OBJECT";
+ Object retrieved = CoerceUtil.coerceParameter(null, value);
+ assertEquals(value, retrieved);
+
+ value = null;
+ retrieved = CoerceUtil.coerceParameter(BuiltInType.DATE, value);
+ assertEquals(value, retrieved);
+ }
+
+ @Test
+ public void coerceParamsCollectionToArrayConverted() {
+ Object item = "TESTED_OBJECT";
+ Object value = Collections.singleton(item);
+ Object[] actualParams1 = {value, "NOT_DATE"};
+ Optional<Object[]> retrieved = CoerceUtil.coerceParams(Set.class,
String.class, actualParams1, 0);
+ assertNotNull(retrieved);
+ assertTrue(retrieved.isPresent());
+ Object[] retrievedObjects = retrieved.get();
+ assertEquals(item, retrievedObjects[0]);
+ assertEquals(actualParams1[1], retrievedObjects[1]);
+
+
+ item = LocalDate.now();
+ value = Collections.singleton(item);
+ Object[] actualParams2 = {value, "NOT_DATE"};
+ retrieved = CoerceUtil.coerceParams(Set.class, ZonedDateTime.class,
actualParams2, 0);
+ assertNotNull(retrieved);
+ assertTrue(retrieved.isPresent());
+ retrievedObjects = retrieved.get();
+ assertTrue(retrievedObjects[0] instanceof ZonedDateTime);
+ ZonedDateTime zdtRetrieved = (ZonedDateTime) retrievedObjects[0];
+ assertEquals(item, zdtRetrieved.toLocalDate());
+ assertEquals(ZoneOffset.UTC, zdtRetrieved.getOffset());
+ assertEquals(0, zdtRetrieved.getHour());
+ assertEquals(0, zdtRetrieved.getMinute());
+ assertEquals(0, zdtRetrieved.getSecond());
+ assertEquals(actualParams2[1], retrievedObjects[1]);
+ }
+
+ @Test
+ public void coerceParamsToDateTimeConverted() {
+ Object value = LocalDate.now();
+ Object[] actualParams = {value, "NOT_DATE"};
+ Optional<Object[]> retrieved =
CoerceUtil.coerceParams(LocalDate.class, ZonedDateTime.class, actualParams, 0);
+ assertNotNull(retrieved);
+ assertTrue(retrieved.isPresent());
+ Object[] retrievedObjects = retrieved.get();
+ assertTrue(retrievedObjects[0] instanceof ZonedDateTime);
+ ZonedDateTime zdtRetrieved = (ZonedDateTime) retrievedObjects[0];
+ assertEquals(value, zdtRetrieved.toLocalDate());
+ assertEquals(ZoneOffset.UTC, zdtRetrieved.getOffset());
+ assertEquals(0, zdtRetrieved.getHour());
+ assertEquals(0, zdtRetrieved.getMinute());
+ assertEquals(0, zdtRetrieved.getSecond());
+ assertEquals(actualParams[1], retrievedObjects[1]);
+ }
+
+ @Test
+ public void coerceParamsNotConverted() {
+ Object item = "TESTED_OBJECT";
+ Object value = Collections.singleton(item);
+ Object[] actualParams1 = {value, "NOT_DATE"};
+ Optional<Object[]> retrieved = CoerceUtil.coerceParams(Set.class,
BigDecimal.class, actualParams1, 0);
+ assertNotNull(retrieved);
+ assertTrue(retrieved.isEmpty());
+
+ value = LocalDate.now();
+ Object[] actualParams2 = {value, "NOT_DATE"};
+ retrieved = CoerceUtil.coerceParams(LocalDate.class, String.class,
actualParams2, 0);
+ assertNotNull(retrieved);
+ assertTrue(retrieved.isEmpty());
+ }
+
+ @Test
+ public void actualCoerceParameterToDateTimeConverted() {
+ Object value = LocalDate.now();
+ Object retrieved =
CoerceUtil.actualCoerceParameter(BuiltInType.DATE_TIME, value);
+ assertNotNull(retrieved);
+ assertTrue(retrieved instanceof ZonedDateTime);
+ ZonedDateTime zdtRetrieved = (ZonedDateTime) retrieved;
+ assertEquals(value, zdtRetrieved.toLocalDate());
+ assertEquals(ZoneOffset.UTC, zdtRetrieved.getOffset());
+ assertEquals(0, zdtRetrieved.getHour());
+ assertEquals(0, zdtRetrieved.getMinute());
+ assertEquals(0, zdtRetrieved.getSecond());
+ }
+
+ @Test
+ public void actualCoerceParameterNotConverted() {
+ Object value = "TEST_OBJECT";
+ Object retrieved =
CoerceUtil.actualCoerceParameter(BuiltInType.DATE_TIME, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+
+ value = LocalDate.now();
+ retrieved = CoerceUtil.actualCoerceParameter(BuiltInType.DATE, value);
+ assertNotNull(retrieved);
+ assertEquals(value, retrieved);
+ }
+
+ @Test
+ public void actualCoerceParams() {
+ Object value = LocalDate.now();
+ Object[] actualParams = {value, "NOT_DATE"};
+ Object coercedValue = BigDecimal.valueOf(1L);
+ Object[] retrieved = CoerceUtil.actualCoerceParams(actualParams,
coercedValue, 0);
+ assertNotNull(retrieved);
+ assertEquals(actualParams.length, retrieved.length);
+ assertEquals(coercedValue, retrieved[0]);
+ assertEquals(actualParams[1], retrieved[1]);
+ }
+
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]