updates for FHIRPath test corrections

This commit is contained in:
Grahame Grieve 2019-01-23 07:18:29 +11:00
parent ebd07775a7
commit 7b8b5df67c
9 changed files with 580 additions and 215 deletions

View File

@ -24,6 +24,7 @@ package org.hl7.fhir.r4.model;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -138,6 +139,14 @@ private Map<String, Object> userData;
return null;
}
public boolean isDateTime() {
return false;
}
public Date dateTimeValue() {
return null;
}
public abstract String fhirType() ;
public boolean hasType(String... name) {

View File

@ -846,4 +846,15 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
}
}
@Override
public boolean isDateTime() {
return true;
}
@Override
public Date dateTimeValue() {
return getValue();
}
}

View File

@ -234,4 +234,9 @@ public class DateTimeType extends BaseDateTimeType {
return r.substring(0, i)+r.substring(i+1);
}
@Override
public boolean isDateTime() {
return true;
}
}

View File

@ -203,4 +203,9 @@ public class DateType extends BaseDateTimeType {
public String fhirType() {
return "date";
}
@Override
public boolean isDateTime() {
return true;
}
}

View File

@ -29,7 +29,7 @@ import org.hl7.fhir.utilities.Utilities;
public class ExpressionNode {
public enum Kind {
Name, Function, Constant, Group
Name, Function, Constant, Group, Unary
}
public static class SourceLocation {
private int line;
@ -61,7 +61,8 @@ public class ExpressionNode {
Empty, Not, Exists, SubsetOf, SupersetOf, IsDistinct, Distinct, Count, Where, Select, All, Repeat, Aggregate, Item /*implicit from name[]*/, As, Is, Single,
First, Last, Tail, Skip, Take, Union, Combine, Intersect, Exclude, Iif, Upper, Lower, ToChars, Substring, StartsWith, EndsWith, Matches, ReplaceMatches, Contains, Replace, Length,
Children, Descendants, MemberOf, Trace, Today, Now, Resolve, Extension, HasValue, AliasAs, Alias, HtmlChecks, OfType, Type,
Children, Descendants, MemberOf, Trace, Today, Now, Resolve, Extension, AllFalse, AnyFalse, AllTrue, AnyTrue,
HasValue, AliasAs, Alias, HtmlChecks, OfType, Type,
ConvertsToBoolean, ConvertsToInteger, ConvertsToString, ConvertsToDecimal, ConvertsToQuantity, ConvertsToDateTime, ConvertsToTime, ToBoolean, ToInteger, ToString, ToDecimal, ToQuantity, ToDateTime, ToTime, ConformsTo;
public static Function fromCode(String name) {
@ -111,6 +112,10 @@ public class ExpressionNode {
if (name.equals("now")) return Function.Now;
if (name.equals("resolve")) return Function.Resolve;
if (name.equals("extension")) return Function.Extension;
if (name.equals("allFalse")) return Function.AllFalse;
if (name.equals("anyFalse")) return Function.AnyFalse;
if (name.equals("allTrue")) return Function.AllTrue;
if (name.equals("anyTrue")) return Function.AnyTrue;
if (name.equals("hasValue")) return Function.HasValue;
if (name.equals("alias")) return Function.Alias;
if (name.equals("aliasAs")) return Function.AliasAs;
@ -182,6 +187,10 @@ public class ExpressionNode {
case Now : return "now";
case Resolve : return "resolve";
case Extension : return "extension";
case AllFalse : return "allFalse";
case AnyFalse : return "anyFalse";
case AllTrue : return "allTrue";
case AnyTrue : return "anyTrue";
case HasValue : return "hasValue";
case Alias : return "alias";
case AliasAs : return "aliasAs";
@ -209,7 +218,7 @@ public class ExpressionNode {
}
public enum Operation {
Equals, Equivalent, NotEquals, NotEquivalent, LessThen, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, And, Xor, Implies,
Equals, Equivalent, NotEquals, NotEquivalent, LessThan, Greater, LessOrEqual, GreaterOrEqual, Is, As, Union, Or, And, Xor, Implies,
Times, DivideBy, Plus, Minus, Concatenate, Div, Mod, In, Contains, MemberOf;
public static Operation fromCode(String name) {
@ -226,7 +235,7 @@ public class ExpressionNode {
if (name.equals(">"))
return Operation.Greater;
if (name.equals("<"))
return Operation.LessThen;
return Operation.LessThan;
if (name.equals(">="))
return Operation.GreaterOrEqual;
if (name.equals("<="))
@ -275,7 +284,7 @@ public class ExpressionNode {
case NotEquals : return "!=";
case NotEquivalent : return "!~";
case Greater : return ">";
case LessThen : return "<";
case LessThan : return "<";
case GreaterOrEqual : return ">=";
case LessOrEqual : return "<=";
case Union : return "|";
@ -579,6 +588,8 @@ public class ExpressionNode {
break;
case Unary:
break;
case Constant:
if (constant == null)
return "No Constant provided @ "+location();

View File

@ -90,7 +90,7 @@ public class FHIRLexer {
}
public boolean isStringConstant() {
return current.charAt(0) == '\'' || current.charAt(0) == '"';
return current.charAt(0) == '\'' || current.charAt(0) == '"' || current.charAt(0) == '`';
}
public String take() throws FHIRLexerException {
@ -185,9 +185,9 @@ public class FHIRLexer {
current = source.substring(currentStart, cursor);
} else if (ch == '%') {
cursor++;
if (cursor < source.length() && (source.charAt(cursor) == '"')) {
if (cursor < source.length() && (source.charAt(cursor) == '`')) {
cursor++;
while (cursor < source.length() && (source.charAt(cursor) != '"'))
while (cursor < source.length() && (source.charAt(cursor) != '`'))
cursor++;
cursor++;
} else
@ -228,6 +228,20 @@ public class FHIRLexer {
throw error("Unterminated string");
cursor++;
current = "\""+source.substring(currentStart+1, cursor-1)+"\"";
} else if (ch == '`') {
cursor++;
boolean escape = false;
while (cursor < source.length() && (escape || source.charAt(cursor) != '`')) {
if (escape)
escape = false;
else
escape = (source.charAt(cursor) == '\\');
cursor++;
}
if (cursor == source.length())
throw error("Unterminated string");
cursor++;
current = "`"+source.substring(currentStart+1, cursor-1)+"`";
} else if (ch == '\''){
cursor++;
char ech = ch;
@ -358,6 +372,9 @@ public class FHIRLexer {
case '"':
b.append('"');
break;
case '`':
b.append('`');
break;
case '\\':
b.append('\\');
break;

View File

@ -72,6 +72,7 @@ import org.hl7.fhir.utilities.Utilities;
//import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.util.ElementUtil;
import net.sf.saxon.om.Item;
/**
*
@ -655,30 +656,13 @@ public class FHIRPathEngine {
return b.toString();
}
private String convertToString(Base item) {
public String convertToString(Base item) {
if (item.isPrimitive())
return item.primitiveValue();
else if (item instanceof Quantity) {
Quantity q = (Quantity) item;
if (q.getSystem().equals("http://unitsofmeasure.org")) {
String u = "'"+q.getCode()+"'";
boolean plural = !q.getValue().toPlainString().equals("1");
if ("a".equals(q.getCode()))
u = plural ? "years" : "year";
else if ("mo".equals(q.getCode()))
u = plural ? "months" : "month";
else if ("wk".equals(q.getCode()))
u = plural ? "weeks" : "week";
else if ("d".equals(q.getCode()))
u = plural ? "days" : "day";
else if ("h".equals(q.getCode()))
u = plural ? "hours" : "hour";
else if ("min".equals(q.getCode()))
u = plural ? "minutes" : "minute";
else if ("s".equals(q.getCode()))
u = plural ? "seconds" : "seconds";
else if ("ms".equals(q.getCode()))
u = plural ? "milliseconds" : "milliseconds";
return q.getValue().toPlainString()+" "+u;
}
else
@ -795,19 +779,31 @@ public class FHIRPathEngine {
private ExpressionNode parseExpression(FHIRLexer lexer, boolean proximal) throws FHIRLexerException {
ExpressionNode result = new ExpressionNode(lexer.nextId());
ExpressionNode wrapper = null;
SourceLocation c = lexer.getCurrentStartLocation();
result.setStart(lexer.getCurrentLocation());
// special:
if (lexer.getCurrent().equals("-")) {
lexer.take();
lexer.setCurrent("-"+lexer.getCurrent());
}
if (lexer.getCurrent().equals("+")) {
lexer.take();
lexer.setCurrent("+"+lexer.getCurrent());
// special: +/- represents a unary operation at this point, but cannot be a feature of the lexer, since that's not always true.
// so we back correct for both +/- and as part of a numeric constant below.
// special: +/- represents a unary operation at this point, but cannot be a feature of the lexer, since that's not always true.
// so we back correct for both +/- and as part of a numeric constant below.
if (Utilities.existsInList(lexer.getCurrent(), "-", "+")) {
wrapper = new ExpressionNode(lexer.nextId());
wrapper.setKind(Kind.Unary);
wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.take()));
wrapper.setProximal(proximal);
}
if (lexer.isConstant()) {
boolean isString = lexer.isStringConstant();
if (!isString && (lexer.getCurrent().startsWith("-") || lexer.getCurrent().startsWith("+"))) {
// the grammar says that this is a unary operation; it affects the correct processing order of the inner operations
wrapper = new ExpressionNode(lexer.nextId());
wrapper.setKind(Kind.Unary);
wrapper.setOperation(ExpressionNode.Operation.fromCode(lexer.getCurrent().substring(0, 1)));
wrapper.setProximal(proximal);
lexer.setCurrent(lexer.getCurrent().substring(1));
}
result.setConstant(processConstant(lexer));
result.setKind(Kind.Constant);
if (!isString && !lexer.done() && (result.getConstant() instanceof IntegerType || result.getConstant() instanceof DecimalType) && (lexer.isStringConstant() || lexer.hasToken("year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds", "millisecond", "milliseconds"))) {
@ -909,6 +905,11 @@ public class FHIRPathEngine {
}
result = organisePrecedence(lexer, result);
}
if (wrapper != null) {
wrapper.setOpNext(result);
result.setProximal(false);
result = wrapper;
}
return result;
}
@ -916,7 +917,7 @@ public class FHIRPathEngine {
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Times, Operation.DivideBy, Operation.Div, Operation.Mod));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Plus, Operation.Minus, Operation.Concatenate));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Union));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.LessThen, Operation.Greater, Operation.LessOrEqual, Operation.GreaterOrEqual));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.LessThan, Operation.Greater, Operation.LessOrEqual, Operation.GreaterOrEqual));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Is));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.Equals, Operation.Equivalent, Operation.NotEquals, Operation.NotEquivalent));
node = gatherPrecedence(lexer, node, EnumSet.of(Operation.And));
@ -1093,6 +1094,10 @@ public class FHIRPathEngine {
case Now: return checkParamCount(lexer, location, exp, 0);
case Resolve: return checkParamCount(lexer, location, exp, 0);
case Extension: return checkParamCount(lexer, location, exp, 1);
case AllFalse: return checkParamCount(lexer, location, exp, 0);
case AnyFalse: return checkParamCount(lexer, location, exp, 0);
case AllTrue: return checkParamCount(lexer, location, exp, 0);
case AnyTrue: return checkParamCount(lexer, location, exp, 0);
case HasValue: return checkParamCount(lexer, location, exp, 0);
case Alias: return checkParamCount(lexer, location, exp, 1);
case AliasAs: return checkParamCount(lexer, location, exp, 1);
@ -1121,6 +1126,9 @@ public class FHIRPathEngine {
// System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString());
List<Base> work = new ArrayList<Base>();
switch (exp.getKind()) {
case Unary:
work.add(new IntegerType(0));
break;
case Name:
if (atEntry && exp.getName().equals("$this"))
work.add(context.getThisItem());
@ -1176,12 +1184,17 @@ public class FHIRPathEngine {
private List<Base> executeTypeName(ExecutionContext context, List<Base> focus, ExpressionNode next, boolean atEntry) {
List<Base> result = new ArrayList<Base>();
result.add(new StringType(next.getName()));
if (next.getInner() != null)
result.add(new StringType(next.getName()+"."+next.getInner().getName()));
else
result.add(new StringType(next.getName()));
return result;
}
private List<Base> preOperate(List<Base> left, Operation operation) {
if (left.size() == 0)
return null;
switch (operation) {
case And:
return isBoolean(left, false) ? makeBoolean(false) : null;
@ -1225,6 +1238,9 @@ public class FHIRPathEngine {
case Function:
result.update(evaluateFunctionType(context, focus, exp));
break;
case Unary:
result.addType("integer");
break;
case Constant:
result.update(resolveConstantType(context, exp.getConstant()));
break;
@ -1301,11 +1317,11 @@ public class FHIRPathEngine {
return context.context;
} else if (s.equals("%us-zip"))
return new StringType("[0-9]{5}(-[0-9]{4}){0,1}").noExtensions();
else if (s.startsWith("%\"vs-"))
else if (s.startsWith("%`vs-"))
return new StringType("http://hl7.org/fhir/ValueSet/"+s.substring(5, s.length()-1)+"").noExtensions();
else if (s.startsWith("%\"cs-"))
else if (s.startsWith("%`cs-"))
return new StringType("http://hl7.org/fhir/"+s.substring(5, s.length()-1)+"").noExtensions();
else if (s.startsWith("%\"ext-"))
else if (s.startsWith("%`ext-"))
return new StringType("http://hl7.org/fhir/StructureDefinition/"+s.substring(6, s.length()-1)).noExtensions();
else if (hostServices == null)
throw new PathEngineException("Unknown fixed constant '"+s+"'");
@ -1340,6 +1356,9 @@ public class FHIRPathEngine {
case '"':
b.append('"');
break;
case '`':
b.append('`');
break;
case '\\':
b.append('\\');
break;
@ -1371,7 +1390,7 @@ public class FHIRPathEngine {
case Equivalent: return opEquivalent(left, right);
case NotEquals: return opNotEquals(left, right);
case NotEquivalent: return opNotEquivalent(left, right);
case LessThen: return opLessThen(left, right);
case LessThan: return opLessThan(left, right);
case Greater: return opGreater(left, right);
case LessOrEqual: return opLessOrEqual(left, right);
case GreaterOrEqual: return opGreaterOrEqual(left, right);
@ -1419,7 +1438,7 @@ public class FHIRPathEngine {
if (left.get(0) instanceof org.hl7.fhir.r4.elementmodel.Element)
result.add(new BooleanType(left.get(0).hasType(tn)).noExtensions());
else if ((left.get(0) instanceof Element) && ((Element) left.get(0)).isDisallowExtensions())
result.add(new BooleanType(Utilities.capitalize(left.get(0).fhirType()).equals(tn)).noExtensions());
result.add(new BooleanType(Utilities.capitalize(left.get(0).fhirType()).equals(tn) || ("System."+Utilities.capitalize(left.get(0).fhirType())).equals(tn)).noExtensions());
else
result.add(new BooleanType(left.get(0).hasType(tn)).noExtensions());
}
@ -1433,7 +1452,7 @@ public class FHIRPathEngine {
case Equivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case NotEquals: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case NotEquivalent: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case LessThen: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case LessThan: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case Greater: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case LessOrEqual: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case GreaterOrEqual: return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
@ -1495,6 +1514,9 @@ public class FHIRPathEngine {
private List<Base> opEquals(List<Base> left, List<Base> right) {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() != right.size())
return makeBoolean(false);
@ -1509,6 +1531,9 @@ public class FHIRPathEngine {
}
private List<Base> opNotEquals(List<Base> left, List<Base> right) {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() != right.size())
return makeBoolean(true);
@ -1522,9 +1547,38 @@ public class FHIRPathEngine {
return makeBoolean(!res);
}
private String removeTrailingZeros(String s) {
if (Utilities.noString(s))
return "";
int i = s.length()-1;
boolean done = false;
boolean dot = false;
while (i > 0 && !done) {
if (s.charAt(i) == '.') {
i--;
dot = true;
}
else if (!dot && s.charAt(i) == '0')
i--;
else
done = true;
}
return s.substring(0, i+1);
}
private boolean decEqual(String left, String right) {
left = removeTrailingZeros(left);
right = removeTrailingZeros(right);
return left.equals(right);
}
private boolean doEquals(Base left, Base right) {
if (left instanceof Quantity && right instanceof Quantity)
return qtyEqual((Quantity) left, (Quantity) right);
else if (left.isDateTime() && right.isDateTime())
return left.dateTimeValue().equals(right.dateTimeValue());
else if (left instanceof DecimalType || right instanceof DecimalType)
return decEqual(left.primitiveValue(), right.primitiveValue());
else if (left.isPrimitive() && right.isPrimitive())
return Base.equals(left.primitiveValue(), right.primitiveValue());
else
@ -1543,7 +1597,7 @@ public class FHIRPathEngine {
return Utilities.equivalentNumber(left.primitiveValue(), right.primitiveValue());
if (left.hasType("date", "dateTime", "time", "instant") && right.hasType("date", "dateTime", "time", "instant"))
return compareDateTimeElements(left, right, true) == 0;
if (left.hasType("string", "id", "code", "uri") && right.hasType("string", "id", "code", "uri"))
if (left.hasType(FHIR_TYPES_STRING) && right.hasType(FHIR_TYPES_STRING))
return Utilities.equivalent(convertToString(left), convertToString(right));
throw new PathEngineException(String.format("Unable to determine equivalence between %s and %s", left.fhirType(), right.fhirType()));
@ -1600,6 +1654,8 @@ public class FHIRPathEngine {
private List<Base> opEquivalent(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() != right.size())
return makeBoolean(false);
@ -1621,6 +1677,8 @@ public class FHIRPathEngine {
}
private List<Base> opNotEquivalent(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() != right.size())
return makeBoolean(true);
@ -1641,11 +1699,16 @@ public class FHIRPathEngine {
return makeBoolean(!res);
}
private List<Base> opLessThen(List<Base> left, List<Base> right) throws FHIRException {
private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary"};
private List<Base> opLessThan(List<Base> left, List<Base> right) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) {
Base l = left.get(0);
Base r = right.get(0);
if (l.hasType("string") && r.hasType("string"))
if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) < 0);
else if ((l.hasType("integer") || l.hasType("decimal")) && (r.hasType("integer") || r.hasType("decimal")))
return makeBoolean(new Double(l.primitiveValue()) < new Double(r.primitiveValue()));
@ -1653,11 +1716,13 @@ public class FHIRPathEngine {
return makeBoolean(compareDateTimeElements(l, r, false) < 0);
else if ((l.hasType("time")) && (r.hasType("time")))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) < 0);
else
throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType());
} else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) {
List<Base> lUnit = left.get(0).listChildrenByName("code");
List<Base> rUnit = right.get(0).listChildrenByName("code");
if (Base.compareDeep(lUnit, rUnit, true)) {
return opLessThen(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"));
return opLessThan(left.get(0).listChildrenByName("value"), right.get(0).listChildrenByName("value"));
} else {
if (worker.getUcumService() == null)
return makeBoolean(false);
@ -1666,7 +1731,7 @@ public class FHIRPathEngine {
dl.add(qtyToCanonical((Quantity) left.get(0)));
List<Base> dr = new ArrayList<Base>();
dr.add(qtyToCanonical((Quantity) right.get(0)));
return opLessThen(dl, dr);
return opLessThan(dl, dr);
}
}
}
@ -1674,10 +1739,12 @@ public class FHIRPathEngine {
}
private List<Base> opGreater(List<Base> left, List<Base> right) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) {
Base l = left.get(0);
Base r = right.get(0);
if (l.hasType("string") && r.hasType("string"))
if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) > 0);
else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt")))
return makeBoolean(new Double(l.primitiveValue()) > new Double(r.primitiveValue()));
@ -1685,6 +1752,8 @@ public class FHIRPathEngine {
return makeBoolean(compareDateTimeElements(l, r, false) > 0);
else if ((l.hasType("time")) && (r.hasType("time")))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) > 0);
else
throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType());
} else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) {
List<Base> lUnit = left.get(0).listChildrenByName("unit");
List<Base> rUnit = right.get(0).listChildrenByName("unit");
@ -1706,10 +1775,12 @@ public class FHIRPathEngine {
}
private List<Base> opLessOrEqual(List<Base> left, List<Base> right) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) {
Base l = left.get(0);
Base r = right.get(0);
if (l.hasType("string") && r.hasType("string"))
if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) <= 0);
else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt")))
return makeBoolean(new Double(l.primitiveValue()) <= new Double(r.primitiveValue()));
@ -1717,6 +1788,8 @@ public class FHIRPathEngine {
return makeBoolean(compareDateTimeElements(l, r, false) <= 0);
else if ((l.hasType("time")) && (r.hasType("time")))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) <= 0);
else
throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType());
} else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) {
List<Base> lUnits = left.get(0).listChildrenByName("unit");
String lunit = lUnits.size() == 1 ? lUnits.get(0).primitiveValue() : null;
@ -1740,10 +1813,12 @@ public class FHIRPathEngine {
}
private List<Base> opGreaterOrEqual(List<Base> left, List<Base> right) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) {
Base l = left.get(0);
Base r = right.get(0);
if (l.hasType("string") && r.hasType("string"))
if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) >= 0);
else if ((l.hasType("integer", "decimal", "unsignedInt", "positiveInt")) && (r.hasType("integer", "decimal", "unsignedInt", "positiveInt")))
return makeBoolean(new Double(l.primitiveValue()) >= new Double(r.primitiveValue()));
@ -1751,6 +1826,8 @@ public class FHIRPathEngine {
return makeBoolean(compareDateTimeElements(l, r, false) >= 0);
else if ((l.hasType("time")) && (r.hasType("time")))
return makeBoolean(l.primitiveValue().compareTo(r.primitiveValue()) >= 0);
else
throw new PathEngineException("Unable to compare values of type "+l.fhirType()+" and "+r.fhirType());
} else if (left.size() == 1 && right.size() == 1 && left.get(0).fhirType().equals("Quantity") && right.get(0).fhirType().equals("Quantity") ) {
List<Base> lUnit = left.get(0).listChildrenByName("unit");
List<Base> rUnit = right.get(0).listChildrenByName("unit");
@ -1792,6 +1869,8 @@ public class FHIRPathEngine {
}
private List<Base> opIn(List<Base> left, List<Base> right) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
boolean ans = true;
for (Base l : left) {
boolean f = false;
@ -1809,6 +1888,8 @@ public class FHIRPathEngine {
}
private List<Base> opContains(List<Base> left, List<Base> right) {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
boolean ans = true;
for (Base r : right) {
boolean f = false;
@ -1826,14 +1907,12 @@ public class FHIRPathEngine {
}
private List<Base> opPlus(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0)
throw new PathEngineException("Error performing +: left operand has no value");
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing +: left operand has more than one value");
if (!left.get(0).isPrimitive())
throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() == 0)
throw new PathEngineException("Error performing +: right operand has no value");
if (right.size() > 1)
throw new PathEngineException("Error performing +: right operand has more than one value");
if (!right.get(0).isPrimitive())
@ -1842,7 +1921,7 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
Base l = left.get(0);
Base r = right.get(0);
if (l.hasType("string", "id", "code", "uri") && r.hasType("string", "id", "code", "uri"))
if (l.hasType(FHIR_TYPES_STRING) && r.hasType(FHIR_TYPES_STRING))
result.add(new StringType(l.primitiveValue() + r.primitiveValue()));
else if (l.hasType("integer") && r.hasType("integer"))
result.add(new IntegerType(Integer.parseInt(l.primitiveValue()) + Integer.parseInt(r.primitiveValue())));
@ -1854,14 +1933,12 @@ public class FHIRPathEngine {
}
private List<Base> opTimes(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0)
throw new PathEngineException("Error performing *: left operand has no value");
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing *: left operand has more than one value");
if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity))
throw new PathEngineException(String.format("Error performing +: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() == 0)
throw new PathEngineException("Error performing *: right operand has no value");
if (right.size() > 1)
throw new PathEngineException("Error performing *: right operand has more than one value");
if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity))
@ -1891,9 +1968,22 @@ public class FHIRPathEngine {
}
private List<Base> opConcatenate(List<Base> left, List<Base> right) {
private List<Base> opConcatenate(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing &: left operand has more than one value");
if (!left.get(0).hasType(FHIR_TYPES_STRING))
throw new PathEngineException(String.format("Error performing &: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() > 1)
throw new PathEngineException("Error performing &: right operand has more than one value");
if (!right.get(0).hasType(FHIR_TYPES_STRING))
throw new PathEngineException(String.format("Error performing &: right operand has the wrong type (%s)", right.get(0).fhirType()));
List<Base> result = new ArrayList<Base>();
result.add(new StringType(convertToString(left) + convertToString((right))));
Base l = left.get(0);
Base r = right.get(0);
result.add(new StringType(l.primitiveValue() + r.primitiveValue()));
return result;
}
@ -1954,7 +2044,9 @@ public class FHIRPathEngine {
}
private List<Base> opImplies(List<Base> left, List<Base> right) {
if (!convertToBoolean(left))
if (left.size() == 0)
return new ArrayList<Base>();
else if (!convertToBoolean(left))
return makeBoolean(true);
else if (right.size() == 0)
return new ArrayList<Base>();
@ -1964,14 +2056,12 @@ public class FHIRPathEngine {
private List<Base> opMinus(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0)
throw new PathEngineException("Error performing -: left operand has no value");
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing -: left operand has more than one value");
if (!left.get(0).isPrimitive())
throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() == 0)
throw new PathEngineException("Error performing -: right operand has no value");
if (right.size() > 1)
throw new PathEngineException("Error performing -: right operand has more than one value");
if (!right.get(0).isPrimitive())
@ -1991,14 +2081,12 @@ public class FHIRPathEngine {
}
private List<Base> opDivideBy(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0)
throw new PathEngineException("Error performing /: left operand has no value");
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing /: left operand has more than one value");
if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity))
throw new PathEngineException(String.format("Error performing -: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() == 0)
throw new PathEngineException("Error performing /: right operand has no value");
if (right.size() > 1)
throw new PathEngineException("Error performing /: right operand has more than one value");
if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity))
@ -2033,14 +2121,12 @@ public class FHIRPathEngine {
}
private List<Base> opDiv(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0)
throw new PathEngineException("Error performing div: left operand has no value");
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing div: left operand has more than one value");
if (!left.get(0).isPrimitive() && !(left.get(0) instanceof Quantity))
throw new PathEngineException(String.format("Error performing div: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() == 0)
throw new PathEngineException("Error performing div: right operand has no value");
if (right.size() > 1)
throw new PathEngineException("Error performing div: right operand has more than one value");
if (!right.get(0).isPrimitive() && !(right.get(0) instanceof Quantity))
@ -2068,14 +2154,12 @@ public class FHIRPathEngine {
}
private List<Base> opMod(List<Base> left, List<Base> right) throws PathEngineException {
if (left.size() == 0)
throw new PathEngineException("Error performing mod: left operand has no value");
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() > 1)
throw new PathEngineException("Error performing mod: left operand has more than one value");
if (!left.get(0).isPrimitive())
throw new PathEngineException(String.format("Error performing mod: left operand has the wrong type (%s)", left.get(0).fhirType()));
if (right.size() == 0)
throw new PathEngineException("Error performing mod: right operand has no value");
if (right.size() > 1)
throw new PathEngineException("Error performing mod: right operand has more than one value");
if (!right.get(0).isPrimitive())
@ -2140,11 +2224,11 @@ public class FHIRPathEngine {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
else if (s.equals("%us-zip"))
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
else if (s.startsWith("%\"vs-"))
else if (s.startsWith("%`vs-"))
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
else if (s.startsWith("%\"cs-"))
else if (s.startsWith("%`cs-"))
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
else if (s.startsWith("%\"ext-"))
else if (s.startsWith("%`ext-"))
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String);
else if (hostServices == null)
throw new PathEngineException("Unknown fixed constant type for '"+s+"'");
@ -2201,7 +2285,7 @@ public class FHIRPathEngine {
@SuppressWarnings("unchecked")
private TypeDetails evaluateFunctionType(ExecutionTypeContext context, TypeDetails focus, ExpressionNode exp) throws PathEngineException, DefinitionException {
List<TypeDetails> paramTypes = new ArrayList<TypeDetails>();
if (exp.getFunction() == Function.Is || exp.getFunction() == Function.As)
if (exp.getFunction() == Function.Is || exp.getFunction() == Function.As || exp.getFunction() == Function.OfType)
paramTypes.add(new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
else
for (ExpressionNode expr : exp.getParameters()) {
@ -2391,6 +2475,14 @@ public class FHIRPathEngine {
checkParamTypes(exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
return new TypeDetails(CollectionStatus.SINGLETON, "Extension");
}
case AnyTrue:
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case AllTrue:
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case AnyFalse:
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case AllFalse:
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case HasValue :
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case HtmlChecks :
@ -2567,6 +2659,10 @@ public class FHIRPathEngine {
case Now : return funcNow(context, focus, exp);
case Resolve : return funcResolve(context, focus, exp);
case Extension : return funcExtension(context, focus, exp);
case AnyFalse: return funcAnyFalse(context, focus, exp);
case AllFalse: return funcAllFalse(context, focus, exp);
case AnyTrue: return funcAnyTrue(context, focus, exp);
case AllTrue: return funcAllTrue(context, focus, exp);
case HasValue : return funcHasValue(context, focus, exp);
case AliasAs : return funcAliasAs(context, focus, exp);
case Alias : return funcAlias(context, focus, exp);
@ -2759,10 +2855,16 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
if (focus.size() == 1 && !Utilities.noString(sw))
result.add(new BooleanType(convertToString(focus.get(0)).endsWith(sw)).noExtensions());
else
if (focus.size() == 0)
result.add(new BooleanType(false).noExtensions());
else if (Utilities.noString(sw))
result.add(new BooleanType(true).noExtensions());
else {
if (focus.size() == 1 && !Utilities.noString(sw))
result.add(new BooleanType(convertToString(focus.get(0)).endsWith(sw)).noExtensions());
else
result.add(new BooleanType(false).noExtensions());
}
return result;
}
@ -2778,12 +2880,21 @@ public class FHIRPathEngine {
if (focus.size() == 1) {
if (focus.get(0) instanceof BooleanType)
result.add(focus.get(0));
else if (focus.get(0) instanceof IntegerType)
result.add(new BooleanType(!focus.get(0).primitiveValue().equals("0")).noExtensions());
else if (focus.get(0) instanceof StringType) {
if ("true".equals(focus.get(0).primitiveValue()))
else if (focus.get(0) instanceof IntegerType) {
int i = Integer.parseInt(focus.get(0).primitiveValue());
if (i == 0)
result.add(new BooleanType(false).noExtensions());
else if (i == 1)
result.add(new BooleanType(true).noExtensions());
else if ("false".equals(focus.get(0).primitiveValue()))
} else if (focus.get(0) instanceof DecimalType) {
if (((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ZERO) == 0)
result.add(new BooleanType(false).noExtensions());
else if (((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ONE) == 0)
result.add(new BooleanType(true).noExtensions());
} else if (focus.get(0) instanceof StringType) {
if ("true".equalsIgnoreCase(focus.get(0).primitiveValue()))
result.add(new BooleanType(true).noExtensions());
else if ("false".equalsIgnoreCase(focus.get(0).primitiveValue()))
result.add(new BooleanType(false).noExtensions());
}
}
@ -2900,7 +3011,7 @@ public class FHIRPathEngine {
List<Base> other = execute(context, focus, exp.getParameters().get(0), true);
for (Base item : focus) {
if (!doContains(result, item) && !doContains(other, item))
if (!doContains(other, item))
result.add(item);
}
return result;
@ -2936,6 +3047,8 @@ public class FHIRPathEngine {
n = texp.getName();
}
if (ns.equals("System")) {
if (focus.get(0) instanceof Resource)
return makeBoolean(false);
if (!(focus.get(0) instanceof Element) || ((Element) focus.get(0)).isDisallowExtensions())
return makeBoolean(n.equals(Utilities.capitalize(focus.get(0).fhirType())));
else
@ -2950,10 +3063,21 @@ public class FHIRPathEngine {
private List<Base> funcAs(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> result = new ArrayList<Base>();
String tn = exp.getParameters().get(0).getName();
for (Base b : focus)
if (b.hasType(tn))
result.add(b);
String tn;
if (exp.getParameters().get(0).getInner() != null)
tn = exp.getParameters().get(0).getName()+"."+exp.getParameters().get(0).getInner().getName();
else
tn = "FHIR."+exp.getParameters().get(0).getName();
for (Base b : focus) {
if (tn.startsWith("System.")) {
if (b instanceof Element &&((Element) b).isDisallowExtensions())
if (b.hasType(tn.substring(7)))
result.add(b);
} else if (tn.startsWith("FHIR.")) {
if (b.hasType(tn.substring(5)))
result.add(b);
}
}
return result;
}
@ -3132,6 +3256,138 @@ public class FHIRPathEngine {
return result;
}
private List<Base> funcAllFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean all = true;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
if (convertToBoolean(res)) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
} else {
boolean all = true;
for (Base item : focus) {
boolean v;
if (item instanceof BooleanType)
v = ((BooleanType) item).booleanValue();
else
v = item != null;
if (v) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
}
return result;
}
private List<Base> funcAnyFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean any = false;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
if (!convertToBoolean(res)) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
} else {
boolean any = false;
for (Base item : focus) {
boolean v;
if (item instanceof BooleanType)
v = ((BooleanType) item).booleanValue();
else
v = item != null;
if (!v) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
}
return result;
}
private List<Base> funcAllTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean all = true;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
if (!convertToBoolean(res)) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
} else {
boolean all = true;
for (Base item : focus) {
boolean v;
if (item instanceof BooleanType)
v = ((BooleanType) item).booleanValue();
else
v = item != null;
if (!v) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
}
return result;
}
private List<Base> funcAnyTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean any = false;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
if (convertToBoolean(res)) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
} else {
boolean any = false;
for (Base item : focus) {
boolean v;
if (item instanceof BooleanType)
v = ((BooleanType) item).booleanValue();
else
v = item != null;
if (v) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
}
return result;
}
private List<Base> funcTrace(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
String name = nl.get(0).primitiveValue();
@ -3181,14 +3437,17 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
if (focus.size() == 1 && !Utilities.noString(sw)) {
if (focus.size() != 1) {
result.add(new BooleanType(false).noExtensions());
} else if (Utilities.noString(sw)) {
result.add(new BooleanType(true).noExtensions());
} else {
String st = convertToString(focus.get(0));
if (Utilities.noString(st))
result.add(new BooleanType(false).noExtensions());
else
result.add(new BooleanType(st.contains(sw)).noExtensions());
} else
result.add(new BooleanType(false).noExtensions());
}
return result;
}
@ -3214,15 +3473,17 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
if (focus.size() == 1 && !Utilities.noString(sw)) {
if (focus.size() == 0) {
result.add(new BooleanType(false).noExtensions());
} else if (Utilities.noString(sw)) {
result.add(new BooleanType(true).noExtensions());
} else {
String s = convertToString(focus.get(0));
if (s == null)
result.add(new BooleanType(false).noExtensions());
else
result.add(new BooleanType(s.startsWith(sw)).noExtensions());
}
else
result.add(new BooleanType(false).noExtensions());
return result;
}
@ -3312,12 +3573,14 @@ public class FHIRPathEngine {
List<Base> result = new ArrayList<Base>();
if (focus.size() != 1)
result.add(new BooleanType(false).noExtensions());
else if (focus.get(0) instanceof IntegerType && ((IntegerType) focus.get(0)).getValue() >= 0)
result.add(new BooleanType(true).noExtensions());
else if (focus.get(0) instanceof IntegerType)
result.add(new BooleanType(((IntegerType) focus.get(0)).getValue() >= 0 && ((IntegerType) focus.get(0)).getValue() <= 1).noExtensions());
else if (focus.get(0) instanceof DecimalType)
result.add(new BooleanType(((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ZERO) == 0 || ((DecimalType) focus.get(0)).getValue().compareTo(BigDecimal.ONE) == 0).noExtensions());
else if (focus.get(0) instanceof BooleanType)
result.add(new BooleanType(true).noExtensions());
else if (focus.get(0) instanceof StringType)
result.add(new BooleanType(Utilities.existsInList(convertToString(focus.get(0)), "true", "false")).noExtensions());
result.add(new BooleanType(Utilities.existsInList(convertToString(focus.get(0)).toLowerCase(), "true", "false")).noExtensions());
else
result.add(new BooleanType(false).noExtensions());
return result;
@ -3385,6 +3648,8 @@ public class FHIRPathEngine {
result.add(new BooleanType(true).noExtensions());
else if (focus.get(0) instanceof Quantity)
result.add(new BooleanType(true).noExtensions());
else if (focus.get(0) instanceof BooleanType)
result.add(new BooleanType(true).noExtensions());
else if (focus.get(0) instanceof StringType) {
Quantity q = parseQuantityString(focus.get(0).primitiveValue());
result.add(new BooleanType(q != null).noExtensions());

View File

@ -149,7 +149,7 @@ public class FHIRPathTests {
fp.setHostServices(new FHIRPathTestEvaluationServices());
String input = test.getAttribute("inputfile");
String expression = XMLUtil.getNamedChild(test, "expression").getTextContent();
boolean fail = "true".equals(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"));
boolean fail = Utilities.existsInList(XMLUtil.getNamedChild(test, "expression").getAttribute("invalid"), "true", "semantic");
Resource res = null;
List<Base> outcome = new ArrayList<Base>();
@ -182,20 +182,38 @@ public class FHIRPathTests {
List<Element> expected = new ArrayList<Element>();
XMLUtil.getNamedChildren(test, "output", expected);
Assert.assertTrue(String.format("Expected %d objects but found %d", expected.size(), outcome.size()), outcome.size() == expected.size());
for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
String tn = expected.get(i).getAttribute("type");
if (!Utilities.noString(tn)) {
Assert.assertTrue(String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType()), tn.equals(outcome.get(i).fhirType()));
Assert.assertTrue(String.format("Expected %d objects but found %d for expression %s", expected.size(), outcome.size(), expression), outcome.size() == expected.size());
if ("false".equals(test.getAttribute("ordered"))) {
for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
String tn = outcome.get(i).fhirType();
String s;
if (outcome.get(i) instanceof Quantity)
s = fp.convertToString(outcome.get(i));
else
s = ((PrimitiveType) outcome.get(i)).asStringValue();
boolean found = false;
for (Element e : expected) {
if ((Utilities.noString(e.getAttribute("type")) || e.getAttribute("type").equals(tn)) &&
(Utilities.noString(e.getTextContent()) || e.getTextContent().equals(s)))
found = true;
}
Assert.assertTrue(String.format("Outcome %d: Value %s of type %s not expected for %s", i, s, tn, expression), found);
}
String v = expected.get(i).getTextContent();
if (!Utilities.noString(v)) {
if (outcome.get(i) instanceof Quantity) {
Quantity q = fp.parseQuantityString(v);
Assert.assertTrue(String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString()), outcome.get(i).equalsDeep(q));
} else {
Assert.assertTrue(String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType()), outcome.get(i) instanceof PrimitiveType);
Assert.assertTrue(String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString()), v.equals(((PrimitiveType)outcome.get(i)).asStringValue()));
} else {
for (int i = 0; i < Math.min(outcome.size(), expected.size()); i++) {
String tn = expected.get(i).getAttribute("type");
if (!Utilities.noString(tn)) {
Assert.assertTrue(String.format("Outcome %d: Type should be %s but was %s", i, tn, outcome.get(i).fhirType()), tn.equals(outcome.get(i).fhirType()));
}
String v = expected.get(i).getTextContent();
if (!Utilities.noString(v)) {
if (outcome.get(i) instanceof Quantity) {
Quantity q = fp.parseQuantityString(v);
Assert.assertTrue(String.format("Outcome %d: Value should be %s but was %s", i, v, outcome.get(i).toString()), outcome.get(i).equalsDeep(q));
} else {
Assert.assertTrue(String.format("Outcome %d: Value should be a primitive type but was %s", i, outcome.get(i).fhirType()), outcome.get(i) instanceof PrimitiveType);
Assert.assertTrue(String.format("Outcome %d: Value should be %s but was %s for expression %s", i, v, outcome.get(i).toString(), expression), v.equals(((PrimitiveType)outcome.get(i)).asStringValue()));
}
}
}
}

View File

@ -46,10 +46,18 @@
<output type="string">Peter</output>
<output type="string">James</output>
</test>
<test name="testSimpleBackTick1" inputfile="patient-example.xml">
<expression>`Patient`.name.`given`</expression>
<output type="string">Peter</output>
<output type="string">James</output>
<output type="string">Jim</output>
<output type="string">Peter</output>
<output type="string">James</output>
</test>
<!-- testWrong(patient(), "name.given1"); -->
<test name="testSimpleFail" inputfile="patient-example.xml">
<expression invalid="true">name.given1</expression>
<test name="testSimpleFail" inputfile="patient-example.xml" mode="strict">
<expression invalid="semantic">name.given1</expression>
</test>
<!-- test(patient(), "Patient.name.given", 3, "string"); -->
@ -63,8 +71,8 @@
</test>
<!-- testWrong(patient(), "Encounter.name.given"); -->
<test name="testSimpleWithWrongContext" inputfile="patient-example.xml">
<expression invalid="true">Encounter.name.given</expression>
<test name="testSimpleWithWrongContext" inputfile="patient-example.xml" mode="strict">
<expression invalid="semantic">Encounter.name.given</expression>
</test>
</group>
@ -72,12 +80,12 @@
<!-- test(observation(), "Observation.value.unit", 1, "string"); -->
<test name="testPolymorphismA" inputfile="observation-example.xml">
<expression>Observation.value.unit</expression>
<output type="string"></output>
<output type="string">lbs</output>
</test>
<!-- testWrong(observation(), "Observation.valueQuantity.unit"); -->
<test name="testPolymorphismB" inputfile="observation-example.xml">
<expression invalid="true">Observation.valueQuantity.unit</expression>
<test name="testPolymorphismB" inputfile="observation-example.xml" mode="strict">
<expression invalid="semantic">Observation.valueQuantity.unit</expression>
</test>
<!-- testBoolean(observation(), "Observation.value.is(Quantity)", true); -->
@ -109,8 +117,8 @@
</test>
<!-- testWrong(observation(), "(Observation.value as Period).unit"); -->
<test name="testPolymorphismAsB" inputfile="observation-example.xml">
<expression invalid="true">(Observation.value as Period).unit</expression>
<test name="testPolymorphismAsB" inputfile="observation-example.xml" mode="strict">
<expression invalid="semantic">(Observation.value as Period).unit</expression>
</test>
<!-- test(observation(), "Observation.value.as(Period).start", 0); -->
@ -146,8 +154,8 @@
</test>
<!-- testWrong(patient(), "Patient.children().skip(1)"); -->
<test name="testDollarOrderNotAllowed" inputfile="patient-example.xml">
<expression invalid="true">Patient.children().skip(1)</expression>
<test name="testDollarOrderNotAllowed" inputfile="patient-example.xml" mode="strict">
<expression invalid="semantic">Patient.children().skip(1)</expression>
</test>
</group>
@ -172,12 +180,12 @@
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>1.convertsToInteger()</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>0.convertsToInteger()</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>-1.convertsToInteger()</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>-1.convertsToInteger()</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>(-1).convertsToInteger()</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression invalid="true">-1.convertsToInteger()</expression></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>2147483647.convertsToInteger()</expression><output type="boolean">true</output></test>
<test name="testLiteralString" inputfile="patient-example.xml"><expression>'test'.convertsToString()</expression><output type="boolean">true</output></test>
<test name="testLiteralString" inputfile="patient-example.xml"><expression>'\\\/\f\r\n\t\"\"\'\u002a'.convertsToString()</expression><output type="boolean">true</output></test>
<test name="testLiteralString" inputfile="patient-example.xml"><expression>'\\\/\f\r\n\t\"\`\'\u002a'.convertsToString()</expression><output type="boolean">true</output></test>
<test name="testLiteralBoolean" inputfile="patient-example.xml"><expression>true.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="testLiteralBoolean" inputfile="patient-example.xml"><expression>false.convertsToBoolean()</expression><output type="boolean">true</output></test>
@ -185,7 +193,8 @@
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>1.0.convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>0.1.convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>0.0.convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>-0.1.convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>(-0.1).convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression invalid="true">-0.1.convertsToDecimal()</expression></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>1234567890987654321.0.convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="patient-example.xml"><expression>0.000000000000000000000000000001.convertsToDecimal()</expression><output type="boolean">true</output></test>
@ -206,6 +215,7 @@
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>-3 != 3</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>Patient.name.given.count() = 5</expression><output type="boolean">true</output></test>
<test name="testPolarityPrecedence" inputfile="patient-example.xml"><expression>-Patient.name.given.count() = -5</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>Patient.name.given.count() &gt; -3</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>Patient.name.given.count() != 0</expression><output type="boolean">true</output></test>
<test name="testLiteralInteger" inputfile="patient-example.xml"><expression>1 &lt; 2</expression><output type="boolean">true</output></test>
@ -217,7 +227,7 @@
<test name="testLiteralDecimal" inputfile="observation-example.xml"><expression>Observation.value.value &gt; 0.0</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="observation-example.xml"><expression>Observation.value.value &gt; 0</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="observation-example.xml"><expression>Observation.value.value &lt; 190</expression><output type="boolean">true</output></test>
<test name="testLiteralDecimal" inputfile="observation-example.xml"><expression>Observation.value.value &lt; 'test'</expression><!-- no output - empty set --></test>
<test name="testLiteralDecimal" inputfile="observation-example.xml"><expression invalid="semantic">Observation.value.value &lt; 'test'</expression><!-- no output - empty set --></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate = @1974-12-25</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00</expression><output type="boolean">true</output></test>
@ -232,18 +242,19 @@
<test name="testLiteralDateTZ" inputfile="patient-example.xml"><expression>@2017-11-05T01:30:00.0-04:00 > @2017-11-05T01:15:00.0-05:00</expression><output type="boolean">false</output></test>
<test name="testLiteralDateTZ" inputfile="patient-example.xml"><expression>@2017-11-05T01:30:00.0-04:00 &lt; @2017-11-05T01:15:00.0-05:00</expression><output type="boolean">true</output></test>
<test name="testLiteralDateTZ" inputfile="patient-example.xml"><expression>@2017-11-05T01:30:00.0-04:00 = @2017-11-05T01:15:00.0-05:00</expression><output type="boolean">false</output></test>
<test name="testLiteralDateTZ" inputfile="patient-example.xml"><expression>@2017-11-05T01:30:00.0-04:00 = @2017-11-05T00:30:00.0-05:00</expression><output type="boolean">false</output></test>
<test name="testLiteralDateTZ" inputfile="patient-example.xml"><expression>@2017-11-05T01:30:00.0-04:00 = @2017-11-05T00:30:00.0-05:00</expression><output type="boolean">true</output></test>
<test name="testLiteralUnicode" inputfile="patient-example.xml"><expression>Patient.name.given.first() = 'P\u0065ter'</expression><output type="boolean">true</output></test>
<test name="testLiteralEmptyCollection" inputfile="patient-example.xml"><expression>Patient.name.given != {}</expression><output type="boolean">true</output></test>
<test name="testLiteralEmptyCollection" inputfile="patient-example.xml"><expression>Patient.name.given.empty().not()</expression><output type="boolean">true</output></test>
<test name="testLiteralEmptyCollection" inputfile="patient-example.xml"><expression>Patient.name.given != {}</expression></test>
<test name="testExpressions" inputfile="patient-example.xml"><expression>Patient.name.select(given | family).distinct()</expression>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<test name="testExpressions" inputfile="patient-example.xml" ordered="false"><expression>Patient.name.select(given | family).distinct()</expression>
<output type="string">Peter</output>
<output type="string">James</output>
<output type="string">Chalmers</output>
<output type="string">Jim</output>
<output type="string">Windsor</output>
</test>
<test name="testExpressions" inputfile="patient-example.xml"><expression>Patient.name.given.count() = 1 + 4</expression><output type="boolean">true</output></test>
@ -271,7 +282,8 @@
<test name="test" inputfile="patient-example.xml"><expression>1.toInteger() = 1</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'1'.toInteger() = 1</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'1.1'.toInteger() = {}</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'1.1'.toInteger() = {}</expression></test>
<test name="test" inputfile="patient-example.xml"><expression>'1.1'.toInteger().empty()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>true.toInteger() = 1</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.convertsToDecimal()</expression><output type="boolean">true</output></test>
@ -286,7 +298,7 @@
<test name="test" inputfile="patient-example.xml"><expression>true.convertsToDecimal()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>true.is(Decimal).not()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.toDecimal() != 1.0</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.toDecimal() = 1.0</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.toDecimal() ~ 1.0</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.0.toDecimal() = 1.0</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'1.1'.toDecimal() = 1.1</expression><output type="boolean">true</output></test>
@ -304,7 +316,7 @@
<test name="test" inputfile="patient-example.xml"><expression>'1.a'.convertsToQuantity().not()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'1.0'.convertsToQuantity()</expression><output type="boolean">true</output></test>
<!-- <test name="test" inputfile="patient-example.xml"><expression>'1.0'.is(System.Quantity).not()</expression><output type="boolean">true</output></test> -->
<test name="test" inputfile="patient-example.xml"><expression>true.convertsToQuantity()</expression><output type="boolean">false</output></test>
<test name="test" inputfile="patient-example.xml"><expression>true.convertsToQuantity()</expression><output type="boolean">true</output></test>
<!-- <test name="test" inputfile="patient-example.xml"><expression>true.is(System.Quantity).not()</expression><output type="boolean">true</output></test> -->
<test name="test" inputfile="patient-example.xml"><expression>1.toQuantity() = 1 '1'</expression><output type="boolean">true</output></test>
@ -316,41 +328,45 @@
<test name="test" inputfile="patient-example.xml"><expression>'1.0'.toQuantity() ~ 1 '1'</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>2.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>-1.convertsToBoolean().not()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>2.convertsToBoolean()</expression><output type="boolean">false</output></test>
<test name="test" inputfile="patient-example.xml"><expression>(-1).convertsToBoolean()</expression><output type="boolean">false</output></test>
<test name="test" inputfile="patient-example.xml"><expression>0.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.0.convertsToBoolean().not()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.0.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'true'.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'false'.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'False'.convertsToBoolean().not()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'False'.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>true.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>false.convertsToBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.toBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>2.toBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>2.toBoolean()</expression><!-- empty --></test>
<test name="test" inputfile="patient-example.xml"><expression>0.toBoolean()</expression><output type="boolean">false</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'true'.toBoolean()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'false'.toBoolean()</expression><output type="boolean">false</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.is(String).not()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>-1.convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>(-1).convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.0.convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'true'.convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>true.convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1 'wk'.convertsToString()</expression><output type="boolean">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.toString()</expression><output type="string">1</output></test>
<test name="test" inputfile="patient-example.xml"><expression>-1.toString()</expression><output type="string">-1</output></test>
<test name="test" inputfile="patient-example.xml"><expression>(-1).toString()</expression><output type="string">-1</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1.0.toString()</expression><output type="string">1.0</output></test>
<test name="test" inputfile="patient-example.xml"><expression>'true'.toString()</expression><output type="string">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>true.toString()</expression><output type="string">true</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1 'wk'.toString()</expression><output type="string">1 week</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1 'wk'.toString()</expression><output type="string">1 'wk'</output></test>
<test name="test" inputfile="patient-example.xml"><expression>1 week.toString()</expression><output type="string">1 'wk'</output></test>
</group>
<group name="testAll">
<test inputfile="patient-example.xml"><expression>Patient.name.select(given.exists()).all()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.name.select(family.exists()).all()</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>Patient.name.select(given.exists()).allTrue()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.name.select(period.exists()).allTrue()</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>Patient.name.all(given.exists())</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.name.all(period.exists())</expression><output type="boolean">false</output></test>
</group>
<group name="testSubSetOf">
@ -382,6 +398,8 @@
<test inputfile="patient-example.xml"><expression>iif({}, true, false)</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>iif(true, true, false)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>iif({} | true, true, false)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>iif(true, true, 1/0)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>iif(false, 1/0, true)</expression><output type="boolean">true</output></test>
</group>
@ -398,23 +416,13 @@
</test>
<test inputfile="questionnaire-example.xml">
<expression>Questionnaire.descendants().linkId.distinct()</expression>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<output type="string"></output>
<expression>Questionnaire.descendants().linkId.distinct().count()</expression>
<output type="integer">10</output>
</test>
<test inputfile="questionnaire-example.xml">
<expression>Questionnaire.descendants().linkId.select(substring(0,1)).distinct()</expression>
<output type="string"></output>
<output type="string"></output>
<expression>Questionnaire.descendants().linkId.select(substring(0,1)).distinct().count()</expression>
<output type="integer">2</output>
</test>
</group>
@ -458,7 +466,7 @@
<group name="testSingle">
<test inputfile="patient-example.xml"><expression>Patient.name.first().single().exists()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression invalid="true">Patient.name.single().exists()</expression></test>
<test inputfile="patient-example.xml"><expression invalid="semantic">Patient.name.single().exists()</expression></test>
</group>
<group name="testFirstLast">
@ -543,7 +551,7 @@
<test inputfile="patient-example.xml"><expression>'12345'.startsWith('13') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.startsWith('12345') = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.startsWith('123456') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.startsWith('') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.startsWith('') = true</expression><output type="boolean">true</output></test>
</group>
<group name="testEndsWith">
@ -553,7 +561,7 @@
<test inputfile="patient-example.xml"><expression>'12345'.endsWith('35') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.endsWith('12345') = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.endsWith('012345') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.endsWith('') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.endsWith('') = true</expression><output type="boolean">true</output></test>
</group>
<group name="testContainsString">
@ -563,7 +571,7 @@
<test inputfile="patient-example.xml"><expression>'12345'.contains('35') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.contains('12345') = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.contains('012345') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.contains('') = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'12345'.contains('') = true</expression><output type="boolean">true</output></test>
</group>
<group name="testLength">
@ -591,46 +599,49 @@
<group name="testEquality">
<test inputfile="patient-example.xml"><expression>1 = 1</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>{} = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>{} = {}</expression></test>
<test inputfile="patient-example.xml"><expression>true = {}</expression></test>
<test inputfile="patient-example.xml"><expression>1 = 2</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>'a' = 'a'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'a' = 'A'</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>'a' = 'b'</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>1.1 = 1.1</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.1 = 1.2</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>1.10 = 1.1</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>1.10 = 1.1</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>0 = 0</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>0.0 = 0</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>0.0 = 0</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 = @2012-04-15</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 = @2012-04-16</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 = @2012-04-15T10:00:00</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>name = name</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2) = name.take(2).first() | name.take(2).last()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2) = name.take(2).last() | name.take(2).first()</expression><output type="boolean">false</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value = 185 '[lb_av]'</expression><output type="boolean">true</output></test>
</group>
<group name="testNEquality">
<test inputfile="patient-example.xml"><expression>1 != 1</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>{} != {}</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>{} != {}</expression></test>
<test inputfile="patient-example.xml"><expression>1 != 2</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'a' != 'a'</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>'a' != 'b'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.1 != 1.1</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>1.1 != 1.2</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.10 != 1.1</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.10 != 1.1</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>0 != 0</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>0.0 != 0</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>0.0 != 0</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 != @2012-04-15</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 != @2012-04-16</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 != @2012-04-15T10:00:00</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>name != name</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2) != name.take(2).first() | name.take(2).last()</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2) != name.take(2).last() | name.take(2).first()</expression><output type="boolean">true</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value != 185 'kg'</expression><output type="boolean">true</output></test>
</group>
<group name="testEquivalent">
<test inputfile="patient-example.xml"><expression>1 ~ 1</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>{} ~ {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>{} ~ {}</expression></test>
<test inputfile="patient-example.xml"><expression>1 ~ 2</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>'a' ~ 'a'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'a' ~ 'A'</expression><output type="boolean">true</output></test>
@ -646,11 +657,12 @@
<!-- <test inputfile="patient-example.xml"><expression>name ~ name</expression><output type="boolean">true</output></test> -->
<test inputfile="patient-example.xml"><expression>name.take(2).given ~ name.take(2).first().given | name.take(2).last().given</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2).given ~ name.take(2).last().given | name.take(2).first().given</expression><output type="boolean">true</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value ~ 185 '[lb_av]'</expression><output type="boolean">true</output></test>
</group>
<group name="testNotEquivalent">
<test inputfile="patient-example.xml"><expression>1 !~ 1</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>{} !~ {}</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>{} !~ {}</expression></test>
<test inputfile="patient-example.xml"><expression>1 !~ 2</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'a' !~ 'a'</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>'a' !~ 'A'</expression><output type="boolean">false</output></test>
@ -666,6 +678,8 @@
<!-- <test inputfile="patient-example.xml"><expression>name !~ name</expression><output type="boolean">true</output></test> -->
<test inputfile="patient-example.xml"><expression>name.take(2).given !~ name.take(2).first().given | name.take(2).last().given</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2).given !~ name.take(2).last().given | name.take(2).first().given</expression><output type="boolean">false</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value !~ 185 'kg'</expression><output type="boolean">true</output></test>
</group>
<group name="testLessThan">
@ -692,6 +706,8 @@
<test inputfile="patient-example.xml"><expression>@2014-12-13 &lt; @2014-12-12</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2014-12-13T12:00:01 &lt; @2014-12-13T12:00:00</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@T12:00:01 &lt; @T12:00:00</expression><output type="boolean">false</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value &lt; 200 '[lb_av]'</expression><output type="boolean">true</output></test>
</group>
<group name="testLessOrEqual">
@ -718,6 +734,7 @@
<test inputfile="patient-example.xml"><expression>@2014-12-13 &lt;= @2014-12-12</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2014-12-13T12:00:01 &lt;= @2014-12-13T12:00:00</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@T12:00:01 &lt;= @T12:00:00</expression><output type="boolean">false</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value &lt;= 200 '[lb_av]'</expression><output type="boolean">true</output></test>
</group>
<group name="testGreatorOrEqual">
@ -744,9 +761,11 @@
<test inputfile="patient-example.xml"><expression>@2014-12-13 >= @2014-12-12</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2014-12-13T12:00:01 >= @2014-12-13T12:00:00</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@T12:00:01 >= @T12:00:00</expression><output type="boolean">true</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value &gt;= 100 '[lb_av]'</expression><output type="boolean">true</output></test>
</group>
<group name="testGreatorThan">
<group name="testGreaterThan">
<test inputfile="patient-example.xml"><expression>1 > 2</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>1.0 > 1.2</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>'a' > 'b'</expression><output type="boolean">false</output></test>
@ -770,6 +789,7 @@
<test inputfile="patient-example.xml"><expression>@2014-12-13 > @2014-12-12</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2014-12-13T12:00:01 > @2014-12-13T12:00:00</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@T12:00:01 > @T12:00:00</expression><output type="boolean">true</output></test>
<test inputfile="observation-example.xml"><expression>Observation.value &gt; 100 '[lb_av]'</expression><output type="boolean">true</output></test>
</group>
<group name="testUnion">
@ -785,8 +805,8 @@
<group name="testIntersect">
<test inputfile="patient-example.xml"><expression>(1 | 2 | 3).intersect(2 | 4) = 2</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2).intersect(4) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2).intersect({}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2).intersect(4).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2).intersect({}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.combine(1).intersect(1).count() = 1</expression><output type="boolean">true</output></test> <!-- do not merge duplicates -->
</group>
@ -794,7 +814,7 @@
<test inputfile="patient-example.xml"><expression>(1 | 2 | 3).exclude(2 | 4) = 1 | 3</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2).exclude(4) = 1 | 2</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2).exclude({}) = 1 | 2</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.combine(1).exclude(2).count() = 1</expression><output type="boolean">true</output></test> <!-- do not merge duplicates -->
<test inputfile="patient-example.xml"><expression>1.combine(1).exclude(2).count() = 2</expression><output type="boolean">true</output></test> <!-- do not merge duplicates -->
</group>
<group name="testIn">
@ -814,15 +834,15 @@
<group name="testBooleanLogicAnd">
<test inputfile="patient-example.xml"><expression>(true and true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true and false) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true and {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true and {}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false and true) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false and false) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false and {}) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} and true) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} and true).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} and false) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} and {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} and {}).empty()</expression><output type="boolean">true</output></test>
</group>
<group name="testBooleanLogicOr">
@ -832,39 +852,39 @@
<test inputfile="patient-example.xml"><expression>(false or true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false or false) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false or {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false or {}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} or true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} or false) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} or {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} or false).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} or {}).empty()</expression><output type="boolean">true</output></test>
</group>
<group name="testBooleanLogicXOr">
<test inputfile="patient-example.xml"><expression>(true xor true) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true xor false) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true xor {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true xor {}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false xor true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false xor false) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false xor {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false xor {}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} xor true) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} xor false) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} xor {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} xor true).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} xor false).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} xor {}).empty()</expression><output type="boolean">true</output></test>
</group>
<group name="testBooleanImplies">
<test inputfile="patient-example.xml"><expression>(true implies true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true implies false) = false</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true implies {}) = {}</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(true implies {}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false implies true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false implies false) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(false implies {}) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} implies true) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} implies false) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} implies {}) = true</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} implies true).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} implies false).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} implies {}).empty()</expression><output type="boolean">true</output></test>
</group>
<group name="testPlus">
@ -875,18 +895,17 @@
</group>
<group name="testConcatenate">
<test inputfile="patient-example.xml"><expression>1 &amp; 1 = '11'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1 &amp; 'a' = '1a'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>{} &amp; 'b' = 'b'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>(1 | 2 | 3) &amp; 'b' = '1,2,3b'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'a'&amp;'b' = 'ab'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>'a' &amp; 'b' = 'ab'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>('1' &amp; {}).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>({} &amp; 'b').empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression invalid="semantic">(1 | 2 | 3) &amp; 'b' = '1,2,3b'</expression></test>
</group>
<group name="testMinus">
<test inputfile="patient-example.xml"><expression>1 - 1 = 0</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1 - 0 = 1</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1.8 - 1.2 = 0.6</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression invalid="true">'a'-'b' = 'ab'</expression></test>
<test inputfile="patient-example.xml"><expression invalid="semantic">'a'-'b' = 'ab'</expression></test>
</group>
<group name="testMultiply">
@ -919,6 +938,11 @@
</group>
<group name="testPrecedence">
<test name="testUnaryPrecedence" inputfile="patient-example.xml">
<expression invalid="semantic">-1.convertsToInteger()</expression>
<!-- should error because unary does not work on boolean: -(1.convertsToInteger()) -->
</test>
<test inputfile="patient-example.xml"><expression>1+2*3+4 = 11</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1 > 2 is Boolean</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>1 | 1 is Integer</expression><output type="boolean">true</output></test>
@ -928,12 +952,12 @@
<test inputfile="patient-example.xml"><expression>%sct = 'http://snomed.info/sct'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>%loinc = 'http://loinc.org'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>%ucum = 'http://unitsofmeasure.org'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>%"vs-administrative-gender" = 'http://hl7.org/fhir/ValueSet/administrative-gender'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>%`vs-administrative-gender` = 'http://hl7.org/fhir/ValueSet/administrative-gender'</expression><output type="boolean">true</output></test>
</group>
<group name="testExtension">
<test inputfile="patient-example.xml"><expression>Patient.birthDate.extension('http://hl7.org/fhir/StructureDefinition/patient-birthTime').exists()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.birthDate.extension(%"ext-patient-birthTime").exists()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.birthDate.extension(%`ext-patient-birthTime`).exists()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.birthDate.extension('http://hl7.org/fhir/StructureDefinition/patient-birthTime1').empty()</expression><output type="boolean">true</output></test>
</group>
@ -944,6 +968,8 @@
<test inputfile="patient-example.xml"><expression>true.type().name = 'Boolean'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>true.is(Boolean)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>true.is(System.Boolean)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>true is Boolean</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>true is System.Boolean</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.active.type().namespace = 'FHIR'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.active.type().name = 'boolean'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.active.is(boolean)</expression><output type="boolean">true</output></test>
@ -954,6 +980,11 @@
<test inputfile="patient-example.xml"><expression>Patient.type().name = 'Patient'</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.is(Patient)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.is(FHIR.Patient)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.is(FHIR.`Patient`)</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.ofType(Patient).type().name</expression><output type="string">Patient</output></test>
<test inputfile="patient-example.xml"><expression>Patient.ofType(FHIR.Patient).type().name</expression><output type="string">Patient</output></test>
<test inputfile="patient-example.xml"><expression>Patient.is(System.Patient).not()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>Patient.ofType(FHIR.`Patient`).type().name</expression><output type="string">Patient</output></test>
</group>
<group name="testConformsTo">
@ -962,12 +993,5 @@
<test inputfile="patient-example.xml"><expression invalid="true">conformsTo('http://trash')</expression></test>
</group>
<!--
<group name="testDollarResource">
testBoolean(patient(), patient().getManagingOrganization(), "Reference", "reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))</expression><output type="boolean">true</output></test>
testBoolean(patient(), patient(), "Patient", "contained.select(('#'+id in %resource.descendants().reference).not()).empty()</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression invalid="true">contained.select(('#'+id in %resource.descendants().reference).not()).empty()");
</group>
-->
</tests>