Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Null handling by ProperContains, ProperIncludedIn, ProperIncludes, ProperIn evaluators. #1370

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,8 @@ public static Object[][] dataMethod() {
"cql/CqlListOperatorsTest/NotEqual/NotEqual123AndABC",
"cql/CqlListOperatorsTest/NotEqual/NotEqual123AndString123",
"cql/CqlListOperatorsTest/NotEqual/NotEqualABCAnd123",
"cql/CqlListOperatorsTest/ProperContains/ProperContainsNullRightFalse",
"cql/CqlListOperatorsTest/ProperContains/ProperContainsTimeNull",
"cql/CqlListOperatorsTest/ProperIn/ProperInTimeNull",
"cql/CqlListOperatorsTest/ProperlyIncludedIn/ProperlyIncludedInNulRight",
"cql/CqlListOperatorsTest/ProperlyIncludes/ProperlyIncludesNullLeft",
"cql/CqlListOperatorsTest/Union/UnionListNullAndListNull",
"cql/CqlStringOperatorsTest/toString tests/DateTimeToString3",
"cql/CqlTypeOperatorsTest/As/AsQuantity",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@
<expression>minimum Time</expression>
<output>@T00:00:00.000</output>
</test>
<test name="BooleanMinValue">
<expression invalid="true">minimum Boolean</expression>
<!-- EXPECT: The Minimum operator is not implemented for type Boolean -->
</test>
</group>
<group name="MaxValue">
<test name="IntegerMaxValue">
Expand Down Expand Up @@ -362,6 +366,10 @@
<expression>maximum Time</expression>
<output>@T23:59:59.999</output>
</test>
<test name="BooleanMaxValue">
<expression invalid="true">maximum Boolean</expression>
<!-- EXPECT: The Maximum operator is not implemented for type Boolean -->
</test>
</group>
<group name="Modulo">
<test name="ModuloNull">
Expand Down Expand Up @@ -777,6 +785,10 @@
<expression>2 div 1</expression>
<output>2</output>
</test>
<test name="TruncatedDivide2By0">
<expression>2 div 0</expression>
<output>null</output>
</test>
<test name="TruncatedDivide10By3">
<expression>10 div 3</expression>
<output>3</output>
Expand All @@ -785,10 +797,18 @@
<expression>10L div 3L</expression>
<output>3L</output>
</test>
<test name="TruncatedDivide10LBy0L">
<expression>10L div 0L</expression>
<output>null</output>
</test>
<test name="TruncatedDivide10d1By3D1">
<expression>10.1 div 3.1</expression>
<output>3.0</output>
</test>
<test name="TruncatedDivide10D1By0D">
<expression>10.1 div 0.0</expression>
<output>null</output>
</test>
<test name="TruncatedDivideNeg2ByNeg1">
<expression>-2 div -1</expression>
<output>2</output>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -650,13 +650,17 @@
</test>
</group>
<group name="ProperContains">
<test name="ProperContainsNullRightFalse">
<test name="ProperContainsNullRight1">
<expression>{'s', 'u', 'n'} properly includes null</expression>
<output>false</output>
<output>null</output>
</test>
<test name="ProperContainsNullRightTrue">
<test name="ProperContainsNullRight2">
<expression>{'s', 'u', 'n', null} properly includes null</expression>
<output>true</output>
<output>null</output>
</test>
<test name="ProperContainsNullLeft">
<expression>null as List&lt;String&gt; properly includes 's'</expression>
<output>null</output>
</test>
<test name="ProperContainsTimeTrue">
<expression>{ @T15:59:59, @T20:59:59.999, @T20:59:49.999 } properly includes @T15:59:59</expression>
Expand All @@ -668,13 +672,17 @@
</test>
</group>
<group name="ProperIn">
<test name="ProperInNullRightFalse">
<test name="ProperInNullLeft1">
<expression>null properly included in {'s', 'u', 'n'}</expression>
<output>false</output>
<output>null</output>
</test>
<test name="ProperInNullRightTrue">
<test name="ProperInNullLeft2">
<expression>null properly included in {'s', 'u', 'n', null}</expression>
<output>true</output>
<output>null</output>
</test>
<test name="ProperInNullRight">
<expression>'s' properly included in null as List&lt;String&gt;</expression>
<output>null</output>
</test>
<test name="ProperInTimeTrue">
<expression>@T15:59:59 properly included in { @T15:59:59, @T20:59:59.999, @T20:59:49.999 }</expression>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public static Boolean properContains(Object left, Object right, State state) {
}

public static Boolean properContains(Object left, Object right, String precision, State state) {
if (left == null || right == null) {
return null;
}

if (left instanceof Interval && right instanceof BaseTemporal) {
Boolean startProperContains = AfterEvaluator.after(right, ((Interval) left).getStart(), precision, state);
Boolean endProperContains = BeforeEvaluator.before(right, ((Interval) left).getEnd(), precision, state);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public static Boolean properIn(Object left, Object right, String precision, Stat
}

public static Object internalEvaluate(Object left, Object right, String precision, State state) {
if (left == null || right == null) {
return null;
}

if (precision != null) {
return properIn(left, right, precision, state);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,14 @@ properly included in(left List<T>, right list<T>) Boolean
The properly included in operator for lists returns true if every element of the first list is in the second list and the
first list is strictly smaller than the second list.
This operator uses the notion of equivalence to determine whether or not two elements are the same.
If the left argument is null, the result is true if the right argument is not empty. Otherwise, if the right argument is null, the result is false.
If either argument is null, the result is null.
Note that the order of elements does not matter for the purposes of determining inclusion.
*/

public class ProperIncludedInEvaluator {

public static Object properlyIncludedIn(Object left, Object right, String precision, State state) {
if (left == null && right == null) {
return null;
}

try {
if (left == null) {
return right instanceof Interval
? ProperIncludesEvaluator.intervalProperlyIncludes((Interval) right, null, precision, state)
: ProperIncludesEvaluator.listProperlyIncludes((Iterable<?>) right, null, state);
}

if (right == null) {
return left instanceof Interval
? ProperIncludesEvaluator.intervalProperlyIncludes(null, (Interval) left, precision, state)
: ProperIncludesEvaluator.listProperlyIncludes(null, (Iterable<?>) left, state);
}

return ProperIncludesEvaluator.properlyIncludes(right, left, precision, state);
} catch (InvalidOperatorArgument e) {
throw new InvalidOperatorArgument(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,21 @@ properly includes(left List<T>, right List<T>) Boolean
The properly includes operator for lists returns true if the first list contains every element of the second list, a
nd the first list is strictly larger than the second list.
This operator uses the notion of equivalence to determine whether or not two elements are the same.
If the left argument is null, the result is false, else if the right argument is null, the result is true if the left argument is not empty.
If either argument is null, the result is null.
Note that the order of elements does not matter for the purposes of determining inclusion.
*/

public class ProperIncludesEvaluator {

public static Boolean properlyIncludes(Object left, Object right, String precision, State state) {
if (left == null && right == null) {
if (left == null || right == null) {
return null;
}

if (left == null) {
return right instanceof Interval
? intervalProperlyIncludes(null, (Interval) right, precision, state)
: listProperlyIncludes(null, (Iterable<?>) right, state);
}

if (right == null) {
return left instanceof Interval
? intervalProperlyIncludes((Interval) left, null, precision, state)
: listProperlyIncludes((Iterable<?>) left, null, state);
}

if (left instanceof Interval && right instanceof Interval) {
if (left instanceof Interval) {
return intervalProperlyIncludes((Interval) left, (Interval) right, precision, state);
}
if (left instanceof Iterable && right instanceof Iterable) {
if (left instanceof Iterable) {
return listProperlyIncludes((Iterable<?>) left, (Iterable<?>) right, state);
}

Expand All @@ -62,10 +50,6 @@ public static Boolean properlyIncludes(Object left, Object right, String precisi
}

public static Boolean intervalProperlyIncludes(Interval left, Interval right, String precision, State state) {
if (left == null || right == null) {
return null;
}

Object leftStart = left.getStart();
Object leftEnd = left.getEnd();
Object rightStart = right.getStart();
Expand All @@ -88,17 +72,9 @@ public static Boolean intervalProperlyIncludes(Interval left, Interval right, St
}

public static Boolean listProperlyIncludes(Iterable<?> left, Iterable<?> right, State state) {
if (left == null) {
return false;
}

int leftCount = (int)
StreamSupport.stream(((Iterable<?>) left).spliterator(), false).count();

if (right == null) {
return leftCount > 0;
}

return AndEvaluator.and(
IncludedInEvaluator.listIncludedIn(right, left, state),
GreaterEvaluator.greater(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.junit.jupiter.api.Test;
import org.opencds.cqf.cql.engine.elm.executing.*;
import org.opencds.cqf.cql.engine.exception.CqlException;
import org.opencds.cqf.cql.engine.exception.InvalidOperatorArgument;
import org.opencds.cqf.cql.engine.exception.UndefinedResult;
import org.opencds.cqf.cql.engine.runtime.*;

Expand Down Expand Up @@ -374,6 +375,13 @@ void maximum() {

value = engine.expression(library, "TimeMaxValue").value();
assertTrue(EquivalentEvaluator.equivalent(value, new Time(23, 59, 59, 999)));

try {
value = engine.expression(library, "BooleanMaxValue").value();
fail();
} catch (InvalidOperatorArgument ae) {
assertThat(ae.getMessage(), is("The Maximum operator is not implemented for type Boolean"));
}
}

/**
Expand All @@ -396,6 +404,13 @@ void minimum() {

value = engine.expression(library, "TimeMinValue").value();
assertTrue(EquivalentEvaluator.equivalent(value, new Time(0, 0, 0, 0)));

try {
value = engine.expression(library, "BooleanMinValue").value();
fail();
} catch (InvalidOperatorArgument ae) {
assertThat(ae.getMessage(), is("The Minimum operator is not implemented for type Boolean"));
}
}

/**
Expand Down Expand Up @@ -796,12 +811,24 @@ void truncatedDivide() {
value = engine.expression(library, "TruncatedDivide2By1").value();
assertThat(value, is(2));

value = engine.expression(library, "TruncatedDivide2By0").value();
assertThat(value, is(nullValue()));

value = engine.expression(library, "TruncatedDivide10By3").value();
assertThat(value, is(3));

value = engine.expression(library, "TruncatedDivide10LBy3L").value();
assertThat(value, is(3L));

value = engine.expression(library, "TruncatedDivide10LBy0L").value();
assertThat(value, is(nullValue()));

value = engine.expression(library, "TruncatedDivide10d1By3D1").value();
assertThat((BigDecimal) value, comparesEqualTo(BigDecimal.valueOf(3.0)));

value = engine.expression(library, "TruncatedDivide10D1By0D").value();
assertThat(value, is(nullValue()));

value = engine.expression(library, "TruncatedDivideNeg2ByNeg1").value();
assertThat(value, is(2));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,28 +569,34 @@ void all_interval_operators() {
assertThat(value, is(false));

value = results.forExpression("ProperlyIncludesNullLeft").value();
assertThat(value, is(false));
assertThat(value, is(nullValue()));

value = results.forExpression("ProperlyIncludes1And111").value();
assertThat(value, is(false));

value = results.forExpression("ProperContainsNullRightFalse").value();
assertThat(value, is(false));
value = results.forExpression("ProperContainsNullRight1").value();
assertThat(value, is(nullValue()));

value = results.forExpression("ProperContainsNullRightTrue").value();
assertThat(value, is(true));
value = results.forExpression("ProperContainsNullRight2").value();
assertThat(value, is(nullValue()));

value = results.forExpression("ProperContainsNullLeft").value();
assertThat(value, is(nullValue()));

value = results.forExpression("ProperContainsTimeTrue").value();
assertThat(value, is(true));

value = results.forExpression("ProperContainsTimeNull").value();
assertThat(value, is(false));

value = results.forExpression("ProperInNullRightFalse").value();
assertThat(value, is(false));
value = results.forExpression("ProperInNullLeft1").value();
assertThat(value, is(nullValue()));

value = results.forExpression("ProperInNullRightTrue").value();
assertThat(value, is(true));
value = results.forExpression("ProperInNullLeft2").value();
assertThat(value, is(nullValue()));

value = results.forExpression("ProperInNullRight").value();
assertThat(value, is(nullValue()));

value = results.forExpression("ProperInTimeTrue").value();
assertThat(value, is(true));
Expand Down Expand Up @@ -626,7 +632,7 @@ void all_interval_operators() {
assertThat(value, is(false));

value = results.forExpression("ProperlyIncludedInNullRight").value();
assertThat(value, is(false));
assertThat(value, is(nullValue()));

value = results.forExpression("ProperlyIncludedIn11And1").value();
assertThat(value, is(false));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ define DecimalMinValue: minimum Decimal
define QuantityMinValue: minimum Quantity
define DateTimeMinValue: minimum DateTime
define TimeMinValue: minimum Time
define BooleanMinValue: minimum Boolean

//MaxValue
define IntegerMaxValue: maximum Integer
Expand All @@ -113,6 +114,7 @@ define DecimalMaxValue: maximum Decimal
define QuantityMaxValue: maximum Quantity
define DateTimeMaxValue: maximum DateTime
define TimeMaxValue: maximum Time
define BooleanMaxValue: maximum Boolean

//Modulo
define ModuloNull : 1 mod null
Expand Down Expand Up @@ -236,8 +238,12 @@ define TruncateNeg1D9: Truncate(-1.9)
//Truncated Divide
define TruncatedDivideNull: (null as Integer) div (null as Integer)
define TruncatedDivide2By1: 2 div 1
define TruncatedDivide2By0: 2 div 0
define TruncatedDivide10By3: 10 div 3
define TruncatedDivide10LBy3L: 10L div 3L
define TruncatedDivide10LBy0L: 10L div 0L
define TruncatedDivide10d1By3D1: 10.1 div 3.1
define TruncatedDivide10D1By0D: 10.1 div 0.0
define TruncatedDivideNeg2ByNeg1: -2 div -1
define TruncatedDivideNeg10ByNeg3: -10 div -3
define TruncatedDivideNeg10d1ByNeg3D1: -10.1 div -3.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,16 @@ define ProperlyIncludesNullLeft: null properly includes {2}
define ProperlyIncludes1And111: {1} properly includes {1, 1}

//ProperContains
define ProperContainsNullRightFalse: {'s', 'u', 'n'} properly includes null
define ProperContainsNullRightTrue: {'s', 'u', 'n', null} properly includes null
define ProperContainsNullRight1: {'s', 'u', 'n'} properly includes null
define ProperContainsNullRight2: {'s', 'u', 'n', null} properly includes null
define ProperContainsNullLeft: null as List<String> properly includes 's'
define ProperContainsTimeTrue: { @T15:59:59, @T20:59:59.999, @T20:59:49.999 } properly includes @T15:59:59
define ProperContainsTimeNull: { @T15:59:59.999, @T20:59:59.999, @T20:59:49.999 } properly includes @T15:59:59

//ProperIn
define ProperInNullRightFalse: null properly included in {'s', 'u', 'n'}
define ProperInNullRightTrue: null properly included in {'s', 'u', 'n', null}
define ProperInNullLeft1: null properly included in {'s', 'u', 'n'}
define ProperInNullLeft2: null properly included in {'s', 'u', 'n', null}
define ProperInNullRight: 's' properly included in null as List<String>
define ProperInTimeTrue: @T15:59:59 properly included in { @T15:59:59, @T20:59:59.999, @T20:59:49.999 }
define ProperInTimeNull: @T15:59:59 properly included in { @T15:59:59.999, @T20:59:59.999, @T20:59:49.999 }

Expand Down
Loading
Loading