Skip to content

Commit

Permalink
[#655] DMN 1.5: Clarify edge cases for unary tests with ranges (DMN15…
Browse files Browse the repository at this point in the history
…-78). Move mapping for Operator Ranges in constructor of Range
  • Loading branch information
opatrascoiu committed Dec 10, 2024
1 parent 94645d8 commit 925403b
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public Object visit(ExpressionTest<Type> element, DMNContext context) {
public Object visit(OperatorRange<Type> element, DMNContext context) {
LOGGER.debug("Visiting element '{}'", element);

String operator = element.getOperator();
String operator = normalizeOperator(element.getOperator());
Type inputExpressionType = context.getInputExpressionType();
Expression<Type> endpoint = element.getEndpoint();
Type endpointType = endpoint.getType();
Expand All @@ -152,20 +152,14 @@ public Object visit(OperatorRange<Type> element, DMNContext context) {
Object self = context.lookupBinding(INPUT_ENTRY_PLACE_HOLDER);
if (Type.sameSemanticDomain(endpointType, inputExpressionType)) {
// input and endpoint are comparable
Object condition;
if (operator == null) {
condition = evaluateOperatorRange(element, "=", self, endpoint, context);
} else {
condition = evaluateOperatorRange(element, operator, self, endpoint, context);
}
return condition;
return evaluateOperatorRange(element, operator, self, endpoint, context);
} else if (endpointType instanceof ListType) {
Type endpointElementType = ((ListType) endpointType).getElementType();
if (Type.sameSemanticDomain(endpointElementType, inputExpressionType)) {
// input and list elements are comparable
List endpointValueList = (List) endpoint.accept(this, context);
List results = new ArrayList();
for(Object endpointValue: endpointValueList) {
for (Object endpointValue: endpointValueList) {
results.add(evaluateOperatorRange(element, "=", self, inputExpressionType, ((ListType) endpointType).getElementType(), endpointValue));
}
return this.lib.or(results);
Expand All @@ -185,18 +179,8 @@ public Object visit(OperatorRange<Type> element, DMNContext context) {
private Object evaluateOperatorRange(Expression<Type> element, String operator, Object self, Expression<Type> endpointExpression, DMNContext context) throws Exception {
Object endpointValue = endpointExpression.accept(this, context);
if (context.isExpressionContext()) {
if (operator == null || "=".equals(operator)) {
return new Range(true, endpointValue, true, endpointValue, "=");
} else if ("!=".equals(operator)) {
return new Range(false, endpointValue, false, endpointValue, operator);
} else if ("<".equals(operator)) {
return new Range(false, null, false, endpointValue, operator);
} else if ("<=".equals(operator)) {
return new Range(false, null, true, endpointValue, operator);
} else if (">".equals(operator)) {
return new Range(false, endpointValue, false, null, operator);
} else if (">=".equals(operator)) {
return new Range(true, endpointValue, false, null, operator);
if (isValidRangeOperator(operator)) {
return new Range(operator, endpointValue);
} else {
throw new DMNRuntimeException(String.format("Unknown operator '%s'", operator));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@
import com.gs.dmn.transformation.basic.BasicDMNToNativeTransformer;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;

public abstract class AbstractFEELToJavaVisitor<R> extends AbstractAnalysisVisitor<Type, DMNContext, R> {
private static final Map<String, String> FEEL_2_JAVA_FUNCTION = new LinkedHashMap<>();
Expand Down Expand Up @@ -77,6 +75,10 @@ public abstract class AbstractFEELToJavaVisitor<R> extends AbstractAnalysisVisit
FEEL_2_JAVA_FUNCTION.put("week of year", "weekOfYear");
}

private static final Set<String> RANGE_OPERATORS = new LinkedHashSet<>(
Arrays.asList("=", "!=", "<", "<=", ">", ">=")
);

public AbstractFEELToJavaVisitor(BasicDMNToNativeTransformer<Type, DMNContext> dmnTransformer) {
super(dmnTransformer, new LogAndThrowErrorHandler(LOGGER));
}
Expand Down Expand Up @@ -116,4 +118,12 @@ protected String functionName(Object function) {
return null;
}
}

protected static String normalizeOperator(String operator) {
return StringUtils.isBlank(operator) ? "=" : operator;
}

protected static boolean isValidRangeOperator(String operator) {
return RANGE_OPERATORS.contains(operator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,28 +112,15 @@ public Triple visit(ExpressionTest<Type> element, DMNContext context) {

@Override
public Triple visit(OperatorRange<Type> element, DMNContext context) {
String operator = element.getOperator();
String operator = normalizeOperator(element.getOperator());
Expression<Type> endpoint = element.getEndpoint();
if (context.isExpressionContext()) {
// Evaluate as range
Triple endpointValue = (Triple) endpoint.accept(this, context);
Triple trueValue = this.triples.booleanValueConstant("true");
Triple falseValue = this.triples.booleanValueConstant("false");
Triple nullValue = this.triples.nullLiteral();
Triple operatorValue = this.triples.text(operator == null ? "=" : operator);
Triple operatorValue = this.triples.text(operator);
List<Triple> arguments;
if (operator == null || "=".equals(operator)) {
arguments = Arrays.asList(trueValue, endpointValue, trueValue, endpointValue, operatorValue);
} else if ("!=".equals(operator)) {
arguments = Arrays.asList(falseValue, endpointValue, falseValue, endpointValue, operatorValue);
} else if ("<".equals(operator)) {
arguments = Arrays.asList(falseValue, nullValue, falseValue, endpointValue, operatorValue);
} else if ("<=".equals(operator)) {
arguments = Arrays.asList(falseValue, nullValue, trueValue, endpointValue, operatorValue);
} else if (">".equals(operator)) {
arguments = Arrays.asList(falseValue, endpointValue, falseValue, nullValue, operatorValue);
} else if (">=".equals(operator)) {
arguments = Arrays.asList(trueValue, endpointValue, falseValue, nullValue, operatorValue);
if (isValidRangeOperator(operator)) {
arguments = Arrays.asList(operatorValue, endpointValue);
} else {
throw new DMNRuntimeException(String.format("Unknown operator '%s'", operator));
}
Expand All @@ -145,13 +132,7 @@ public Triple visit(OperatorRange<Type> element, DMNContext context) {
Type endpointType = endpoint.getType();
if (Type.sameSemanticDomain(endpointType, inputExpressionType)) {
// input and endpoint are comparable
Triple condition;
if (operator == null) {
condition = makeRangeCondition("=", (Expression) context.getInputExpression(), endpoint, context);
} else {
condition = makeRangeCondition(operator, (Expression) context.getInputExpression(), endpoint, context);
}
return condition;
return makeRangeCondition(operator, (Expression) context.getInputExpression(), endpoint, context);
} else if (endpointType instanceof ListType) {
Type endpointElementType = ((ListType) endpointType).getElementType();
if (Type.sameSemanticDomain(endpointElementType, inputExpressionType)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1148,38 +1148,38 @@ public void testRelationalExpression() {
doExpressionTest(entries, "", "(< 10) = (null..10)",
"Relational(=,OperatorRange(<,NumericLiteral(10)),EndpointsRange(true,NullLiteral(),true,NumericLiteral(10)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(false, null, false, number(\"10\"), \"<\"), new com.gs.dmn.runtime.Range(false, null, false, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(false, null, false, this.lib.number("10"), "<"), new com.gs.dmn.runtime.Range(false, null, false, this.lib.number("10"))),
"rangeEqual(new com.gs.dmn.runtime.Range(\"<\", number(\"10\")), new com.gs.dmn.runtime.Range(false, null, false, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range("<", this.lib.number("10")), new com.gs.dmn.runtime.Range(false, null, false, this.lib.number("10"))),
false);
doExpressionTest(entries, "", "(<= 10) = (null..10]",
"Relational(=,OperatorRange(<=,NumericLiteral(10)),EndpointsRange(true,NullLiteral(),false,NumericLiteral(10)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(false, null, true, number(\"10\"), \"<=\"), new com.gs.dmn.runtime.Range(false, null, true, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(false, null, true, this.lib.number("10"), "<="), new com.gs.dmn.runtime.Range(false, null, true, this.lib.number("10"))),
"rangeEqual(new com.gs.dmn.runtime.Range(\"<=\", number(\"10\")), new com.gs.dmn.runtime.Range(false, null, true, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range("<=", this.lib.number("10")), new com.gs.dmn.runtime.Range(false, null, true, this.lib.number("10"))),
false);
doExpressionTest(entries, "", "(> 10) = (10..null)",
"Relational(=,OperatorRange(>,NumericLiteral(10)),EndpointsRange(true,NumericLiteral(10),true,NullLiteral()))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(false, number(\"10\"), false, null, \">\"), new com.gs.dmn.runtime.Range(false, number(\"10\"), false, null))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(false, this.lib.number("10"), false, null, ">"), new com.gs.dmn.runtime.Range(false, this.lib.number("10"), false, null)),
"rangeEqual(new com.gs.dmn.runtime.Range(\">\", number(\"10\")), new com.gs.dmn.runtime.Range(false, number(\"10\"), false, null))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(">", this.lib.number("10")), new com.gs.dmn.runtime.Range(false, this.lib.number("10"), false, null)),
false);
doExpressionTest(entries, "", "(>= 10) = [10..null)",
"Relational(=,OperatorRange(>=,NumericLiteral(10)),EndpointsRange(false,NumericLiteral(10),true,NullLiteral()))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null, \">=\"), new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null, ">="), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null)),
"rangeEqual(new com.gs.dmn.runtime.Range(\">=\", number(\"10\")), new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(">=", this.lib.number("10")), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null)),
false);
doExpressionTest(entries, "", "(=10) = [10..10]",
"Relational(=,OperatorRange(=,NumericLiteral(10)),EndpointsRange(false,NumericLiteral(10),false,NumericLiteral(10)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(true, number(\"10\"), true, number(\"10\"), \"=\"), new com.gs.dmn.runtime.Range(true, number(\"10\"), true, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(true, this.lib.number("10"), true, this.lib.number("10"), "="), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), true, this.lib.number("10"))),
"rangeEqual(new com.gs.dmn.runtime.Range(\"=\", number(\"10\")), new com.gs.dmn.runtime.Range(true, number(\"10\"), true, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range("=", this.lib.number("10")), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), true, this.lib.number("10"))),
false);
doExpressionTest(entries, "", "(!=10) = (!=10)",
"Relational(=,OperatorRange(!=,NumericLiteral(10)),OperatorRange(!=,NumericLiteral(10)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(false, number(\"10\"), false, number(\"10\"), \"!=\"), new com.gs.dmn.runtime.Range(false, number(\"10\"), false, number(\"10\"), \"!=\"))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(false, this.lib.number("10"), false, this.lib.number("10"), "!="), new com.gs.dmn.runtime.Range(false, this.lib.number("10"), false, this.lib.number("10"), "!=")),
"rangeEqual(new com.gs.dmn.runtime.Range(\"!=\", number(\"10\")), new com.gs.dmn.runtime.Range(\"!=\", number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range("!=", this.lib.number("10")), new com.gs.dmn.runtime.Range("!=", this.lib.number("10"))),
true);
}

Expand Down Expand Up @@ -2640,7 +2640,7 @@ public void testList() {
doExpressionTest(expressionPairs, "", "[1, <2, [3..4]]",
"ListLiteral(NumericLiteral(1),OperatorRange(<,NumericLiteral(2)),EndpointsRange(false,NumericLiteral(3),false,NumericLiteral(4)))",
"ListType(Any)",
"asList(number(\"1\"), new com.gs.dmn.runtime.Range(false, null, false, number(\"2\"), \"<\"), new com.gs.dmn.runtime.Range(true, number(\"3\"), true, number(\"4\")))",
"asList(number(\"1\"), new com.gs.dmn.runtime.Range(\"<\", number(\"2\")), new com.gs.dmn.runtime.Range(true, number(\"3\"), true, number(\"4\")))",
null,
null);

Expand Down Expand Up @@ -2853,14 +2853,14 @@ public void testTypeIsMissing() {
doExpressionTest(entries, "", "(< 10) = (null_input..10)",
"Relational(=,OperatorRange(<,NumericLiteral(10)),EndpointsRange(true,Name(null_input),true,NumericLiteral(10)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(false, null, false, number(\"10\"), \"<\"), new com.gs.dmn.runtime.Range(false, null_input, false, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(false, null, false, this.lib.number("10"), "<"), new com.gs.dmn.runtime.Range(false, null_input, false, this.lib.number("10"))),
"rangeEqual(new com.gs.dmn.runtime.Range(\"<\", number(\"10\")), new com.gs.dmn.runtime.Range(false, null_input, false, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range("<", this.lib.number("10")), new com.gs.dmn.runtime.Range(false, null_input, false, this.lib.number("10"))),
false);
doExpressionTest(entries, "", "(>=; 10) = [10..null_input)",
"Relational(=,OperatorRange(>=,NumericLiteral(10)),EndpointsRange(false,NumericLiteral(10),true,Name(null_input)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null, \">=\"), new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null_input))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null, ">="), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null_input)),
"rangeEqual(new com.gs.dmn.runtime.Range(\">=\", number(\"10\")), new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null_input))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(">=", this.lib.number("10")), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null_input)),
false);
}

Expand All @@ -2883,14 +2883,14 @@ public void testTypeIsAny() {
doExpressionTest(entries, "", "(< 10) = (null_input..10)",
"Relational(=,OperatorRange(<,NumericLiteral(10)),EndpointsRange(true,Name(null_input),true,NumericLiteral(10)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(false, null, false, number(\"10\"), \"<\"), new com.gs.dmn.runtime.Range(false, null_input, false, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(false, null, false, this.lib.number("10"), "<"), new com.gs.dmn.runtime.Range(false, null_input, false, this.lib.number("10"))),
"rangeEqual(new com.gs.dmn.runtime.Range(\"<\", number(\"10\")), new com.gs.dmn.runtime.Range(false, null_input, false, number(\"10\")))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range("<", this.lib.number("10")), new com.gs.dmn.runtime.Range(false, null_input, false, this.lib.number("10"))),
false);
doExpressionTest(entries, "", "(>=; 10) = [10..null_input)",
"Relational(=,OperatorRange(>=,NumericLiteral(10)),EndpointsRange(false,NumericLiteral(10),true,Name(null_input)))",
"boolean",
"rangeEqual(new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null, \">=\"), new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null_input))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null, ">="), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null_input)),
"rangeEqual(new com.gs.dmn.runtime.Range(\">=\", number(\"10\")), new com.gs.dmn.runtime.Range(true, number(\"10\"), false, null_input))",
this.lib.rangeEqual(new com.gs.dmn.runtime.Range(">=", this.lib.number("10")), new com.gs.dmn.runtime.Range(true, this.lib.number("10"), false, null_input)),
false);
}

Expand Down
Loading

0 comments on commit 925403b

Please sign in to comment.