FHIRPath fixes (string handling)

This commit is contained in:
Grahame Grieve 2022-07-27 22:38:32 +10:00
parent 816f1832d5
commit 7e8cace0fb
1 changed files with 71 additions and 41 deletions

View File

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