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 f2669bf41b [Incubator kie issues#2173] Decouple FEELDialect from
BooleanEvalHelper (#6537)
f2669bf41b is described below
commit f2669bf41b22e91457e34ec4581705eb0918ebeb
Author: ChinchuAjith <[email protected]>
AuthorDate: Fri Dec 5 16:31:31 2025 +0530
[Incubator kie issues#2173] Decouple FEELDialect from BooleanEvalHelper
(#6537)
* [incubator-kie-issues#2099] Fixed BFEEL execution
* [incubator-kie-issues#2099] Missing header
* [incubator-kie-issues#2099] Fixed date+number case
* AddExecutor : Design and Implementation
* AddExecutor : Design and Implementation - Review comments fix
* AndExecutor : Design and Implementation
* Gte, Gt and Eq executors implementation
* Lte, Lt, NotEqual, Or and Pow executors implementation
* Code cleanup - executor classes
* Code cleanup, adding comments
* Adding license headers and removing commented code
* Correcting minor comments
* Review comments fix
* Fixing AND operator short cut scenario
* Removing unused imports
* Code Formatting, review comments
* Removing Feel dialect dependency from the Util class
* Code clean up
* Fixing Review comments
* Resolving test failures
* Removing commented lines
* Removing commented lines
---------
Co-authored-by: Gabriele-Cardosi <[email protected]>
---
.../org/kie/dmn/feel/lang/ast/BetweenNode.java | 21 +-
.../dmn/feel/lang/ast/FunctionInvocationNode.java | 25 +-
.../java/org/kie/dmn/feel/lang/ast/InNode.java | 37 +--
.../org/kie/dmn/feel/lang/ast/UnaryTestNode.java | 165 ++++++------
.../ast/dialectHandlers/BFEELDialectHandler.java | 229 ++++++++++++++--
.../ast/dialectHandlers/DefaultDialectHandler.java | 295 ++++++++++++++++-----
.../ast/dialectHandlers/FEELDialectHandler.java | 139 +++++++++-
.../main/java/org/kie/dmn/feel/runtime/Range.java | 13 +-
.../runtime/functions/DecisionTableFunction.java | 138 +++++-----
.../kie/dmn/feel/runtime/functions/IsFunction.java | 9 +-
.../org/kie/dmn/feel/runtime/impl/RangeImpl.java | 86 +++---
.../org/kie/dmn/feel/util/BooleanEvalHelper.java | 161 +----------
.../org/kie/dmn/feel/util/DateTimeEvalHelper.java | 18 +-
.../kie/dmn/feel/runtime/impl/RangeImplTest.java | 63 ++---
.../kie/dmn/feel/util/BooleanEvalHelperTest.java | 59 ++---
.../dmn/validation/dtanalysis/model/Interval.java | 97 ++++---
16 files changed, 931 insertions(+), 624 deletions(-)
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/BetweenNode.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/BetweenNode.java
index 430ff574ac..0ac663c89c 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/BetweenNode.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/BetweenNode.java
@@ -24,9 +24,10 @@ import
org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.FEELDialect;
import org.kie.dmn.feel.lang.Type;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DialectHandler;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DialectHandlerFactory;
import org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils;
import org.kie.dmn.feel.lang.types.BuiltInType;
-import org.kie.dmn.feel.util.BooleanEvalHelper;
import org.kie.dmn.feel.util.Msg;
public class BetweenNode
@@ -89,7 +90,8 @@ public class BetweenNode
ctx.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.IS_NULL, "end")));
problem = true;
}
- if (problem) return null;
+ if (problem)
+ return null;
Object o_val = value.evaluate(ctx);
Object o_s = start.evaluate(ctx);
@@ -107,22 +109,19 @@ public class BetweenNode
ctx.notifyEvt(astEvent(severity, Msg.createMessage(Msg.IS_NULL,
"end")));
problem = true;
}
- if (problem && ctx.getFEELDialect() != FEELDialect.BFEEL) return null;
+ if (problem && ctx.getFEELDialect() != FEELDialect.BFEEL)
+ return null;
- Object gte = InfixExecutorUtils.or(BooleanEvalHelper.compare(o_val,
o_s, ctx.getFEELDialect(), (l, r) -> l.compareTo(r) > 0),
- BooleanEvalHelper.isEqual(o_val,
o_s, ctx.getFEELDialect()),
- ctx); // do not use Java || to
avoid potential NPE due to FEEL 3vl.
+ DialectHandler handler = DialectHandlerFactory.getHandler(ctx);
+ Object gte = handler.executeGte(o_val, o_s, ctx);
if (gte == null) {
ctx.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.X_TYPE_INCOMPATIBLE_WITH_Y_TYPE, "value", "start")));
}
- Object lte = InfixExecutorUtils.or(BooleanEvalHelper.compare(o_val,
o_e, ctx.getFEELDialect(), (l, r) -> l.compareTo(r) < 0),
- BooleanEvalHelper.isEqual(o_val, o_e, ctx.getFEELDialect()),
- ctx); // do not use Java || to avoid potential NPE due to FEEL
3vl.
+ Object lte = handler.executeLte(o_val, o_e, ctx);
if (lte == null) {
ctx.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.X_TYPE_INCOMPATIBLE_WITH_Y_TYPE, "value", "end")));
}
-
return InfixExecutorUtils.and(gte, lte, ctx); // do not use Java && to
avoid potential NPE due to FEEL 3vl.
}
@@ -133,7 +132,7 @@ public class BetweenNode
@Override
public ASTNode[] getChildrenNode() {
- return new ASTNode[]{value, start, end};
+ return new ASTNode[] { value, start, end };
}
@Override
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/FunctionInvocationNode.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/FunctionInvocationNode.java
index 411037473c..d910e4c5a2 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/FunctionInvocationNode.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/FunctionInvocationNode.java
@@ -37,13 +37,12 @@ import org.kie.dmn.feel.util.MsgUtil;
public class FunctionInvocationNode
extends BaseNode {
-
private BaseNode name;
private ListNode params;
private TemporalConstantNode tcFolded; // this is NOT a child node
intentionally.
public FunctionInvocationNode(ParserRuleContext ctx, BaseNode name,
ListNode params) {
- super( ctx );
+ super(ctx);
this.name = name;
this.params = params;
}
@@ -86,22 +85,22 @@ public class FunctionInvocationNode
}
FEELFunction function;
Object value;
- if ( name instanceof NameRefNode ) {
+ if (name instanceof NameRefNode) {
// simple name
- value = ctx.getValue( name.getText() );
+ value = ctx.getValue(name.getText());
} else if (name instanceof QualifiedNameNode) {
QualifiedNameNode qn = (QualifiedNameNode) name;
String[] qns = qn.getPartsAsStringArray();
- value = ctx.getValue( qns );
+ value = ctx.getValue(qns);
} else if (name instanceof PathExpressionNode) {
PathExpressionNode pathExpressionNode = (PathExpressionNode) name;
value = pathExpressionNode.evaluate(ctx);
} else {
value = name.evaluate(ctx);
}
- if ( value instanceof FEELFunction ) {
+ if (value instanceof FEELFunction) {
function = (FEELFunction) value;
- Object[] p = params.getElements().stream().map( e -> e.evaluate(
ctx ) ).toArray( Object[]::new );
+ Object[] p = params.getElements().stream().map(e ->
e.evaluate(ctx)).toArray(Object[]::new);
List<String> functionNameParts;
if (name instanceof NameRefNode) {
functionNameParts = Collections.singletonList(name.getText());
@@ -114,17 +113,17 @@ public class FunctionInvocationNode
}
Object result = invokeTheFunction(functionNameParts, function,
ctx, p);
return result;
- } else if( value instanceof UnaryTest ) {
- if( params.getElements().size() == 1 ) {
- Object p = params.getElements().get( 0 ).evaluate( ctx );
- return ((UnaryTest) value).apply( ctx, p );
+ } else if (value instanceof UnaryTest) {
+ if (params.getElements().size() == 1) {
+ Object p = params.getElements().get(0).evaluate(ctx);
+ return ((UnaryTest) value).apply(ctx, p);
} else {
- ctx.notifyEvt( astEvent(Severity.ERROR,
Msg.createMessage(Msg.CAN_T_INVOKE_AN_UNARY_TEST_WITH_S_PARAMETERS_UNARY_TESTS_REQUIRE_1_SINGLE_PARAMETER,
params.getElements().size()) ) );
+ ctx.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.CAN_T_INVOKE_AN_UNARY_TEST_WITH_S_PARAMETERS_UNARY_TESTS_REQUIRE_1_SINGLE_PARAMETER,
params.getElements().size())));
}
} else if (value instanceof Range) {
if (params.getElements().size() == 1) {
Object p = params.getElements().get(0).evaluate(ctx);
- return ((Range) value).includes(ctx.getFEELDialect(), p);
+ return ((Range) value).includes(ctx, p);
} else {
ctx.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.CAN_T_INVOKE_AN_UNARY_TEST_WITH_S_PARAMETERS_UNARY_TESTS_REQUIRE_1_SINGLE_PARAMETER,
params.getElements().size())));
}
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java
index 1486d3161d..a8e7bc2813 100644
--- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java
+++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/InNode.java
@@ -35,7 +35,7 @@ public class InNode
private BaseNode exprs;
public InNode(ParserRuleContext ctx, BaseNode value, BaseNode exprs) {
- super( ctx );
+ super(ctx);
this.value = value;
this.exprs = exprs;
}
@@ -64,42 +64,43 @@ public class InNode
@Override
public Boolean evaluate(EvaluationContext ctx) {
- if (exprs == null) return null;
- Object value = this.value.evaluate( ctx );
- Object expr = this.exprs.evaluate( ctx );
- if ( expr != null ) {
- if ( expr instanceof Iterable ) {
+ if (exprs == null)
+ return null;
+ Object value = this.value.evaluate(ctx);
+ Object expr = this.exprs.evaluate(ctx);
+ if (expr != null) {
+ if (expr instanceof Iterable) {
// evaluate in the collection
- for ( Object e : ((Iterable) expr) ) {
+ for (Object e : ((Iterable) expr)) {
// have to compare to Boolean.TRUE because in() might
return null
- if ( in( ctx, value, e ) == Boolean.TRUE ) {
+ if (in(ctx, value, e) == Boolean.TRUE) {
return true;
}
}
return false;
} else {
// evaluate single entity
- return in( ctx, value, expr );
+ return in(ctx, value, expr);
}
}
- ctx.notifyEvt( astEvent(Severity.ERROR, Msg.createMessage(Msg.IS_NULL,
"Expression")) );
+ ctx.notifyEvt(astEvent(Severity.ERROR, Msg.createMessage(Msg.IS_NULL,
"Expression")));
return null;
}
private Boolean in(EvaluationContext ctx, Object value, Object expr) {
// need to improve this to work with unary tests
- if ( expr == null ) {
+ if (expr == null) {
return value == expr;
- } else if ( expr instanceof UnaryTest ) {
- return ((UnaryTest) expr).apply( ctx, value );
- } else if ( expr instanceof Range ) {
+ } else if (expr instanceof UnaryTest) {
+ return ((UnaryTest) expr).apply(ctx, value);
+ } else if (expr instanceof Range) {
try {
- return ((Range) expr).includes(ctx.getFEELDialect(), value );
- } catch ( Exception e ) {
- ctx.notifyEvt( astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE,
value.toString(), expr.toString() ), e ) );
+ return ((Range) expr).includes(ctx, value);
+ } catch (Exception e) {
+ ctx.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE,
value.toString(), expr.toString()), e));
return null;
}
- } else if ( value != null ) {
+ } else if (value != null) {
return BooleanEvalHelper.isEqualsStringCompare(value, expr);
} else {
// value == null, expr != null
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
index dc1a0262b4..914e1353b1 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/UnaryTestNode.java
@@ -20,34 +20,34 @@ package org.kie.dmn.feel.lang.ast;
import java.util.Collection;
import java.util.List;
-import java.util.function.BiPredicate;
import org.antlr.v4.runtime.ParserRuleContext;
import org.kie.dmn.api.feel.runtime.events.FEELEvent.Severity;
import org.kie.dmn.feel.lang.EvaluationContext;
-import org.kie.dmn.feel.lang.FEELDialect;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DefaultDialectHandler;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DialectHandler;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DialectHandlerFactory;
import org.kie.dmn.feel.runtime.Range;
import org.kie.dmn.feel.runtime.UnaryTest;
import org.kie.dmn.feel.runtime.UnaryTestImpl;
-import org.kie.dmn.feel.util.BooleanEvalHelper;
import org.kie.dmn.feel.util.Msg;
public class UnaryTestNode
extends BaseNode {
private UnaryOperator operator;
- private BaseNode value;
+ private BaseNode value;
public enum UnaryOperator {
- LTE( "<=" ),
- LT( "<" ),
- GT( ">" ),
- GTE( ">=" ),
- NE( "!=" ),
- EQ( "=" ),
- NOT( "not" ),
- IN( "in" ),
- TEST( "test");
+ LTE("<="),
+ LT("<"),
+ GT(">"),
+ GTE(">="),
+ NE("!="),
+ EQ("="),
+ NOT("not"),
+ IN("in"),
+ TEST("test");
public final String symbol;
@@ -56,39 +56,39 @@ public class UnaryTestNode
}
public static UnaryOperator determineOperator(String symbol) {
- for ( UnaryOperator op : UnaryOperator.values() ) {
- if ( op.symbol.equals( symbol ) ) {
+ for (UnaryOperator op : UnaryOperator.values()) {
+ if (op.symbol.equals(symbol)) {
return op;
}
}
- throw new IllegalArgumentException( "No operator found for symbol
'" + symbol + "'" );
+ throw new IllegalArgumentException("No operator found for symbol
'" + symbol + "'");
}
}
- public UnaryTestNode( String op, BaseNode value ) {
+ public UnaryTestNode(String op, BaseNode value) {
super();
- setText( op+" "+value.getText() );
- this.operator = UnaryOperator.determineOperator( op );
+ setText(op + " " + value.getText());
+ this.operator = UnaryOperator.determineOperator(op);
this.value = value;
}
- public UnaryTestNode( UnaryOperator op, BaseNode value ) {
+ public UnaryTestNode(UnaryOperator op, BaseNode value) {
super();
- setText( op.symbol+" "+value.getText() );
+ setText(op.symbol + " " + value.getText());
this.operator = op;
this.value = value;
}
public UnaryTestNode(ParserRuleContext ctx, String op, BaseNode value) {
- super( ctx );
- this.operator = UnaryOperator.determineOperator( op );
+ super(ctx);
+ this.operator = UnaryOperator.determineOperator(op);
this.value = value;
}
- public UnaryTestNode( UnaryOperator op, BaseNode value, String text ) {
+ public UnaryTestNode(UnaryOperator op, BaseNode value, String text) {
this.operator = op;
this.value = value;
- this.setText( text);
+ this.setText(text);
}
public UnaryOperator getOperator() {
@@ -117,35 +117,41 @@ public class UnaryTestNode
}
public UnaryTest getUnaryTest() {
- switch ( operator ) {
- case LTE:
- return new UnaryTestImpl( createCompareUnaryTest( (l, r) ->
l.compareTo( r ) <= 0 ) , value.getText() );
- case LT:
- return new UnaryTestImpl( createCompareUnaryTest( (l, r) ->
l.compareTo( r ) < 0 ) , value.getText() );
- case GT:
- return new UnaryTestImpl( createCompareUnaryTest( (l, r) ->
l.compareTo( r ) > 0 ) , value.getText() );
- case GTE:
- return new UnaryTestImpl( createCompareUnaryTest( (l, r) ->
l.compareTo( r ) >= 0 ) , value.getText() );
- case EQ:
- return new UnaryTestImpl( createIsEqualUnaryTest( ) ,
value.getText() );
- case NE:
- return new UnaryTestImpl( createIsNotEqualUnaryTest( ) ,
value.getText() );
- case IN:
- return new UnaryTestImpl( createInUnaryTest() ,
value.getText() );
- case NOT:
- return new UnaryTestImpl( createNotUnaryTest() ,
value.getText() );
- case TEST:
- return new UnaryTestImpl( createBooleanUnaryTest(),
value.getText() );
- }
- return null;
- }
+ return new UnaryTestImpl((context, left) -> {
+ Object right = value.evaluate(context);
+ DialectHandler handler = DialectHandlerFactory.getHandler(context);
- private UnaryTest createCompareUnaryTest( BiPredicate<Comparable,
Comparable> op ) {
- return (context, left) -> {
- Object right = value.evaluate( context );
- // Defaulting FEELDialect to FEEL
- return BooleanEvalHelper.compare(left, right, FEELDialect.FEEL, op
);
- };
+ Object result;
+ switch (operator) {
+ case LTE:
+ result = handler.executeLte(left, right, context);
+ break;
+ case LT:
+ result = handler.executeLt(left, right, context);
+ break;
+ case GT:
+ result = handler.executeGt(left, right, context);
+ break;
+ case GTE:
+ result = handler.executeGte(left, right, context);
+ break;
+ case EQ:
+ return createIsEqualUnaryTest().apply(context, left);
+ case NE:
+ return createIsNotEqualUnaryTest().apply(context, left);
+ case IN:
+ return createInUnaryTest().apply(context, left);
+ case NOT:
+ return createNotUnaryTest().apply(context, left);
+ case TEST:
+ return createBooleanUnaryTest().apply(context, left);
+
+ default:
+ throw new UnsupportedOperationException("Unsupported
operator: " + operator);
+ }
+
+ return (result instanceof Boolean) ? (Boolean) result :
Boolean.FALSE;
+ }, value.getText());
}
/**
@@ -158,22 +164,23 @@ public class UnaryTestNode
return ((Collection) right).contains(left);
} else {
// evaluate single entity
- return BooleanEvalHelper.isEqual(left, right, FEELDialect.FEEL);
+ return DefaultDialectHandler.isEqual(left, right, () -> (left ==
null && right == null), () -> Boolean.FALSE);
+
}
}
- private UnaryTest createIsEqualUnaryTest( ) {
+ private UnaryTest createIsEqualUnaryTest() {
return (context, left) -> {
- Object right = value.evaluate( context );
+ Object right = value.evaluate(context);
return utEqualSemantic(left, right);
};
}
- private UnaryTest createIsNotEqualUnaryTest( ) {
+ private UnaryTest createIsNotEqualUnaryTest() {
return (context, left) -> {
- Object right = value.evaluate( context );
+ Object right = value.evaluate(context);
Boolean result = utEqualSemantic(left, right);
- return result != null ? ! result : null;
+ return result != null ? !result : null;
};
}
@@ -182,10 +189,10 @@ public class UnaryTestNode
if (o == null) {
return false;
}
- Object val = value.evaluate( c );
+ Object val = value.evaluate(c);
if (val instanceof Range) {
try {
- return ((Range) val).includes(c.getFEELDialect(), o);
+ return ((Range) val).includes(c, o);
} catch (Exception e) {
c.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE, o,
val)));
throw e;
@@ -200,38 +207,38 @@ public class UnaryTestNode
private UnaryTest createNotUnaryTest() {
return (c, o) -> {
- Object val = value.evaluate( c );
- if( val == null ) {
+ Object val = value.evaluate(c);
+ if (val == null) {
return null;
}
List<Object> tests = (List<Object>) val;
- for( Object test : tests ) {
- if( test == null ) {
- if( o == null ) {
+ for (Object test : tests) {
+ if (test == null) {
+ if (o == null) {
return false;
}
- } else if( test instanceof UnaryTest ) {
- if( ((UnaryTest)test).apply( c, o ) ) {
+ } else if (test instanceof UnaryTest) {
+ if (((UnaryTest) test).apply(c, o)) {
return false;
}
- } else if( o == null ) {
- if( test == null ) {
+ } else if (o == null) {
+ if (test == null) {
return false;
}
- } else if( test instanceof Range ) {
+ } else if (test instanceof Range) {
try {
- if( ((Range)test).includes(c.getFEELDialect(), o ) ) {
+ if (((Range) test).includes(c, o)) {
return false;
}
- } catch ( Exception e ) {
- c.notifyEvt( astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE, o, test
) ) );
+ } catch (Exception e) {
+ c.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE, o,
test)));
throw e;
}
} else if (test instanceof Collection) {
return !((Collection) test).contains(o);
} else {
// test is a constant, so return false if it is equal to
"o"
- if( test.equals( o ) ) {
+ if (test.equals(o)) {
return false;
}
}
@@ -240,13 +247,13 @@ public class UnaryTestNode
};
}
- private UnaryTest createBooleanUnaryTest( ) {
+ private UnaryTest createBooleanUnaryTest() {
return (context, left) -> {
- Object right = value.evaluate( context );
- if( right instanceof Boolean ) {
+ Object right = value.evaluate(context);
+ if (right instanceof Boolean) {
return (Boolean) right;
} else {
- context.notifyEvt( astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXTENDED_UNARY_TEST_MUST_BE_BOOLEAN, value.getText(),
right ) ) );
+ context.notifyEvt(astEvent(Severity.ERROR,
Msg.createMessage(Msg.EXTENDED_UNARY_TEST_MUST_BE_BOOLEAN, value.getText(),
right)));
return Boolean.FALSE;
}
};
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/BFEELDialectHandler.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/BFEELDialectHandler.java
index 9f15fe194c..251d578f3d 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/BFEELDialectHandler.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/BFEELDialectHandler.java
@@ -24,12 +24,13 @@ import java.time.LocalDate;
import java.time.chrono.ChronoPeriod;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.function.BiFunction;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.FEELDialect;
+import org.kie.dmn.feel.lang.ast.infixexecutors.EqExecutor;
import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
-import org.kie.dmn.feel.util.BooleanEvalHelper;
import static
org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.getString;
@@ -84,67 +85,245 @@ public class BFEELDialectHandler extends
DefaultDialectHandler implements Dialec
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getAndOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
FEELDialect dialect = ctx.getFEELDialect();
+
// Special case: true AND otherwise → false
map.put(
new CheckedPredicate((left, right) -> {
- Boolean leftBool =
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect);
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
Object rightValue = evalRight(right, ctx);
- Boolean rightBool =
BooleanEvalHelper.getBooleanOrDialectDefault(rightValue, dialect);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
return Boolean.TRUE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
}, false),
(left, right) -> Boolean.FALSE);
- // Special case: otherwise AND true → false
+ // Special case: false AND true → false
map.put(
new CheckedPredicate((left, right) -> {
- Boolean leftBool =
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect);
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
Object rightValue = evalRight(right, ctx);
- Boolean rightBool =
BooleanEvalHelper.getBooleanOrDialectDefault(rightValue, dialect);
- return leftBool == null && Boolean.TRUE.equals(rightBool);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
+ return Boolean.FALSE.equals(leftBool) &&
Boolean.TRUE.equals(rightBool);
}, false),
(left, right) -> Boolean.FALSE);
- // Special case: otherwise AND otherwise → false
+ // Special case: false AND false → false
map.put(
new CheckedPredicate((left, right) -> {
- Boolean leftBool =
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect);
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
Object rightValue = evalRight(right, ctx);
- Boolean rightBool =
BooleanEvalHelper.getBooleanOrDialectDefault(rightValue, dialect);
- return leftBool == null && Boolean.FALSE.equals(rightBool);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
+ return Boolean.FALSE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
}, false),
(left, right) -> Boolean.FALSE);
+
map.putAll(getCommonAndOperations(ctx));
return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getEqualOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonEqualOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // Shortcut: null = null → false
+ map.put(
+ new CheckedPredicate((left, right) -> left == null && right ==
null, false),
+ (left, right) -> Boolean.TRUE);
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> isEqual(left, right,
+ () -> Boolean.FALSE, () -> Boolean.FALSE));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getGteOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonGteOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+ // Any non-Boolean coerces to false, so (false,false) --> false
+ map.put(
+ new CheckedPredicate((left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Object rightValue = evalRight(right, ctx);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
+ return Boolean.FALSE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
+ }, false),
+ (left, right) -> Boolean.FALSE);
+
+ // non-Boolean coercion to false
+ map.put(
+ new CheckedPredicate((left, right) -> (!(left instanceof
Boolean) || !(right instanceof Boolean)), false),
+ (left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Boolean rightBool = (right instanceof Boolean) ? (Boolean)
right : Boolean.FALSE;
+ return leftBool || rightBool;
+ });
+ // numeric/comparable >= logic
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> {
+ Boolean greater = compare(left, right, (l, r) ->
l.compareTo(r) > 0,
+ () -> Boolean.FALSE,
+ () -> Boolean.FALSE);
+ //Boolean equal = BooleanEvalHelper.isEqual(left, right,
ctx.getFEELDialect());
+ Boolean equal = (EqExecutor.instance().evaluate(left,
right, ctx) instanceof Boolean)
+ ? (Boolean) EqExecutor.instance().evaluate(left,
right, ctx)
+ : null;
+
+ if (greater == null && equal == null) {
+ return Boolean.FALSE; // BFEEL default
+ }
+ if (Boolean.TRUE.equals(greater) ||
Boolean.TRUE.equals(equal)) {
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ });
+
+ // Fall back to common >= operations for all other cases
+ map.putAll(getCommonGteOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getGtOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonGtOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // Any non-Boolean coerces to false, so (false,false) --> false
+ map.put(
+ new CheckedPredicate((left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Object rightValue = evalRight(right, ctx);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
+ return Boolean.FALSE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
+ }, false),
+ (left, right) -> Boolean.FALSE);
+
+ // non-Boolean coercion to false
+ map.put(
+ new CheckedPredicate((left, right) -> (!(left instanceof
Boolean) || !(right instanceof Boolean)), false),
+ (left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Boolean rightBool = (right instanceof Boolean) ? (Boolean)
right : Boolean.FALSE;
+ return leftBool || rightBool;
+ });
+
+ // numeric/comparable > logic
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> {
+ Boolean greater = compare(left, right,
+ (l, r) -> l.compareTo(r) > 0,
+ () -> Boolean.FALSE,
+ () -> Boolean.FALSE);
+ return Objects.requireNonNullElse(greater, Boolean.FALSE);
+ });
+
+ map.putAll(getCommonGtOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getLteOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonLteOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // Any non-Boolean coerces to false, so (false,false) --> false
+ map.put(
+ new CheckedPredicate((left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Object rightValue = evalRight(right, ctx);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
+ return Boolean.FALSE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
+ }, false),
+ (left, right) -> Boolean.FALSE);
+
+ // General non-Boolean coercion to false
+ map.put(
+ new CheckedPredicate((left, right) -> (!(left instanceof
Boolean) || !(right instanceof Boolean)), false),
+ (left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Boolean rightBool = (right instanceof Boolean) ? (Boolean)
right : Boolean.FALSE;
+ return leftBool || rightBool;
+ });
+
+ // Numeric/comparable ≤ logic
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> {
+ Boolean less = compare(left, right,
+ (l, r) -> l.compareTo(r) < 0,
+ () -> Boolean.FALSE,
+ () -> Boolean.FALSE);
+ Boolean equal = (EqExecutor.instance().evaluate(left,
right, ctx) instanceof Boolean)
+ ? (Boolean) EqExecutor.instance().evaluate(left,
right, ctx)
+ : null;
+
+ if (less == null && equal == null) {
+ return Boolean.FALSE; // BFEEL default
+ }
+ if (Boolean.TRUE.equals(less) ||
Boolean.TRUE.equals(equal)) {
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ });
+
+ map.putAll(getCommonLteOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getLtOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonLtOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // Non-Boolean coerces to false, so (false,false) --> false
+ map.put(
+ new CheckedPredicate((left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Object rightValue = evalRight(right, ctx);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
+ return Boolean.FALSE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
+ }, false),
+ (left, right) -> Boolean.FALSE);
+
+ // General non-Boolean coercion to false
+ map.put(
+ new CheckedPredicate((left, right) -> (!(left instanceof
Boolean) || !(right instanceof Boolean)), false),
+ (left, right) -> {
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
+ Boolean rightBool = (right instanceof Boolean) ? (Boolean)
right : Boolean.FALSE;
+ return leftBool || rightBool;
+ });
+
+ // Numeric/comparable < logic
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> {
+ Boolean less = compare(left, right,
+ (l, r) -> l.compareTo(r) < 0,
+ () -> Boolean.FALSE,
+ () -> Boolean.FALSE);
+ return Objects.requireNonNullElse(less, Boolean.FALSE);
+ });
+ map.putAll(getCommonLtOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getNotEqualOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonNotEqualOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+ // Shortcut: null != null → true
+ map.put(
+ new CheckedPredicate((left, right) -> left == null && right ==
null, false),
+ (left, right) -> Boolean.FALSE);
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> {
+ Boolean result = isEqual(left, right,
+ () -> Boolean.FALSE, // nullFallback
+ () -> Boolean.FALSE // defaultFallback
+ );
+ // If result is null, treat as false
+ return result != null ? !result : Boolean.FALSE;
+ });
+
+ return map;
}
/**
@@ -156,18 +335,16 @@ public class BFEELDialectHandler extends
DefaultDialectHandler implements Dialec
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getOrOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
-
- // Special case: false OR null/otherwise → false (BFEEL override)
+ // false OR otherwise → false
map.put(
new CheckedPredicate((left, right) -> {
- Boolean leftBool =
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect);
+ Boolean leftBool = (left instanceof Boolean) ? (Boolean)
left : Boolean.FALSE;
Object rightValue = evalRight(right, ctx);
- Boolean rightBool =
BooleanEvalHelper.getBooleanOrDialectDefault(rightValue, dialect);
+ Boolean rightBool = (rightValue instanceof Boolean) ?
(Boolean) rightValue : Boolean.FALSE;
return Boolean.FALSE.equals(leftBool) &&
Boolean.FALSE.equals(rightBool);
}, false),
(left, right) -> Boolean.FALSE);
- // Fall back to FEEL semantics for all other cases
+
map.putAll(getCommonOrOperations(ctx));
return map;
}
@@ -199,7 +376,7 @@ public class BFEELDialectHandler extends
DefaultDialectHandler implements Dialec
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getSubOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- //subtraction with Strings → empty string
+ // empty string
map.put(
new CheckedPredicate((left, right) -> (left instanceof String
|| right instanceof String), false),
(left, right) -> "");
@@ -235,12 +412,12 @@ public class BFEELDialectHandler extends
DefaultDialectHandler implements Dialec
new CheckedPredicate((left, right) -> left == null && right
instanceof Duration, false),
(left, right) -> Duration.ZERO);
- // BFEEL-specific: ChronoPeriod * null → zero period
+ // ChronoPeriod * null → zero period
map.put(
new CheckedPredicate((left, right) -> left instanceof
ChronoPeriod && right == null, false),
(left, right) -> ComparablePeriod.ofMonths(0));
- // BFEEL-specific: null * ChronoPeriod → zero period
+ // null * ChronoPeriod → zero period
map.put(
new CheckedPredicate((left, right) -> left == null && right
instanceof ChronoPeriod, false),
(left, right) -> ComparablePeriod.ofMonths(0));
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/DefaultDialectHandler.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/DefaultDialectHandler.java
index b9baf01bc2..11fe10fdd6 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/DefaultDialectHandler.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/DefaultDialectHandler.java
@@ -27,23 +27,37 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.Temporal;
+import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
+import java.time.temporal.TemporalQueries;
+import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
+import java.util.function.Supplier;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.FEELDialect;
import org.kie.dmn.feel.lang.ast.BaseNode;
import org.kie.dmn.feel.lang.ast.InfixOpNode;
-import org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils;
+import org.kie.dmn.feel.lang.ast.infixexecutors.EqExecutor;
+import org.kie.dmn.feel.lang.types.BuiltInType;
import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
+import org.kie.dmn.feel.runtime.Range;
import org.kie.dmn.feel.util.BooleanEvalHelper;
-
-import static org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.*;
+import org.kie.dmn.feel.util.BuiltInTypeUtils;
+import org.kie.dmn.feel.util.DateTimeEvalHelper;
+
+import static
org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.addLocalDateAndDuration;
+import static
org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.commonManageInvalidParameters;
+import static
org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.getBigDecimal;
+import static org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.math;
+import static
org.kie.dmn.feel.lang.ast.infixexecutors.InfixExecutorUtils.subtractTemporals;
+import static org.kie.dmn.feel.util.DateTimeEvalHelper.valuedt;
+import static org.kie.dmn.feel.util.DateTimeEvalHelper.valuet;
import static org.kie.dmn.feel.util.NumberEvalHelper.getBigDecimalOrNull;
import ch.obermuhlner.math.big.BigDecimalMath;
@@ -121,64 +135,53 @@ public abstract class DefaultDialectHandler implements
DialectHandler {
// false AND anything → false (short‑circuit)
map.put(
- new CheckedPredicate((left, right) ->
Boolean.FALSE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left,
dialect)), false),
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.FALSE.equals(left), false),
+ (left, right) -> Boolean.FALSE);
+
+ // left not Boolean → treat as false
+ map.put(
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && dialect.equals(FEELDialect.BFEEL), false),
(left, right) -> Boolean.FALSE);
// true AND true → true
map.put(
- new CheckedPredicate((left, right) ->
Boolean.TRUE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect))
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.TRUE.equals(left)
&& Boolean.TRUE.equals(evalRight(right, ctx)), false),
(left, right) -> Boolean.TRUE);
// true AND false → false
map.put(
- new CheckedPredicate((left, right) ->
Boolean.TRUE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect))
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.TRUE.equals(left)
&& Boolean.FALSE.equals(evalRight(right, ctx)), false),
(left, right) -> Boolean.FALSE);
// true AND otherwise → null
map.put(
- new CheckedPredicate((left, right) ->
Boolean.TRUE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect))
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.TRUE.equals(left)
&& evalRight(right, ctx) == null, false),
(left, right) -> null);
// otherwise AND true → null
map.put(
- new CheckedPredicate((left, right) ->
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect) == null
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && !dialect.equals(FEELDialect.BFEEL)
&& Boolean.TRUE.equals(evalRight(right, ctx)), false),
(left, right) -> null);
// otherwise AND false → false
map.put(
- new CheckedPredicate((left, right) ->
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect) == null
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && !dialect.equals(FEELDialect.BFEEL)
&& Boolean.FALSE.equals(evalRight(right, ctx)), false),
(left, right) -> Boolean.FALSE);
// otherwise AND otherwise → null
map.put(
- new CheckedPredicate((left, right) ->
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect) == null
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && !dialect.equals(FEELDialect.BFEEL)
&& evalRight(right, ctx) == null, false),
(left, right) -> null);
return map;
}
- /**
- * Builds the common 'Equal' operation map used by the dialect handlers.
- *
- * @param ctx : Current Evaluation context
- * @return : a Map of CheckedPredicate to BiFunction representing the
common 'Equal' operations
- */
- Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonEqualOperations(EvaluationContext ctx) {
- Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
-
- map.put(
- new CheckedPredicate((left, right) -> true, false),
- (left, right) -> BooleanEvalHelper.isEqual(left, right,
dialect));
- return map;
- }
-
/**
* Builds the common 'Greater than Or EqualTo' operation map used by the
dialect handlers.
*
@@ -187,15 +190,41 @@ public abstract class DefaultDialectHandler implements
DialectHandler {
*/
Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonGteOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
+ // FEELDialect dialect = ctx.getFEELDialect();
+
+ // left or right is null → null
+ map.put(
+ new CheckedPredicate((left, right) -> left == null || right ==
null, false),
+ (left, right) -> null);
- // left > right OR left == right
+ // both results are Boolean
+ map.put(
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& right instanceof Boolean, false),
+ (left, right) -> {
+ Boolean leftBool = (Boolean) left;
+ Boolean rightBool = (Boolean) right;
+ return leftBool || rightBool;
+ });
+
+ // numeric/comparable >= logic
map.put(
new CheckedPredicate((left, right) -> true, false),
- (left, right) -> InfixExecutorUtils.or(
- BooleanEvalHelper.compare(left, right, dialect,
(leftNum, rightNum) -> leftNum.compareTo(rightNum) > 0),
- BooleanEvalHelper.isEqual(left, right, dialect),
- ctx));
+ (left, right) -> {
+ Boolean greater = compare(left, right,
+ (leftNum, rightNum) -> leftNum.compareTo(rightNum)
> 0,
+ () -> null, // nullFallback for Default dialect
+ () -> null // defaultFallback for unknown types
+ );
+ //Boolean equal = BooleanEvalHelper.isEqual(left, right,
dialect);
+ Boolean equal = (EqExecutor.instance().evaluate(left,
right, ctx) instanceof Boolean)
+ ? (Boolean) EqExecutor.instance().evaluate(left,
right, ctx)
+ : null;
+ if (greater == null && equal == null)
+ return null;
+ if (Boolean.TRUE.equals(greater) ||
Boolean.TRUE.equals(equal))
+ return Boolean.TRUE;
+ return Boolean.FALSE;
+ });
return map;
}
@@ -207,12 +236,18 @@ public abstract class DefaultDialectHandler implements
DialectHandler {
*/
Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonGtOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
-
- // left > right
+ // FEELDialect dialect = ctx.getFEELDialect();
+ // numeric/comparable > logic
map.put(
new CheckedPredicate((left, right) -> true, false),
- (left, right) -> BooleanEvalHelper.compare(left, right,
dialect, (leftNum, rightNum) -> leftNum.compareTo(rightNum) > 0));
+ (left, right) -> {
+
+ // default dialect: keep null
+ return compare(left, right,
+ (l, r) -> l.compareTo(r) > 0,
+ () -> null,
+ () -> null);
+ });
return map;
}
@@ -224,50 +259,62 @@ public abstract class DefaultDialectHandler implements
DialectHandler {
*/
Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonLteOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
- // left < right OR left == right
+ // left or right is null → null
map.put(
- new CheckedPredicate((left, right) -> true, false),
- (left, right) -> InfixExecutorUtils.or(
- BooleanEvalHelper.compare(left, right, dialect,
(leftNum, rightNum) -> leftNum.compareTo(rightNum) < 0),
- BooleanEvalHelper.isEqual(left, right, dialect),
- ctx));
- return map;
- }
+ new CheckedPredicate((left, right) -> left == null || right ==
null, false),
+ (left, right) -> null);
- /**
- * Builds the common 'Less than' operation map used by the dialect
handlers.
- *
- * @param ctx : Current Evaluation context
- * @return : a Map of CheckedPredicate to BiFunction representing the
common 'Less than Or EqualTo' operations
- */
- Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonLtOperations(EvaluationContext ctx) {
- Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
+ // both results are Boolean
+ map.put(
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& right instanceof Boolean, false),
+ (left, right) -> {
+ Boolean leftBool = (Boolean) left;
+ Boolean rightBool = (Boolean) right;
+ return leftBool || rightBool;
+ });
- // left < right
+ // numeric/comparable ≤ logic
map.put(
new CheckedPredicate((left, right) -> true, false),
- (left, right) -> BooleanEvalHelper.compare(left, right,
dialect, (leftNum, rightNum) -> leftNum.compareTo(rightNum) < 0));
+ (left, right) -> {
+ Boolean less = compare(left, right,
+ (l, r) -> l.compareTo(r) < 0,
+ () -> null,
+ () -> null);
+ // Boolean equal = BooleanEvalHelper.isEqual(left, right,
dialect);
+ Boolean equal = (EqExecutor.instance().evaluate(left,
right, ctx) instanceof Boolean)
+ ? (Boolean) EqExecutor.instance().evaluate(left,
right, ctx)
+ : null;
+
+ if (less == null && equal == null) {
+ return null;
+ }
+ if (Boolean.TRUE.equals(less) ||
Boolean.TRUE.equals(equal)) {
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ });
return map;
}
/**
- * Builds the common 'Not EqualTo' operation map used by the dialect
handlers.
+ * Builds the common 'Less than' operation map used by the dialect
handlers.
*
* @param ctx : Current Evaluation context
- * @return : a Map of CheckedPredicate to BiFunction representing the
common 'Not EqualTo' operations
+ * @return : a Map of CheckedPredicate to BiFunction representing the
common 'Less than Or EqualTo' operations
*/
- Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonNotEqualOperations(EvaluationContext ctx) {
+ Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonLtOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
- FEELDialect dialect = ctx.getFEELDialect();
+ // numeric/comparable < logic
map.put(
new CheckedPredicate((left, right) -> true, false),
(left, right) -> {
- Boolean result = BooleanEvalHelper.isEqual(left, right,
dialect);
- return result != null ? !result : null;
+ return compare(left, right,
+ (l, r) -> l.compareTo(r) < 0,
+ () -> null,
+ () -> null);
});
return map;
}
@@ -281,45 +328,49 @@ public abstract class DefaultDialectHandler implements
DialectHandler {
Map<DefaultDialectHandler.CheckedPredicate, BiFunction<Object, Object,
Object>> getCommonOrOperations(EvaluationContext ctx) {
Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
FEELDialect dialect = ctx.getFEELDialect();
-
// true OR anything → true (short‑circuit)
map.put(
- new CheckedPredicate((left, right) ->
Boolean.TRUE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left,
dialect)), false),
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.TRUE.equals(left), false),
(left, right) -> Boolean.TRUE);
+ // left not Boolean → false
+ map.put(
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && dialect.equals(FEELDialect.BFEEL), false),
+ (left, right) -> Boolean.FALSE);
+
// false OR true → true
map.put(
- new CheckedPredicate((left, right) ->
Boolean.FALSE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left,
dialect))
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.FALSE.equals(left)
&& Boolean.TRUE.equals(evalRight(right, ctx)), false),
(left, right) -> Boolean.TRUE);
// false OR false → false
map.put(
- new CheckedPredicate((left, right) ->
Boolean.FALSE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left,
dialect))
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.FALSE.equals(left)
&& Boolean.FALSE.equals(evalRight(right, ctx)), false),
(left, right) -> Boolean.FALSE);
// false OR otherwise → null
map.put(
- new CheckedPredicate((left, right) ->
Boolean.FALSE.equals(BooleanEvalHelper.getBooleanOrDialectDefault(left,
dialect))
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& Boolean.FALSE.equals(left)
&& evalRight(right, ctx) == null, false),
(left, right) -> null);
// otherwise OR true → true
map.put(
- new CheckedPredicate((left, right) ->
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect) == null
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && !dialect.equals(FEELDialect.BFEEL)
&& Boolean.TRUE.equals(evalRight(right, ctx)), false),
(left, right) -> Boolean.TRUE);
// otherwise OR false → null
map.put(
- new CheckedPredicate((left, right) ->
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect) == null
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && !dialect.equals(FEELDialect.BFEEL)
&& Boolean.FALSE.equals(evalRight(right, ctx)), false),
(left, right) -> null);
// otherwise OR otherwise → null
map.put(
- new CheckedPredicate((left, right) ->
BooleanEvalHelper.getBooleanOrDialectDefault(left, dialect) == null
+ new CheckedPredicate((left, right) -> !(left instanceof
Boolean) && !dialect.equals(FEELDialect.BFEEL)
&& evalRight(right, ctx) == null, false),
(left, right) -> null);
@@ -678,4 +729,108 @@ public abstract class DefaultDialectHandler implements
DialectHandler {
return right;
}
}
+
+ /**
+ * Compares left and right operands using the given predicate and returns
TRUE/FALSE accordingly
+ *
+ * @param left
+ * @param right
+ * @param op
+ * @return
+ */
+ public static Boolean compare(Object left, Object right,
BiPredicate<Comparable, Comparable> op, Supplier<Boolean> nullFallback,
+ Supplier<Boolean> defaultFallback) {
+ if (nullFallback == null || defaultFallback == null) {
+ throw new IllegalArgumentException("Fallback suppliers must not be
null");
+ }
+ if (left == null || right == null) {
+ return nullFallback.get();
+ }
+ if (left instanceof ChronoPeriod && right instanceof ChronoPeriod) {
+ // periods have special compare semantics in FEEL as it ignores
"days". Only months and years are compared
+ Long l = ComparablePeriod.toTotalMonths((ChronoPeriod) left);
+ Long r = ComparablePeriod.toTotalMonths((ChronoPeriod) right);
+ return op.test(l, r);
+ }
+ if (left instanceof TemporalAccessor && right instanceof
TemporalAccessor) {
+ // Handle specific cases when both time / datetime
+ TemporalAccessor l = (TemporalAccessor) left;
+ TemporalAccessor r = (TemporalAccessor) right;
+ if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.TIME) {
+ return op.test(valuet(l), valuet(r));
+ } else if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.DATE_TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.DATE_TIME) {
+ return op.test(valuedt(l, r.query(TemporalQueries.zone())),
valuedt(r, l.query(TemporalQueries.zone())));
+ }
+ }
+ if (left instanceof Number && right instanceof Number) {
+ // Handle specific cases when both are Number, converting both to
BigDecimal
+ BigDecimal l = getBigDecimalOrNull(left);
+ BigDecimal r = getBigDecimalOrNull(right);
+ return op.test(l, r);
+ }
+ // last fallback:
+ if ((left instanceof String && right instanceof String) ||
+ (left instanceof Boolean && right instanceof Boolean) ||
+ (left instanceof Comparable &&
left.getClass().isAssignableFrom(right.getClass()))) {
+ Comparable<?> l = (Comparable<?>) left;
+ Comparable<?> r = (Comparable<?>) right;
+ return op.test(l, r);
+ }
+ return defaultFallback.get();
+ }
+
+ /**
+ * Compares left and right for equality applying FEEL semantics to
specific data types
+ *
+ * @param left : the first object to compare
+ * @param right : the second object to compare
+ * @param nullFallback : supplier invoked when either argument is null;
must not be null
+ * @param defaultFallback supplier invoked when no comparison rule applies
or comparison fails; must not be null
+ * @return : result of the provided fallback suppliers depending on the
case
+ */
+ public static Boolean isEqual(Object left, Object right, Supplier<Boolean>
nullFallback, Supplier<Boolean> defaultFallback) {
+ if (nullFallback == null || defaultFallback == null) {
+ throw new IllegalArgumentException("Fallback suppliers must not be
null");
+ }
+ if (left == null || right == null) {
+ return nullFallback.get();
+ }
+
+ // spec defines that "a=[a]", i.e., singleton collections should be
treated as the single element
+ // and vice-versa
+ if (left instanceof Collection && !(right instanceof Collection) &&
((Collection) left).size() == 1) {
+ left = ((Collection) left).toArray()[0];
+ } else if (right instanceof Collection && !(left instanceof
Collection) && ((Collection) right).size() == 1) {
+ right = ((Collection) right).toArray()[0];
+ }
+
+ if (left instanceof Range && right instanceof Range) {
+ return BooleanEvalHelper.isEqual((Range) left, (Range) right);
+ } else if (left instanceof Iterable && right instanceof Iterable) {
+ return BooleanEvalHelper.isEqual((Iterable) left, (Iterable)
right);
+ } else if (left instanceof Map && right instanceof Map) {
+ return BooleanEvalHelper.isEqual((Map) left, (Map) right);
+ } else if (left instanceof ChronoPeriod && right instanceof
ChronoPeriod) {
+ // periods have special compare semantics in FEEL as it ignores
"days". Only months and years are compared
+ Long l = ComparablePeriod.toTotalMonths((ChronoPeriod) left);
+ Long r = ComparablePeriod.toTotalMonths((ChronoPeriod) right);
+ return isEqual(l, r, nullFallback, defaultFallback);
+ } else if (left instanceof TemporalAccessor && right instanceof
TemporalAccessor) {
+ // Handle specific cases when both time / datetime
+ TemporalAccessor l = (TemporalAccessor) left;
+ TemporalAccessor r = (TemporalAccessor) right;
+ if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.TIME) {
+ return isEqual(DateTimeEvalHelper.valuet(l),
DateTimeEvalHelper.valuet(r), nullFallback, defaultFallback);
+ } else if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.DATE_TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.DATE_TIME) {
+ return isEqual(DateTimeEvalHelper.valuedt(l,
r.query(TemporalQueries.zone())), DateTimeEvalHelper.valuedt(r,
l.query(TemporalQueries.zone())), nullFallback, defaultFallback);
+ } // fallback; continue:
+ }
+ //return compare(left, right, feelDialect, (l, r) -> l.compareTo(r) ==
0);
+ // Fallback: Comparable equality
+ return compare(left, right,
+ (l, r) -> l.compareTo(r) == 0,
+ nullFallback,
+ defaultFallback);
+ }
+
}
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/FEELDialectHandler.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/FEELDialectHandler.java
index ac0e21da70..c43795df14 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/FEELDialectHandler.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/lang/ast/dialectHandlers/FEELDialectHandler.java
@@ -18,8 +18,6 @@
*/
package org.kie.dmn.feel.lang.ast.dialectHandlers;
-import org.kie.dmn.feel.lang.EvaluationContext;
-
import java.time.Duration;
import java.time.LocalDate;
import java.time.chrono.ChronoPeriod;
@@ -28,6 +26,8 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiFunction;
+import org.kie.dmn.feel.lang.EvaluationContext;
+
/**
* Handler implementation of the DialectHandler interface providing FEEL
specific
* functionalities
@@ -93,32 +93,155 @@ public class FEELDialectHandler extends
DefaultDialectHandler implements Dialect
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getEqualOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonEqualOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+ map.put(
+ new CheckedPredicate((left, right) -> left == null && right ==
null, false),
+ (left, right) -> Boolean.TRUE);
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> isEqual(left, right,
+ () -> Boolean.FALSE, () -> null));
+
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getGteOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonGteOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // left is Boolean, right is null
+ map.put(
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& right == null, false),
+ (left, right) -> {
+ Boolean leftBool = (Boolean) left;
+ if (Boolean.FALSE.equals(leftBool)) {
+ return null; // false + null → null
+ }
+ return Boolean.TRUE; // true + null → true
+ });
+
+ // right is Boolean, left is null
+ map.put(
+ new CheckedPredicate((left, right) -> right instanceof Boolean
&& left == null, false),
+ (left, right) -> {
+ Boolean rightBool = (Boolean) right;
+ if (Boolean.FALSE.equals(rightBool)) {
+ return null; // null + false → null
+ }
+ return Boolean.TRUE; // null + true → true
+ });
+ // Fall back to common >= operations for all other cases
+ map.putAll(getCommonGteOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getGtOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonGtOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // left is Boolean, right is null
+ map.put(
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& right == null, false),
+ (left, right) -> {
+ Boolean leftBool = (Boolean) left;
+ if (Boolean.FALSE.equals(leftBool)) {
+ return null; // false + null → null
+ }
+ return Boolean.TRUE; // true + null → true
+ });
+
+ // right is Boolean, left is null
+ map.put(
+ new CheckedPredicate((left, right) -> right instanceof Boolean
&& left == null, false),
+ (left, right) -> {
+ Boolean rightBool = (Boolean) right;
+ if (Boolean.FALSE.equals(rightBool)) {
+ return null; // null + false → null
+ }
+ return Boolean.TRUE; // null + true → true
+ });
+
+ // Fall back to common > operations
+ map.putAll(getCommonGtOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getLteOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonLteOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // left is Boolean, right is null
+ map.put(
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& right == null, false),
+ (left, right) -> {
+ Boolean leftBool = (Boolean) left;
+ if (Boolean.FALSE.equals(leftBool)) {
+ return null; // false + null → null
+ }
+ return Boolean.TRUE; // true + null → true
+ });
+
+ // right is Boolean, left is null
+ map.put(
+ new CheckedPredicate((left, right) -> right instanceof Boolean
&& left == null, false),
+ (left, right) -> {
+ Boolean rightBool = (Boolean) right;
+ if (Boolean.FALSE.equals(rightBool)) {
+ return null; // null + false → null
+ }
+ return Boolean.TRUE; // null + true → true
+ });
+
+ // Fall back to common ≤ operations
+ map.putAll(getCommonLteOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getLtOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonLtOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // left is Boolean, right is null
+ map.put(
+ new CheckedPredicate((left, right) -> left instanceof Boolean
&& right == null, false),
+ (left, right) -> {
+ Boolean leftBool = (Boolean) left;
+ if (Boolean.FALSE.equals(leftBool)) {
+ return null; // false + null → null
+ }
+ return Boolean.TRUE; // true + null → true
+ });
+
+ // right is Boolean, left is null
+ map.put(
+ new CheckedPredicate((left, right) -> right instanceof Boolean
&& left == null, false),
+ (left, right) -> {
+ Boolean rightBool = (Boolean) right;
+ if (Boolean.FALSE.equals(rightBool)) {
+ return null; // null + false → null
+ }
+ return Boolean.TRUE; // null + true → true
+ });
+
+ map.putAll(getCommonLtOperations(ctx));
+ return map;
}
@Override
public Map<CheckedPredicate, BiFunction<Object, Object, Object>>
getNotEqualOperations(EvaluationContext ctx) {
- return new LinkedHashMap<>(getCommonNotEqualOperations(ctx));
+ Map<CheckedPredicate, BiFunction<Object, Object, Object>> map = new
LinkedHashMap<>();
+
+ // Shortcut: null != null → false
+ map.put(
+ new CheckedPredicate((left, right) -> left == null && right ==
null, false),
+ (left, right) -> Boolean.FALSE);
+ map.put(
+ new CheckedPredicate((left, right) -> true, false),
+ (left, right) -> {
+ Boolean result = isEqual(left, right, () -> Boolean.FALSE,
() -> null);
+ return result != null ? !result : null;
+ });
+ return map;
}
/**
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 b5664986b4..1377426090 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
@@ -18,15 +18,13 @@
*/
package org.kie.dmn.feel.runtime;
-import org.kie.dmn.feel.lang.FEELDialect;
-
-import java.math.BigDecimal;
-import java.time.LocalDate;
+import org.kie.dmn.feel.lang.EvaluationContext;
public interface Range {
enum RangeBoundary {
- OPEN, CLOSED;
+ OPEN,
+ CLOSED;
}
RangeBoundary getLowBoundary();
@@ -37,7 +35,9 @@ public interface Range {
RangeBoundary getHighBoundary();
- Boolean includes(FEELDialect feelDialect, Object param);
+ //Boolean includes(FEELDialect feelDialect, Object param);
+
+ Boolean includes(EvaluationContext ctx, Object param);
boolean isWithUndefined();
@@ -45,5 +45,4 @@ public interface Range {
Comparable getEnd();
-
}
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java
index 8232247936..00c0493a7a 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java
@@ -33,11 +33,7 @@ import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.impl.FEELBuilder;
import org.kie.dmn.feel.runtime.Range;
import org.kie.dmn.feel.runtime.UnaryTest;
-import org.kie.dmn.feel.runtime.decisiontables.DTDecisionRule;
-import org.kie.dmn.feel.runtime.decisiontables.DTInputClause;
-import org.kie.dmn.feel.runtime.decisiontables.DTOutputClause;
-import org.kie.dmn.feel.runtime.decisiontables.DecisionTableImpl;
-import org.kie.dmn.feel.runtime.decisiontables.HitPolicy;
+import org.kie.dmn.feel.runtime.decisiontables.*;
import org.kie.dmn.feel.runtime.events.FEELEventBase;
import org.kie.dmn.feel.util.Msg;
import org.slf4j.Logger;
@@ -48,30 +44,30 @@ public class DecisionTableFunction
public static final DecisionTableFunction INSTANCE = new
DecisionTableFunction();
- private static final Logger LOG = LoggerFactory.getLogger(
DecisionTableFunction.class );
+ private static final Logger LOG =
LoggerFactory.getLogger(DecisionTableFunction.class);
private DecisionTableFunction() {
- super( "decision table" );
+ super("decision table");
}
/**
- @param inputExpressionList a list of the N>=0 input expressions in
display order
- @param inputValuesList * a list of N input values, corresponding to the
input expressions. Each
- list element is a unary tests literal (see below).
- @param outputs * a name (a string matching grammar rule 27) or a list of
M>0 names
- @param outputValues * if outputs is a list, then output values is a list
of lists of values, one list
- per output; else output values is a list of values for the one output.
- Each value is a string.
- @param ruleList a list of R>0 rules. A rule is a list of N input entries
followed by M
- output entries. An input entry is a unary tests literal. An output entry
is
- an expression represented as a string.
- @param hitPolicy * one of: "U", "A", “P”, “F”, "R", "O", "C", "C+", "C#",
"C<", “C>”
- (default is "U")
- @param defaultOutputValue * if outputs is a list, then default output
value is a context with entries
- composed of outputs and output values; else default output value is one
- of the output values.
+ * @param inputExpressionList a list of the N>=0 input expressions in
display order
+ * @param inputValuesList * a list of N input values, corresponding to the
input expressions. Each
+ * list element is a unary tests literal (see below).
+ * @param outputs * a name (a string matching grammar rule 27) or a list
of M>0 names
+ * @param outputValues * if outputs is a list, then output values is a
list of lists of values, one list
+ * per output; else output values is a list of values for the one
output.
+ * Each value is a string.
+ * @param ruleList a list of R>0 rules. A rule is a list of N input
entries followed by M
+ * output entries. An input entry is a unary tests literal. An
output entry is
+ * an expression represented as a string.
+ * @param hitPolicy * one of: "U", "A", “P”, “F”, "R", "O", "C", "C+",
"C#", "C<", “C>”
+ * (default is "U")
+ * @param defaultOutputValue * if outputs is a list, then default output
value is a context with entries
+ * composed of outputs and output values; else default output value
is one
+ * of the output values.
*/
- public Object invoke(@ParameterName("ctx") EvaluationContext ctx,
+ public Object invoke(@ParameterName("ctx") EvaluationContext ctx,
@ParameterName("outputs") Object outputs,
@ParameterName("input expression list") Object inputExpressionList,
@ParameterName("input values list") List<?> inputValuesList,
@@ -81,62 +77,63 @@ public class DecisionTableFunction
@ParameterName("default output value") Object defaultOutputValue) {
// input expression list can have a single element or be a list
// TODO isn't ^ conflicting with the specs page 136 "input expression
list: a LIST of the"
- List<String> inputExpressions = inputExpressionList instanceof List ?
(List) inputExpressionList : Collections.singletonList( (String)
inputExpressionList );
+ List<String> inputExpressions = inputExpressionList instanceof List ?
(List) inputExpressionList : Collections.singletonList((String)
inputExpressionList);
List<DTInputClause> inputs;
- if ( inputValuesList != null ) {
- List<UnaryTest> inputValues = inputValuesList.stream().map( o ->
toUnaryTest(ctx, o) ).collect( Collectors.toList() );
- if ( inputValues.size() != inputExpressions.size() ) {
+ if (inputValuesList != null) {
+ List<UnaryTest> inputValues = inputValuesList.stream().map(o ->
toUnaryTest(ctx, o)).collect(Collectors.toList());
+ if (inputValues.size() != inputExpressions.size()) {
// TODO handle compilation error
}
// zip inputExpression with its inputValue
- inputs = IntStream.range( 0, inputExpressions.size() )
- .mapToObj( i -> new DTInputClause( inputExpressions.get( i
), inputValuesList.toString(), Collections.singletonList( inputValues.get( i )
), null, false) )
- .collect( Collectors.toList() );
+ inputs = IntStream.range(0, inputExpressions.size())
+ .mapToObj(i -> new DTInputClause(inputExpressions.get(i),
inputValuesList.toString(), Collections.singletonList(inputValues.get(i)),
null, false))
+ .collect(Collectors.toList());
} else {
- inputs = inputExpressions.stream().map( ie -> new DTInputClause(
ie, null, null, null, false ) ).collect( Collectors.toList() );
+ inputs = inputExpressions.stream().map(ie -> new DTInputClause(ie,
null, null, null, false)).collect(Collectors.toList());
}
- List<String> parseOutputs = outputs instanceof List ? (List) outputs :
Collections.singletonList( (String) outputs );
+ List<String> parseOutputs = outputs instanceof List ? (List) outputs :
Collections.singletonList((String) outputs);
List<DTOutputClause> outputClauses;
- if ( outputValues != null ) {
- if ( parseOutputs.size() == 1 ) {
+ if (outputValues != null) {
+ if (parseOutputs.size() == 1) {
outputClauses = new ArrayList<>();
- List<UnaryTest> outputValuesCompiled = objectToUnaryTestList(
ctx, Collections.singletonList( (List<Object>) outputValues ) ).get(0);
- outputClauses.add( new DTOutputClause( parseOutputs.get( 0 ),
outputValuesCompiled ) );
+ List<UnaryTest> outputValuesCompiled =
objectToUnaryTestList(ctx, Collections.singletonList((List<Object>)
outputValues)).get(0);
+ outputClauses.add(new DTOutputClause(parseOutputs.get(0),
outputValuesCompiled));
} else {
- List<List<UnaryTest>> listOfList = objectToUnaryTestList( ctx,
(List<List<Object>>) outputValues );
+ List<List<UnaryTest>> listOfList = objectToUnaryTestList(ctx,
(List<List<Object>>) outputValues);
// zip inputExpression with its inputValue
- outputClauses = IntStream.range( 0, parseOutputs.size() )
- .mapToObj( i -> new DTOutputClause( parseOutputs.get(
i ), listOfList.get( i ) ) )
- .collect( Collectors.toList() );
+ outputClauses = IntStream.range(0, parseOutputs.size())
+ .mapToObj(i -> new DTOutputClause(parseOutputs.get(i),
listOfList.get(i)))
+ .collect(Collectors.toList());
}
} else {
- outputClauses = parseOutputs.stream().map( out -> new
DTOutputClause( out, null ) ).collect( Collectors.toList() );
+ outputClauses = parseOutputs.stream().map(out -> new
DTOutputClause(out, null)).collect(Collectors.toList());
}
// TODO parse default output value.
FEEL feel = FEELBuilder.builder().build();
- List<DTDecisionRule> decisionRules = IntStream.range( 0,
ruleList.size() )
- .mapToObj( index -> toDecisionRule( ctx, feel, index,
ruleList.get( index ), inputExpressions.size() ) )
- .collect( Collectors.toList() );
+ List<DTDecisionRule> decisionRules = IntStream.range(0,
ruleList.size())
+ .mapToObj(index -> toDecisionRule(ctx, feel, index,
ruleList.get(index), inputExpressions.size()))
+ .collect(Collectors.toList());
// TODO is there a way to avoid UUID and get from _evaluation_ ctx the
name of the wrapping context?
// TODO also in this case it is using an ad-hoc created FEEL instance
instead of the "hosted" one.
- DecisionTableImpl dti = new
DecisionTableImpl(UUID.randomUUID().toString(), inputExpressions, inputs,
outputClauses, decisionRules, HitPolicy.fromString(hitPolicy),
FEELBuilder.builder().build());
- return new DTInvokerFunction( dti );
+ DecisionTableImpl dti =
+ new DecisionTableImpl(UUID.randomUUID().toString(),
inputExpressions, inputs, outputClauses, decisionRules,
HitPolicy.fromString(hitPolicy), FEELBuilder.builder().build());
+ return new DTInvokerFunction(dti);
}
protected List<List<UnaryTest>> objectToUnaryTestList(EvaluationContext
ctx, List<List<Object>> values) {
- if ( values == null || values.isEmpty() ) {
+ if (values == null || values.isEmpty()) {
return Collections.emptyList();
}
List<List<UnaryTest>> tests = new ArrayList<>();
- for ( List<Object> lo : values ) {
- List<UnaryTest> uts = new ArrayList<>( );
+ for (List<Object> lo : values) {
+ List<UnaryTest> uts = new ArrayList<>();
tests.add(uts);
- for( Object t : lo ) {
- uts.add( toUnaryTest( ctx, t ) );
+ for (Object t : lo) {
+ uts.add(toUnaryTest(ctx, t));
}
}
return tests;
@@ -144,6 +141,7 @@ public class DecisionTableFunction
/**
* Convert row to DTDecisionRule
+ *
* @param mainCtx the main context is used to identify the hosted
FEELEventManager
* @param embeddedFEEL a possibly cached embedded FEEL to compile the
output expression, error will be reported up to the mainCtx
* @param index
@@ -153,18 +151,18 @@ public class DecisionTableFunction
*/
private static DTDecisionRule toDecisionRule(EvaluationContext mainCtx,
FEEL embeddedFEEL, int index, List<?> rule, int inputSize) {
// TODO should be check indeed block of inputSize n inputs, followed
by block of outputs.
- DTDecisionRule dr = new DTDecisionRule( index, null );
- for ( int i = 0; i < rule.size(); i++ ) {
- Object o = rule.get( i );
- if ( i < inputSize ) {
- dr.getInputEntry().add( toUnaryTest( mainCtx, o ) );
+ DTDecisionRule dr = new DTDecisionRule(index, null);
+ for (int i = 0; i < rule.size(); i++) {
+ Object o = rule.get(i);
+ if (i < inputSize) {
+ dr.getInputEntry().add(toUnaryTest(mainCtx, o));
} else {
- FEELEventListener ruleListener = event -> mainCtx.notifyEvt(
() -> new FEELEventBase(event.getSeverity(),
-
Msg.createMessage(Msg.ERROR_COMPILE_EXPR_DT_FUNCTION_RULE_IDX, index+1,
event.getMessage()),
-
event.getSourceException()));
+ FEELEventListener ruleListener = event -> mainCtx.notifyEvt(()
-> new FEELEventBase(event.getSeverity(),
+
Msg.createMessage(Msg.ERROR_COMPILE_EXPR_DT_FUNCTION_RULE_IDX, index + 1,
event.getMessage()),
+ event.getSourceException()));
embeddedFEEL.addListener(ruleListener);
CompiledExpression compiledExpression =
embeddedFEEL.compile((String) o, embeddedFEEL.newCompilerContext());
- dr.getOutputEntry().add( compiledExpression );
+ dr.getOutputEntry().add(compiledExpression);
embeddedFEEL.removeListener(ruleListener);
}
}
@@ -172,23 +170,23 @@ public class DecisionTableFunction
}
private static UnaryTest toUnaryTest(EvaluationContext ctx, Object o) {
- if ( o instanceof UnaryTest ) {
+ if (o instanceof UnaryTest) {
return (UnaryTest) o;
- } else if ( o instanceof Range ) {
+ } else if (o instanceof Range) {
return (c, x) -> {
try {
- return ((Range) o).includes(ctx.getFEELDialect(), x );
- } catch ( Exception e ) {
- ctx.notifyEvt( () -> new FEELEventBase(
FEELEvent.Severity.ERROR,
- Msg.createMessage(
Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE, o.toString(), x.toString()
),
- e ) );
+ return ((Range) o).includes(ctx, x);
+ } catch (Exception e) {
+ ctx.notifyEvt(() -> new
FEELEventBase(FEELEvent.Severity.ERROR,
+
Msg.createMessage(Msg.EXPRESSION_IS_RANGE_BUT_VALUE_IS_NOT_COMPARABLE,
o.toString(), x.toString()),
+ e));
throw e;
}
};
- } else if ( o instanceof List ) {
- return (c, x) -> ((List<?>) o).contains( x );
+ } else if (o instanceof List) {
+ return (c, x) -> ((List<?>) o).contains(x);
} else {
- return (c, x) -> x.equals( o );
+ return (c, x) -> x.equals(o);
}
}
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/IsFunction.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/IsFunction.java
index bf2acf7f1f..7f6a011972 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/IsFunction.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/IsFunction.java
@@ -21,7 +21,7 @@ package org.kie.dmn.feel.runtime.functions;
import java.time.chrono.ChronoPeriod;
import java.time.temporal.TemporalAccessor;
-import org.kie.dmn.feel.lang.FEELDialect;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DefaultDialectHandler;
import org.kie.dmn.feel.lang.types.BuiltInType;
import org.kie.dmn.feel.runtime.FEELBooleanFunction;
import org.kie.dmn.feel.util.BooleanEvalHelper;
@@ -48,8 +48,11 @@ public class IsFunction extends BaseFEELFunction implements
FEELBooleanFunction
return
FEELFnResult.ofResult(BooleanEvalHelper.isEqualDateTimeInSemanticD(left,
right));
} // fallback; continue:
}
- Boolean fallback = BooleanEvalHelper.isEqual(value1, value2,
FEELDialect.FEEL); // if null implying they are not the same semantic domain
value.
- return FEELFnResult.ofResult(fallback != null ? fallback :
Boolean.FALSE);
+ //Boolean fallback = BooleanEvalHelper.isEqual(value1, value2,
FEELDialect.FEEL); // if null implying they are not the same semantic domain
value
+ // If the values are not in the same domain, result should be false.
+ Boolean fallback = DefaultDialectHandler.isEqual(value1, value2, () ->
Boolean.FALSE, () -> Boolean.FALSE);
+ return FEELFnResult.ofResult(fallback);
+
}
}
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 d70467f79d..c466153ff1 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
@@ -20,11 +20,11 @@ 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;
+import org.kie.dmn.feel.lang.EvaluationContext;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DialectHandler;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DialectHandlerFactory;
import org.kie.dmn.feel.runtime.Range;
-import org.kie.dmn.feel.util.BooleanEvalHelper;
import static org.kie.dmn.feel.lang.ast.UnaryTestNode.UnaryOperator.GT;
import static org.kie.dmn.feel.lang.ast.UnaryTestNode.UnaryOperator.GTE;
@@ -36,8 +36,8 @@ public class RangeImpl
private RangeBoundary lowBoundary;
private RangeBoundary highBoundary;
- private Comparable lowEndPoint;
- private Comparable highEndPoint;
+ private Comparable lowEndPoint;
+ private Comparable highEndPoint;
private boolean withUndefined = false;
public RangeImpl() {
@@ -72,7 +72,7 @@ public class RangeImpl
}
@Override
- public Boolean includes(FEELDialect feelDialect, Object param) {
+ public Boolean includes(EvaluationContext ctx, Object param) {
if (param == null) {
return null;
}
@@ -80,15 +80,15 @@ public class RangeImpl
if (highEndPoint == null || highEndPoint instanceof
UndefinedValueComparable) {
return null;
} else if (lowEndPoint != null) { // it means it is
UndefinedValueComparable
- return negInfRangeIncludes(feelDialect, param);
+ return negInfRangeIncludes(ctx, param);
} else {
return false;
}
} else {
if (highEndPoint instanceof UndefinedValueComparable) {
- return posInfRangeIncludes(feelDialect, param);
+ return posInfRangeIncludes(ctx, param);
} else if (highEndPoint != null) {
- return finiteRangeIncludes(feelDialect, param);
+ return finiteRangeIncludes(ctx, param);
} else {
return false;
}
@@ -102,7 +102,7 @@ public class RangeImpl
@Override
public Comparable getStart() {
- if(lowEndPoint instanceof BigDecimal) {
+ if (lowEndPoint instanceof BigDecimal) {
BigDecimal start = (BigDecimal) lowEndPoint;
start = lowBoundary == Range.RangeBoundary.OPEN ?
start.add(BigDecimal.ONE) : start;
return start;
@@ -128,64 +128,73 @@ public class RangeImpl
return highEndPoint;
}
- private Boolean finiteRangeIncludes(FEELDialect feelDialect, Object param)
{
+ private Boolean finiteRangeIncludes(EvaluationContext ctx, Object param) {
+ DialectHandler handler = DialectHandlerFactory.getHandler(ctx);
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);
+ //return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l,
r) -> l.compareTo(r) < 0), compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) > 0), param);
+ return bothOrThrow((Boolean) handler.executeLt(lowEndPoint, param,
ctx), (Boolean) handler.executeGt(highEndPoint, param, ctx), param);
} else if (lowBoundary == RangeBoundary.OPEN && highBoundary ==
RangeBoundary.CLOSED) {
- return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l, r)
-> l.compareTo(r) < 0) , compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) >= 0), param);
+ //return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l,
r) -> l.compareTo(r) < 0), compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) >= 0), param);
+ return bothOrThrow((Boolean) handler.executeLt(lowEndPoint, param,
ctx), (Boolean) handler.executeGte(highEndPoint, param, ctx), param);
} else if (lowBoundary == RangeBoundary.CLOSED && 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);
+ //return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l,
r) -> l.compareTo(r) <= 0), compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) > 0), param);
+ return bothOrThrow((Boolean) handler.executeLte(lowEndPoint,
param, ctx), (Boolean) handler.executeGt(highEndPoint, param, ctx), param);
} else if (lowBoundary == RangeBoundary.CLOSED && highBoundary ==
RangeBoundary.CLOSED) {
- return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l, r)
-> l.compareTo(r) <= 0) , compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) >= 0), param);
+ //return bothOrThrow(compare(feelDialect, lowEndPoint, param, (l,
r) -> l.compareTo(r) <= 0), compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) >= 0), param);
+ return bothOrThrow((Boolean) handler.executeLte(lowEndPoint,
param, ctx), (Boolean) handler.executeGte(highEndPoint, param, ctx), param);
}
throw new RuntimeException("unknown boundary combination");
}
- private Boolean posInfRangeIncludes(FEELDialect feelDialect, Object param)
{
+ private Boolean posInfRangeIncludes(EvaluationContext ctx, Object param) {
+ DialectHandler handler = DialectHandlerFactory.getHandler(ctx);
if (lowBoundary == RangeBoundary.OPEN) {
- return compare(feelDialect, lowEndPoint, param, (l, r) ->
l.compareTo(r) < 0);
+ //return compare(feelDialect, lowEndPoint, param, (l, r) ->
l.compareTo(r) < 0);
+ return (Boolean) handler.executeLt(lowEndPoint, param, ctx);
} else {
- return compare(feelDialect, lowEndPoint, param, (l, r) ->
l.compareTo(r) <= 0);
+ //return compare(feelDialect, lowEndPoint, param, (l, r) ->
l.compareTo(r) <= 0);
+ return (Boolean) handler.executeLte(lowEndPoint, param, ctx);
}
}
- private Boolean negInfRangeIncludes(FEELDialect feelDialect, Object param)
{
+ private Boolean negInfRangeIncludes(EvaluationContext ctx, Object param) {
+ DialectHandler handler = DialectHandlerFactory.getHandler(ctx);
if (highBoundary == RangeBoundary.OPEN) {
- return compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) > 0);
+ //return compare(ctx, highEndPoint, param, (l, r) ->
l.compareTo(r) > 0);
+ return (Boolean) handler.executeGt(highEndPoint, param, ctx);
} else {
- return compare(feelDialect, highEndPoint, param, (l, r) ->
l.compareTo(r) >= 0);
+ //return compare(ctx, highEndPoint, param, (l, r) ->
l.compareTo(r) >= 0);
+ return (Boolean) handler.executeGte(highEndPoint, param, ctx);
}
}
-
+
private Boolean bothOrThrow(Boolean left, Boolean right, Object param) {
if (left == null || right == null) {
- throw new
IllegalArgumentException("Range.include("+classOf(param)+") not comparable with
"+classOf(lowEndPoint)+", "+classOf(highEndPoint));
+ throw new IllegalArgumentException("Range.include(" +
classOf(param) + ") not comparable with " + classOf(lowEndPoint) + ", " +
classOf(highEndPoint));
}
return left && right;
}
-
+
private static String classOf(Object p) {
return p != null ? p.getClass().toString() : "null";
}
-
- private static Boolean compare(FEELDialect feelDialect, Comparable left,
Object right, BiPredicate<Comparable, Comparable> op) {
- if (left.getClass().isAssignableFrom(right.getClass())) { // short path
- return op.test(left, (Comparable) right);
- }
- return BooleanEvalHelper.compare(left, right, feelDialect, op); //
defer to full DMN/FEEL semantic
- }
@Override
public boolean equals(Object o) {
- if ( this == o ) return true;
- if ( !(o instanceof RangeImpl) ) return false;
+ if (this == o)
+ return true;
+ if (!(o instanceof RangeImpl))
+ return false;
RangeImpl range = (RangeImpl) o;
- if ( lowBoundary != range.lowBoundary ) return false;
- if ( highBoundary != range.highBoundary ) return false;
- if ( lowEndPoint != null ? !lowEndPoint.equals( range.lowEndPoint ) :
range.lowEndPoint != null ) return false;
- return highEndPoint != null ? highEndPoint.equals( range.highEndPoint
) : range.highEndPoint == null;
+ if (lowBoundary != range.lowBoundary)
+ return false;
+ if (highBoundary != range.highBoundary)
+ return false;
+ if (lowEndPoint != null ? !lowEndPoint.equals(range.lowEndPoint) :
range.lowEndPoint != null)
+ return false;
+ return highEndPoint != null ? highEndPoint.equals(range.highEndPoint)
: range.highEndPoint == null;
}
@@ -207,7 +216,7 @@ public class RangeImpl
return (lowBoundary == RangeBoundary.OPEN ? "(" : "[") +
" " + lowEndPoint +
" .. " + highEndPoint +
- " " + ( highBoundary == RangeBoundary.OPEN ? ")" : "]" );
+ " " + (highBoundary == RangeBoundary.OPEN ? ")" : "]");
}
private String withUndefinedtoString() {
@@ -232,4 +241,5 @@ public class RangeImpl
sb.append(" )");
return sb.toString();
}
+
}
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/BooleanEvalHelper.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/BooleanEvalHelper.java
index 559b080f30..6ff976aa41 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/BooleanEvalHelper.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/BooleanEvalHelper.java
@@ -18,131 +18,23 @@
*/
package org.kie.dmn.feel.util;
-import java.math.BigDecimal;
import java.time.ZoneId;
-import java.time.chrono.ChronoPeriod;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
-import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Optional;
-import java.util.function.BiPredicate;
-import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.lang.FEELDialect;
-import org.kie.dmn.feel.lang.ast.BaseNode;
-import org.kie.dmn.feel.lang.ast.InfixOpNode;
-import org.kie.dmn.feel.lang.types.BuiltInType;
-import org.kie.dmn.feel.lang.types.impl.ComparablePeriod;
+import org.kie.dmn.feel.lang.ast.dialectHandlers.DefaultDialectHandler;
import org.kie.dmn.feel.runtime.Range;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.kie.dmn.feel.util.DateTimeEvalHelper.valuedt;
-import static org.kie.dmn.feel.util.DateTimeEvalHelper.valuet;
-import static org.kie.dmn.feel.util.NumberEvalHelper.getBigDecimalOrNull;
-
public class BooleanEvalHelper {
public static final Logger LOG =
LoggerFactory.getLogger(BooleanEvalHelper.class);
- public static Boolean getBooleanOrNull(Object value) {
- if (!(value instanceof Boolean)) {
- return null;
- }
- return (Boolean) value;
- }
-
- /**
- * Compares left and right operands using the given predicate and returns
TRUE/FALSE accordingly
- *
- * @param left
- * @param right
- * @param op
- * @return
- */
- public static Boolean compare(Object left, Object right, FEELDialect
feelDialect, BiPredicate<Comparable, Comparable> op) {
- if (left == null || right == null) {
- return getBooleanOrDialectDefault(null, feelDialect);
- }
- if (left instanceof ChronoPeriod && right instanceof ChronoPeriod) {
- // periods have special compare semantics in FEEL as it ignores
"days". Only months and years are compared
- Long l = ComparablePeriod.toTotalMonths((ChronoPeriod) left);
- Long r = ComparablePeriod.toTotalMonths((ChronoPeriod) right);
- return op.test(l, r);
- }
- if (left instanceof TemporalAccessor && right instanceof
TemporalAccessor) {
- // Handle specific cases when both time / datetime
- TemporalAccessor l = (TemporalAccessor) left;
- TemporalAccessor r = (TemporalAccessor) right;
- if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.TIME) {
- return op.test(valuet(l), valuet(r));
- } else if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.DATE_TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.DATE_TIME) {
- return op.test(valuedt(l, r.query(TemporalQueries.zone())),
valuedt(r, l.query(TemporalQueries.zone())));
- }
- }
- if (left instanceof Number && right instanceof Number) {
- // Handle specific cases when both are Number, converting both to
BigDecimal
- BigDecimal l = getBigDecimalOrNull(left);
- BigDecimal r = getBigDecimalOrNull(right);
- return op.test(l, r);
- }
- // last fallback:
- if ((left instanceof String && right instanceof String) ||
- (left instanceof Boolean && right instanceof Boolean) ||
- (left instanceof Comparable &&
left.getClass().isAssignableFrom(right.getClass()))) {
- Comparable<?> l = (Comparable<?>) left;
- Comparable<?> r = (Comparable<?>) right;
- return op.test(l, r);
- }
- return getBooleanOrDialectDefault(null, feelDialect);
- }
-
- /**
- * Compares left and right for equality applying FEEL semantics to
specific data types
- *
- * @param left
- * @param right
- * @return
- */
- public static Boolean isEqual(Object left, Object right, FEELDialect
feelDialect) {
- if (left == null || right == null) {
- return left == right;
- }
-
- // spec defines that "a=[a]", i.e., singleton collections should be
treated as the single element
- // and vice-versa
- if (left instanceof Collection && !(right instanceof Collection) &&
((Collection) left).size() == 1) {
- left = ((Collection) left).toArray()[0];
- } else if (right instanceof Collection && !(left instanceof
Collection) && ((Collection) right).size() == 1) {
- right = ((Collection) right).toArray()[0];
- }
-
- if (left instanceof Range && right instanceof Range) {
- return isEqual((Range) left, (Range) right);
- } else if (left instanceof Iterable && right instanceof Iterable) {
- return isEqual((Iterable) left, (Iterable) right);
- } else if (left instanceof Map && right instanceof Map) {
- return isEqual((Map) left, (Map) right);
- } else if (left instanceof ChronoPeriod && right instanceof
ChronoPeriod) {
- // periods have special compare semantics in FEEL as it ignores
"days". Only months and years are compared
- Long l = ComparablePeriod.toTotalMonths((ChronoPeriod) left);
- Long r = ComparablePeriod.toTotalMonths((ChronoPeriod) right);
- return isEqual(l, r, feelDialect);
- } else if (left instanceof TemporalAccessor && right instanceof
TemporalAccessor) {
- // Handle specific cases when both time / datetime
- TemporalAccessor l = (TemporalAccessor) left;
- TemporalAccessor r = (TemporalAccessor) right;
- if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.TIME) {
- return isEqual(DateTimeEvalHelper.valuet(l),
DateTimeEvalHelper.valuet(r), feelDialect);
- } else if (BuiltInTypeUtils.determineTypeFromInstance(left) ==
BuiltInType.DATE_TIME && BuiltInTypeUtils.determineTypeFromInstance(right) ==
BuiltInType.DATE_TIME) {
- return isEqual(DateTimeEvalHelper.valuedt(l,
r.query(TemporalQueries.zone())), DateTimeEvalHelper.valuedt(r,
l.query(TemporalQueries.zone())), feelDialect);
- } // fallback; continue:
- }
- return compare(left, right, feelDialect, (l, r) -> l.compareTo(r) ==
0);
- }
-
/**
* DMNv1.2 Table 48: Specific semantics of equality
* DMNv1.3 Table 71: Semantic of date and time functions
@@ -186,18 +78,21 @@ public class BooleanEvalHelper {
/**
* This method consider if the <code>value</code> object is a
<code>String</code>
* In that case, return the {@link String#equals(Object)} result
- * Otherwise, default to the {@link #isEqual(Object, Object, FEELDialect)}
+ * Otherwise, default to the isEqual method
*
* @param value
* @param itemFromList
- * @return
+ * @return the boolean result based on the conditions
*/
public static boolean isEqualsStringCompare(Object value, Object
itemFromList) {
+ if (value == null && itemFromList == null) {
+ return true; // both null → equal
+ }
if (value instanceof String) {
return value.equals(itemFromList);
} else {
// Defaulting FEELDialect to FEEL
- Boolean dmnEqual = isEqual(value, itemFromList, FEELDialect.FEEL);
+ Boolean dmnEqual = DefaultDialectHandler.isEqual(value,
itemFromList, () -> null, () -> null);
return dmnEqual != null && dmnEqual;
}
}
@@ -219,59 +114,31 @@ public class BooleanEvalHelper {
return toReturn;
}
- /**
- * Return <code>TRUE</code> if it is the original object or, depending on
the FEELDialect, a default value
- *
- * @param rawReturn
- * @param feelDialect
- * @return
- */
- public static Boolean getTrueOrDialectDefault(Object rawReturn,
FEELDialect feelDialect) {
- if (rawReturn instanceof Boolean bool && bool) {
- return bool;
- } else {
- return getBooleanOrDialectDefault(null, feelDialect);
- }
- }
-
- /**
- * Return <code>TRUE</code> if it is the original object or, depending on
the FEELDialect, a default value
- *
- * @param rawReturn
- * @param feelDialect
- * @return
- */
- public static Boolean getFalseOrDialectDefault(Object rawReturn,
FEELDialect feelDialect) {
- if (rawReturn instanceof Boolean bool && (!bool)) {
- return bool;
- } else {
- return getBooleanOrDialectDefault(null, feelDialect);
- }
- }
-
- static Boolean isEqual(Range left, Range right) {
+ public static Boolean isEqual(Range left, Range right) {
return left.equals(right);
}
- static Boolean isEqual(Iterable left, Iterable right) {
+ public static Boolean isEqual(Iterable left, Iterable right) {
Iterator li = left.iterator();
Iterator ri = right.iterator();
while (li.hasNext() && ri.hasNext()) {
Object l = li.next();
Object r = ri.next();
- if (!isEqualObject(l, r)) return false;
+ if (!isEqualObject(l, r))
+ return false;
}
return li.hasNext() == ri.hasNext();
}
- static Boolean isEqual(Map<?, ?> left, Map<?, ?> right) {
+ public static Boolean isEqual(Map<?, ?> left, Map<?, ?> right) {
if (left.size() != right.size()) {
return false;
}
for (Map.Entry le : left.entrySet()) {
Object l = le.getValue();
Object r = right.get(le.getKey());
- if (!isEqualObject(l, r)) return false;
+ if (!isEqualObject(l, r))
+ return false;
}
return true;
}
diff --git
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/DateTimeEvalHelper.java
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/DateTimeEvalHelper.java
index 92ae726a81..9e3f895964 100644
---
a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/DateTimeEvalHelper.java
+++
b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/DateTimeEvalHelper.java
@@ -18,13 +18,7 @@
*/
package org.kie.dmn.feel.util;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.LocalTime;
-import java.time.OffsetDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
+import java.time.*;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
@@ -36,12 +30,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DateTimeEvalHelper {
- public static final Logger LOG = LoggerFactory.getLogger(
DateTimeEvalHelper.class );
+ public static final Logger LOG =
LoggerFactory.getLogger(DateTimeEvalHelper.class);
public static ZonedDateTime coerceDateTime(final LocalDate value) {
return ZonedDateTime.of(value, LocalTime.of(0, 0, 0, 0),
ZoneOffset.UTC);
}
-
+
public static String toParsableString(TemporalAccessor temporalAccessor) {
int hour = temporalAccessor.get(ChronoField.HOUR_OF_DAY);
int minute = temporalAccessor.get(ChronoField.MINUTE_OF_HOUR);
@@ -51,10 +45,10 @@ public class DateTimeEvalHelper {
}
/**
- * DMNv1.2 10.3.2.3.6 date-time, valuedt(date and time), for use in this
{@link BooleanEvalHelper#compare(Object, Object, EvaluationContext,
BiPredicate)}
+ * DMNv1.2 10.3.2.3.6 date-time, valuedt(date and time), for use in this
compare method
* DMNv1.3 also used for equality DMN13-35
*/
- static long valuedt(TemporalAccessor datetime, ZoneId
otherTimezoneOffset) {
+ public static long valuedt(TemporalAccessor datetime, ZoneId
otherTimezoneOffset) {
ZoneId alternativeTZ =
Optional.ofNullable(otherTimezoneOffset).orElse(ZoneOffset.UTC);
if (datetime instanceof LocalDateTime) {
return ((LocalDateTime)
datetime).atZone(alternativeTZ).toEpochSecond();
@@ -71,7 +65,7 @@ public class DateTimeEvalHelper {
* DMNv1.2 10.3.2.3.4 time, valuet(time), for use in this {@link
BooleanEvalHelper#compare(Object, Object, EvaluationContext, BiPredicate)}
* DMNv1.3 also used for equality DMN13-35
*/
- static long valuet(TemporalAccessor time) {
+ public static long valuet(TemporalAccessor time) {
long result = 0;
result += time.get(ChronoField.HOUR_OF_DAY) * (60 * 60);
result += time.get(ChronoField.MINUTE_OF_HOUR) * (60);
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 4ecfb53fa7..9e63208667 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
@@ -18,14 +18,14 @@
*/
package org.kie.dmn.feel.runtime.impl;
-import org.junit.jupiter.api.Test;
-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 org.junit.jupiter.api.Test;
+import org.kie.dmn.feel.lang.EvaluationContext;
+import org.kie.dmn.feel.runtime.Range;
+
import static org.assertj.core.api.Assertions.assertThat;
class RangeImplTest {
@@ -79,47 +79,48 @@ class RangeImplTest {
@Test
void includes() {
RangeImpl rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, 10, 15,
Range.RangeBoundary.OPEN);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, -15)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 5)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 10)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 12)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 15)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 156)).isFalse();
+ EvaluationContext ctx = null;
+ assertThat(rangeImpl.includes(null, -15)).isFalse();
+ assertThat(rangeImpl.includes(null, 5)).isFalse();
+ assertThat(rangeImpl.includes(null, 10)).isFalse();
+ assertThat(rangeImpl.includes(null, 12)).isTrue();
+ assertThat(rangeImpl.includes(null, 15)).isFalse();
+ assertThat(rangeImpl.includes(null, 156)).isFalse();
rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, 10, 15,
Range.RangeBoundary.OPEN);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 10)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 12)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 15)).isFalse();
+ assertThat(rangeImpl.includes(null, 10)).isTrue();
+ assertThat(rangeImpl.includes(null, 12)).isTrue();
+ assertThat(rangeImpl.includes(null, 15)).isFalse();
rangeImpl = new RangeImpl(Range.RangeBoundary.OPEN, 10, 15,
Range.RangeBoundary.CLOSED);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 10)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 12)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 15)).isTrue();
+ assertThat(rangeImpl.includes(null, 10)).isFalse();
+ assertThat(rangeImpl.includes(null, 12)).isTrue();
+ assertThat(rangeImpl.includes(null, 15)).isTrue();
rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, 10, 15,
Range.RangeBoundary.CLOSED);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 10)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 12)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 15)).isTrue();
+ assertThat(rangeImpl.includes(null, 10)).isTrue();
+ assertThat(rangeImpl.includes(null, 12)).isTrue();
+ assertThat(rangeImpl.includes(null, 15)).isTrue();
rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, new
UndefinedValueComparable(), 15, Range.RangeBoundary.CLOSED);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, -1456)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 20)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, null)).isNull();
+ assertThat(rangeImpl.includes(null, -1456)).isTrue();
+ assertThat(rangeImpl.includes(null, 20)).isFalse();
+ assertThat(rangeImpl.includes(null, null)).isNull();
rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, 15, new
UndefinedValueComparable(), Range.RangeBoundary.CLOSED);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, -1456)).isFalse();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 20)).isTrue();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, null)).isNull();
+ assertThat(rangeImpl.includes(null, -1456)).isFalse();
+ assertThat(rangeImpl.includes(null, 20)).isTrue();
+ assertThat(rangeImpl.includes(null, null)).isNull();
rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, null, new
UndefinedValueComparable(), Range.RangeBoundary.CLOSED);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, -1456)).isNull();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 20)).isNull();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, null)).isNull();
+ assertThat(rangeImpl.includes(null, -1456)).isNull();
+ assertThat(rangeImpl.includes(null, 20)).isNull();
+ assertThat(rangeImpl.includes(null, null)).isNull();
rangeImpl = new RangeImpl(Range.RangeBoundary.CLOSED, new
UndefinedValueComparable(), null, Range.RangeBoundary.CLOSED);
- assertThat(rangeImpl.includes(FEELDialect.FEEL, -1456)).isNull();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, 20)).isNull();
- assertThat(rangeImpl.includes(FEELDialect.FEEL, null)).isNull();
+ assertThat(rangeImpl.includes(null, -1456)).isNull();
+ assertThat(rangeImpl.includes(null, 20)).isNull();
+ assertThat(rangeImpl.includes(null, null)).isNull();
}
@Test
diff --git
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/BooleanEvalHelperTest.java
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/BooleanEvalHelperTest.java
index 838d21aded..b7d60baf1f 100644
---
a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/BooleanEvalHelperTest.java
+++
b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/BooleanEvalHelperTest.java
@@ -18,39 +18,32 @@
*/
package org.kie.dmn.feel.util;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.Date;
-
import org.junit.jupiter.api.Test;
import org.kie.dmn.feel.lang.FEELDialect;
-import org.mockito.MockedStatic;
-import org.mockito.Mockito;
import static org.assertj.core.api.Assertions.assertThat;
import static
org.kie.dmn.feel.util.BooleanEvalHelper.getBooleanOrDialectDefault;
-import static org.kie.dmn.feel.util.BooleanEvalHelper.getFalseOrDialectDefault;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
class BooleanEvalHelperTest {
-
- @Test
- void numericValuesComparative() {
- assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1),
BigDecimal.valueOf(2), FEELDialect.FEEL, (l, r) -> l.compareTo(r) <
0)).isTrue();
- assertThat(BooleanEvalHelper.compare(1.0, 2.0, FEELDialect.FEEL,(l, r)
-> l.compareTo(r) < 0)).isTrue();
- assertThat(BooleanEvalHelper.compare(1, 2, FEELDialect.FEEL, (l, r) ->
l.compareTo(r) > 0)).isFalse();
- assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1), 2,
FEELDialect.FEEL,(l, r) -> l.compareTo(r) > 0)).isFalse();
- assertThat(BooleanEvalHelper.compare(1, BigDecimal.valueOf(2),
FEELDialect.FEEL,(l, r) -> l.compareTo(r) < 0)).isTrue();
- assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1), 2.3,
FEELDialect.FEEL,(l, r) -> l.compareTo(r) == 0)).isFalse();
- assertThat(BooleanEvalHelper.compare(1.2, BigDecimal.valueOf(1.2),
FEELDialect.FEEL, (l, r) -> l.compareTo(r) == 0)).isTrue();
- assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1), 0L,
FEELDialect.FEEL, (l, r) -> l.compareTo(r) > 0)).isTrue();
- assertThat(BooleanEvalHelper.compare(10L, BigDecimal.valueOf(2),
FEELDialect.FEEL, (l, r) -> l.compareTo(r) < 0)).isFalse();
- assertThat(BooleanEvalHelper.compare(BigInteger.valueOf(1),
BigInteger.valueOf(2), FEELDialect.FEEL, (l, r) -> l.compareTo(r) ==
0)).isFalse();
- assertThat(BooleanEvalHelper.compare(BigInteger.valueOf(1), 2,
FEELDialect.FEEL, (l, r) -> l.compareTo(r) < 0)).isTrue();
- assertThat(BooleanEvalHelper.compare(BigInteger.valueOf(1), 2.3,
FEELDialect.FEEL,(l, r) -> l.compareTo(r) == 0)).isFalse();
- }
+ //TODO Will move to FeelDialectTest clss
+ /*
+ * @Test
+ * void numericValuesComparative() {
+ * assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1),
BigDecimal.valueOf(2), (l, r) -> l.compareTo(r) < 0, () -> null, () ->
null)).isTrue();
+ * assertThat(BooleanEvalHelper.compare(1.0, 2.0, (l, r) -> l.compareTo(r)
< 0, () -> null, () -> null)).isTrue();
+ * assertThat(BooleanEvalHelper.compare(1, 2, (l, r) -> l.compareTo(r) >
0, () -> null, () -> null)).isFalse();
+ * assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1), 2, (l, r)
-> l.compareTo(r) > 0, () -> null, () -> null)).isFalse();
+ * assertThat(BooleanEvalHelper.compare(1, BigDecimal.valueOf(2), (l, r)
-> l.compareTo(r) < 0, () -> null, () -> null)).isTrue();
+ * assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1), 2.3, (l, r)
-> l.compareTo(r) == 0, () -> null, () -> null)).isFalse();
+ * assertThat(BooleanEvalHelper.compare(1.2, BigDecimal.valueOf(1.2), (l,
r) -> l.compareTo(r) == 0, () -> null, () -> null)).isTrue();
+ * assertThat(BooleanEvalHelper.compare(BigDecimal.valueOf(1), 0L, (l, r)
-> l.compareTo(r) > 0, () -> null, () -> null)).isTrue();
+ * assertThat(BooleanEvalHelper.compare(10L, BigDecimal.valueOf(2), (l, r)
-> l.compareTo(r) < 0, () -> null, () -> null)).isFalse();
+ * assertThat(BooleanEvalHelper.compare(BigInteger.valueOf(1),
BigInteger.valueOf(2), (l, r) -> l.compareTo(r) == 0, () -> null, () ->
null)).isFalse();
+ * assertThat(BooleanEvalHelper.compare(BigInteger.valueOf(1), 2, (l, r)
-> l.compareTo(r) < 0, () -> null, () -> null)).isTrue();
+ * assertThat(BooleanEvalHelper.compare(BigInteger.valueOf(1), 2.3, (l, r)
-> l.compareTo(r) == 0, () -> null, () -> null)).isFalse();
+ * }
+ */
@Test
void getBooleanOrDialectDefaultFEEL() {
@@ -68,22 +61,6 @@ class BooleanEvalHelperTest {
assertThat(getBooleanOrDialectDefault(null,
FEELDialect.BFEEL)).isEqualTo(Boolean.FALSE);
}
- @Test
- void getFalseOrDialectDefaultFEEL() {
- assertThat(getFalseOrDialectDefault(false,
FEELDialect.FEEL)).isEqualTo(Boolean.FALSE);
- assertThat(getFalseOrDialectDefault(true, FEELDialect.FEEL)).isNull();
- assertThat(getFalseOrDialectDefault("true",
FEELDialect.FEEL)).isNull();
- assertThat(getFalseOrDialectDefault(null, FEELDialect.FEEL)).isNull();
- }
-
- @Test
- void getFalseOrDialectDefaultBFEEL() {
- assertThat(getFalseOrDialectDefault(false,
FEELDialect.BFEEL)).isEqualTo(Boolean.FALSE);
- assertThat(getFalseOrDialectDefault(true,
FEELDialect.BFEEL)).isEqualTo(Boolean.FALSE);
- assertThat(getFalseOrDialectDefault("true",
FEELDialect.BFEEL)).isEqualTo(Boolean.FALSE);
- assertThat(getFalseOrDialectDefault(null,
FEELDialect.BFEEL)).isEqualTo(Boolean.FALSE);
- }
-
@Test
void isEqualsSCWithStringValue() {
assertThat(BooleanEvalHelper.isEqualsStringCompare("", "")).isTrue();
diff --git
a/kie-dmn/kie-dmn-validation/src/main/java/org/kie/dmn/validation/dtanalysis/model/Interval.java
b/kie-dmn/kie-dmn-validation/src/main/java/org/kie/dmn/validation/dtanalysis/model/Interval.java
index d482b15290..2644be4641 100644
---
a/kie-dmn/kie-dmn-validation/src/main/java/org/kie/dmn/validation/dtanalysis/model/Interval.java
+++
b/kie-dmn/kie-dmn-validation/src/main/java/org/kie/dmn/validation/dtanalysis/model/Interval.java
@@ -18,16 +18,11 @@
*/
package org.kie.dmn.validation.dtanalysis.model;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import org.kie.dmn.feel.lang.FEELDialect;
+import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.runtime.Range;
import org.kie.dmn.feel.runtime.Range.RangeBoundary;
import org.kie.dmn.feel.runtime.impl.RangeImpl;
@@ -83,7 +78,8 @@ public class Interval {
private Interval(Bound<?> lowerBound, Bound<?> upperBound) {
this.lowerBound = new Bound(lowerBound.getValue(),
lowerBound.getBoundaryType(), this);
this.upperBound = new Bound(upperBound.getValue(),
upperBound.getBoundaryType(), this);
- if (lowerBound.getParent() != null && upperBound.getParent() != null
&& lowerBound.getParent().rule == upperBound.getParent().rule &&
lowerBound.getParent().col == upperBound.getParent().col) {
+ if (lowerBound.getParent() != null && upperBound.getParent() != null
&& lowerBound.getParent().rule == upperBound.getParent().rule
+ && lowerBound.getParent().col == upperBound.getParent().col) {
this.rule = lowerBound.getParent().rule;
this.col = lowerBound.getParent().col;
} else {
@@ -108,9 +104,9 @@ public class Interval {
@Override
public String toString() {
return (lowerBound.getBoundaryType() == RangeBoundary.OPEN ? "(" :
"[") +
- " " + Bound.boundValueToString(lowerBound.getValue()) +
- " .. " + Bound.boundValueToString(upperBound.getValue()) +
- " " + (upperBound.getBoundaryType() == RangeBoundary.OPEN ? ")"
: "]");
+ " " + Bound.boundValueToString(lowerBound.getValue()) +
+ " .. " + Bound.boundValueToString(upperBound.getValue()) +
+ " " + (upperBound.getBoundaryType() == RangeBoundary.OPEN ?
")" : "]");
}
public Bound<?> getLowerBound() {
@@ -171,13 +167,14 @@ public class Interval {
public boolean asRangeIncludes(Object param) {
// Defaulting FEELDialect to FEEL
- Boolean result = this.asRange.includes(FEELDialect.FEEL, param);
+ EvaluationContext ctx = null;
+ Boolean result = this.asRange.includes(ctx, param);
if (result != null) {
return result;
} else if (this.lowerBound.getValue() == NEG_INF &&
- this.lowerBound.getBoundaryType() == RangeBoundary.CLOSED &&
- this.upperBound.getValue() == POS_INF &&
- this.upperBound.getBoundaryType() == RangeBoundary.CLOSED) {
+ this.lowerBound.getBoundaryType() == RangeBoundary.CLOSED &&
+ this.upperBound.getValue() == POS_INF &&
+ this.upperBound.getBoundaryType() == RangeBoundary.CLOSED) {
return true;
} else {
return false;
@@ -196,7 +193,7 @@ public class Interval {
return thisLeftLower && oRightHigher && (chained || adj);
}
- public static Range.RangeBoundary invertBoundary(Range.RangeBoundary b) {
+ public static RangeBoundary invertBoundary(RangeBoundary b) {
if (b == RangeBoundary.OPEN) {
return RangeBoundary.CLOSED;
} else if (b == RangeBoundary.CLOSED) {
@@ -250,26 +247,26 @@ public class Interval {
List<Interval> results = new ArrayList<>();
if (!domain.lowerBound.equals(interval.lowerBound)) {
Interval left = new Interval(domain.lowerBound.getBoundaryType(),
- domain.lowerBound.getValue(),
- interval.lowerBound.getValue(),
-
invertBoundary(interval.lowerBound.getBoundaryType()),
- interval.rule,
- interval.col);
+ domain.lowerBound.getValue(),
+ interval.lowerBound.getValue(),
+ invertBoundary(interval.lowerBound.getBoundaryType()),
+ interval.rule,
+ interval.col);
results.add(left);
}
if (!domain.upperBound.equals(interval.upperBound)) {
Interval right = new
Interval(invertBoundary(interval.upperBound.getBoundaryType()),
- interval.upperBound.getValue(),
- domain.upperBound.getValue(),
- domain.upperBound.getBoundaryType(),
- interval.rule,
- interval.col);
+ interval.upperBound.getValue(),
+ domain.upperBound.getValue(),
+ domain.upperBound.getBoundaryType(),
+ interval.rule,
+ interval.col);
results.add(right);
}
LOG.debug("results {}", results);
return results;
}
-
+
public static List<Interval> invertOverDomain(List<Interval> intervals,
Interval domain) {
List<Interval> results = new ArrayList<>();
final List<Interval> is = flatten(intervals);
@@ -281,11 +278,11 @@ public class Interval {
Interval firstInterval = iterator.next();
if (!domain.lowerBound.equals(firstInterval.lowerBound)) {
Interval left = new Interval(domain.lowerBound.getBoundaryType(),
- domain.lowerBound.getValue(),
- firstInterval.lowerBound.getValue(),
-
invertBoundary(firstInterval.lowerBound.getBoundaryType()),
- firstInterval.rule,
- firstInterval.col);
+ domain.lowerBound.getValue(),
+ firstInterval.lowerBound.getValue(),
+ invertBoundary(firstInterval.lowerBound.getBoundaryType()),
+ firstInterval.rule,
+ firstInterval.col);
results.add(left);
}
Interval previousInterval = firstInterval;
@@ -294,22 +291,22 @@ public class Interval {
if
((!previousInterval.upperBound.getValue().equals(nextInterval.lowerBound.getValue()))
|| (previousInterval.upperBound.getBoundaryType() ==
RangeBoundary.OPEN && nextInterval.lowerBound.getBoundaryType() ==
RangeBoundary.OPEN)) {
Interval iNew = new
Interval(invertBoundary(previousInterval.upperBound.getBoundaryType()),
-
previousInterval.upperBound.getValue(),
-
nextInterval.lowerBound.getValue(),
-
invertBoundary(nextInterval.lowerBound.getBoundaryType()),
- previousInterval.rule,
- previousInterval.col);
+ previousInterval.upperBound.getValue(),
+ nextInterval.lowerBound.getValue(),
+
invertBoundary(nextInterval.lowerBound.getBoundaryType()),
+ previousInterval.rule,
+ previousInterval.col);
results.add(iNew);
}
previousInterval = nextInterval;
}
if (!domain.upperBound.equals(previousInterval.upperBound)) {
Interval right = new
Interval(invertBoundary(previousInterval.upperBound.getBoundaryType()),
-
previousInterval.upperBound.getValue(),
- domain.upperBound.getValue(),
- domain.upperBound.getBoundaryType(),
- previousInterval.rule,
- previousInterval.col);
+ previousInterval.upperBound.getValue(),
+ domain.upperBound.getValue(),
+ domain.upperBound.getBoundaryType(),
+ previousInterval.rule,
+ previousInterval.col);
results.add(right);
}
LOG.debug("results {}", results);
@@ -317,21 +314,21 @@ public class Interval {
}
public String asHumanFriendly(Domain domain) {
- if (lowerBound.getValue().equals(upperBound.getValue())
- && lowerBound.getBoundaryType() == RangeBoundary.CLOSED
+ if (lowerBound.getValue().equals(upperBound.getValue())
+ && lowerBound.getBoundaryType() == RangeBoundary.CLOSED
&& upperBound.getBoundaryType() == RangeBoundary.CLOSED) {
return Bound.boundValueToString(lowerBound.getValue());
} else if (domain.isDiscreteDomain()) {
List<?> dValues = domain.getDiscreteValues();
int posL = dValues.indexOf(lowerBound.getValue());
if (posL < dValues.size() - 1
- && dValues.get(posL + 1).equals(upperBound.getValue())
- && lowerBound.getBoundaryType() == RangeBoundary.CLOSED
+ && dValues.get(posL + 1).equals(upperBound.getValue())
+ && lowerBound.getBoundaryType() == RangeBoundary.CLOSED
&& upperBound.getBoundaryType() == RangeBoundary.OPEN) {
return Bound.boundValueToString(lowerBound.getValue());
} else if (posL == dValues.size() - 1
- && lowerBound.getBoundaryType() == RangeBoundary.CLOSED
- && upperBound.getBoundaryType() ==
RangeBoundary.CLOSED) {
+ && lowerBound.getBoundaryType() == RangeBoundary.CLOSED
+ && upperBound.getBoundaryType() == RangeBoundary.CLOSED) {
return Bound.boundValueToString(lowerBound.getValue());
} else {
return this.toString();
@@ -376,8 +373,8 @@ public class Interval {
public boolean isSingularity() {
return lowerBound.getBoundaryType() == RangeBoundary.CLOSED &&
- upperBound.getBoundaryType() == RangeBoundary.CLOSED &&
- BoundValueComparator.compareValueDispatchingToInf(lowerBound,
upperBound) == 0;
+ upperBound.getBoundaryType() == RangeBoundary.CLOSED &&
+ BoundValueComparator.compareValueDispatchingToInf(lowerBound,
upperBound) == 0;
}
public static boolean adjOrOverlap(List<Interval> intervalsA,
List<Interval> intervalsB) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]