diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index e35d08127..939d93026 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -2903,6 +2903,8 @@ public class FHIRPathEngine { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Quantity); } else if (constant instanceof FHIRConstant) { return resolveConstantType(context, ((FHIRConstant) constant).getValue(), expr); + } else if (constant == null) { + return new TypeDetails(CollectionStatus.SINGLETON); } else { return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } @@ -3140,60 +3142,60 @@ public class FHIRPathEngine { return types; } case Lower : { - checkContextString(focus, "lower", exp); + checkContextString(focus, "lower", exp, true); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Upper : { - checkContextString(focus, "upper", exp); + checkContextString(focus, "upper", exp, true); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case ToChars : { - checkContextString(focus, "toChars", exp); + checkContextString(focus, "toChars", exp, true); return new TypeDetails(CollectionStatus.ORDERED, TypeDetails.FP_String); } case IndexOf : { - checkContextString(focus, "indexOf", exp); + checkContextString(focus, "indexOf", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer); } case Substring : { - checkContextString(focus, "subString", exp); + checkContextString(focus, "subString", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Integer)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case StartsWith : { - checkContextString(focus, "startsWith", exp); + checkContextString(focus, "startsWith", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case EndsWith : { - checkContextString(focus, "endsWith", exp); + checkContextString(focus, "endsWith", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Matches : { - checkContextString(focus, "matches", exp); + checkContextString(focus, "matches", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case MatchesFull : { - checkContextString(focus, "matches", exp); + checkContextString(focus, "matches", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case ReplaceMatches : { - checkContextString(focus, "replaceMatches", exp); + checkContextString(focus, "replaceMatches", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Contains : { - checkContextString(focus, "contains", exp); + checkContextString(focus, "contains", exp, true); checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean); } case Replace : { - checkContextString(focus, "replace", exp); - checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, "string"), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); + checkContextString(focus, "replace", exp, true); + checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String), new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String); } case Length : { @@ -3427,37 +3429,39 @@ public class FHIRPathEngine { } - private void checkContextString(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { - if (!focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) { - throw makeException(expr, I18nConstants.FHIRPATH_STRING_ONLY, name, focus.describe()); + private void checkContextString(TypeDetails focus, String name, ExpressionNode expr, boolean sing) throws PathEngineException { + if (!focus.hasNoTypes() && !focus.hasType(worker, "string") && !focus.hasType(worker, "code") && !focus.hasType(worker, "uri") && !focus.hasType(worker, "canonical") && !focus.hasType(worker, "id")) { + throw makeException(expr, sing ? I18nConstants.FHIRPATH_STRING_SING_ONLY : I18nConstants.FHIRPATH_STRING_ORD_ONLY, name, focus.describe()); } } private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { - if (canQty) { - if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) { - throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); + if (!focus.hasNoTypes()) { + if (canQty) { + if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) { + throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); + } + } else if (!focus.hasType(primitiveTypes)) { + throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); } - } else if (!focus.hasType(primitiveTypes)) { - throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); } } private void checkContextNumerical(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { - if (!focus.hasType("integer") && !focus.hasType("decimal") && !focus.hasType("Quantity")) { + if (!focus.hasNoTypes() && !focus.hasType("integer") && !focus.hasType("decimal") && !focus.hasType("Quantity")) { throw makeException(expr, I18nConstants.FHIRPATH_NUMERICAL_ONLY, name, focus.describe()); } } private void checkContextDecimal(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { - if (!focus.hasType("decimal") && !focus.hasType("integer")) { + if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("integer")) { throw makeException(expr, I18nConstants.FHIRPATH_DECIMAL_ONLY, name, focus.describe()); } } private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException { - if (!focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time")) { + if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time")) { throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe()); } } @@ -3672,7 +3676,10 @@ public class FHIRPathEngine { private List funcExp(ExecutionContext context, List focus, ExpressionNode expr) { - if (focus.size() != 1) { + if (focus.size() == 0) { + return new ArrayList(); + } + if (focus.size() > 1) { throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "exp", focus.size()); } Base base = focus.get(0); @@ -4218,15 +4225,19 @@ public class FHIRPathEngine { private List funcReplace(ExecutionContext context, List focus, ExpressionNode expr) throws FHIRException, PathEngineException { List result = new ArrayList(); + List tB = execute(context, focus, expr.getParameters().get(0), true); + String t = convertToString(tB); + List rB = execute(context, focus, expr.getParameters().get(1), true); + String r = convertToString(rB); - if (focus.size() == 1) { + if (focus.size() == 0 || tB.size() == 0 || rB.size() == 0) { + // + } else if (focus.size() == 1) { if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { String f = convertToString(focus.get(0)); if (Utilities.noString(f)) { result.add(new StringType("")); } else { - String t = convertToString(execute(context, focus, expr.getParameters().get(0), true)); - String r = convertToString(execute(context, focus, expr.getParameters().get(1), true)); String n = f.replace(t, r); result.add(new StringType(n)); } @@ -4240,10 +4251,14 @@ public class FHIRPathEngine { private List funcReplaceMatches(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); - String regex = convertToString(execute(context, focus, exp.getParameters().get(0), true)); - String repl = convertToString(execute(context, focus, exp.getParameters().get(1), true)); + List regexB = execute(context, focus, exp.getParameters().get(0), true); + String regex = convertToString(regexB); + List replB = execute(context, focus, exp.getParameters().get(1), true); + String repl = convertToString(replB); - if (focus.size() == 1 && !Utilities.noString(regex)) { + if (focus.size() == 0 || regexB.size() == 0 || replB.size() == 0) { + // + } else if (focus.size() == 1 && !Utilities.noString(regex)) { if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { result.add(new StringType(convertToString(focus.get(0)).replaceAll(regex, repl)).noExtensions()); } @@ -4256,10 +4271,13 @@ public class FHIRPathEngine { private List funcEndsWith(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); - String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); + List swb = execute(context, focus, exp.getParameters().get(0), true); + String sw = convertToString(swb); if (focus.size() == 0) { - result.add(new BooleanType(false).noExtensions()); + // + } else if (swb.size() == 0) { + // } else if (Utilities.noString(sw)) { result.add(new BooleanType(true).noExtensions()); } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { @@ -4929,9 +4947,12 @@ public class FHIRPathEngine { private List funcMatches(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); - String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); + List swb = execute(context, focus, exp.getParameters().get(0), true); + String sw = convertToString(swb); - if (focus.size() == 1 && !Utilities.noString(sw)) { + if (focus.size() == 0 || swb.size() == 0) { + // + } else if (focus.size() == 1 && !Utilities.noString(sw)) { if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { String st = convertToString(focus.get(0)); if (Utilities.noString(st)) { @@ -4973,10 +4994,13 @@ public class FHIRPathEngine { private List funcContains(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); - String sw = convertToString(execute(context, baseToList(context.thisItem), exp.getParameters().get(0), true)); + List swb = execute(context, baseToList(context.thisItem), exp.getParameters().get(0), true); + String sw = convertToString(swb); if (focus.size() != 1) { - result.add(new BooleanType(false).noExtensions()); + // + } else if (swb.size() != 1) { + // } else if (Utilities.noString(sw)) { result.add(new BooleanType(true).noExtensions()); } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { @@ -5018,10 +5042,13 @@ public class FHIRPathEngine { private List funcStartsWith(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); - String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); + List swb = execute(context, focus, exp.getParameters().get(0), true); + String sw = convertToString(swb); if (focus.size() == 0) { - result.add(new BooleanType(false).noExtensions()); + // no result + } else if (swb.size() == 0) { + // no result } else if (Utilities.noString(sw)) { result.add(new BooleanType(true).noExtensions()); } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) { @@ -5071,9 +5098,12 @@ public class FHIRPathEngine { private List funcIndexOf(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List result = new ArrayList(); - String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); + List swb = execute(context, focus, exp.getParameters().get(0), true); + String sw = convertToString(swb); if (focus.size() == 0) { - result.add(new IntegerType(0).noExtensions()); + // no result + } else if (swb.size() == 0) { + // no result } else if (Utilities.noString(sw)) { result.add(new IntegerType(0).noExtensions()); } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) {