diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java
index 8c18746c5..4a0b26d88 100644
--- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java
+++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/PhinVadsImporter.java
@@ -1,6 +1,7 @@
package org.hl7.fhir.convertors.misc;
import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.ValueSet;
@@ -36,7 +37,7 @@ public class PhinVadsImporter extends OIDBasedValueSetImporter {
System.out.println("Process " + f.getName());
ValueSet vs = importValueSet(TextFile.fileToBytes(f));
if (vs.getId() != null) {
- new JsonParser().compose(new FileOutputStream(Utilities.path(dest, "ValueSet-" + vs.getId() + ".json")), vs);
+ new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "ValueSet-" + vs.getId() + ".json")), vs);
}
} catch (Exception e) {
e.printStackTrace();
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java
index 84a3857dc..b315dc804 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java
@@ -261,6 +261,7 @@ public class FHIRPathEngine {
private ProfileUtilities profileUtilities;
private String location; // for error messages
private boolean allowPolymorphicNames;
+ private boolean doImplicitStringConversion;
// if the fhir path expressions are allowed to use constants beyond those defined in the specification
// the application can implement them by providing a constant resolver
@@ -446,6 +447,14 @@ public class FHIRPathEngine {
}
+ public boolean isDoImplicitStringConversion() {
+ return doImplicitStringConversion;
+ }
+
+ public void setDoImplicitStringConversion(boolean doImplicitStringConversion) {
+ this.doImplicitStringConversion = doImplicitStringConversion;
+ }
+
// --- public API -------------------------------------------------------
/**
* Parse a path for later use using execute
@@ -2893,6 +2902,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);
}
@@ -3130,60 +3141,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 : {
@@ -3417,14 +3428,15 @@ 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 (!focus.hasNoTypes()) {
if (canQty) {
if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) {
throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString());
@@ -3433,21 +3445,22 @@ public class FHIRPathEngine {
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());
}
}
@@ -3662,7 +3675,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);
@@ -4208,17 +4224,23 @@ 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));
}
+ }
} else {
throw makeException(expr, I18nConstants.FHIRPATH_NO_COLLECTION, "replace", focus.size());
}
@@ -4228,11 +4250,17 @@ 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());
+ }
} else {
result.add(new StringType(convertToString(focus.get(0))).noExtensions());
}
@@ -4242,13 +4270,16 @@ 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 {
+ } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) {
if (focus.size() == 1 && !Utilities.noString(sw)) {
result.add(new BooleanType(convertToString(focus.get(0)).endsWith(sw)).noExtensions());
} else {
@@ -4915,9 +4946,13 @@ 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)) {
result.add(new BooleanType(false).noExtensions());
@@ -4927,6 +4962,7 @@ public class FHIRPathEngine {
boolean ok = m.find();
result.add(new BooleanType(ok).noExtensions());
}
+ }
} else {
result.add(new BooleanType(false).noExtensions());
}
@@ -4938,6 +4974,7 @@ public class FHIRPathEngine {
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
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)) {
result.add(new BooleanType(false).noExtensions());
@@ -4947,6 +4984,7 @@ public class FHIRPathEngine {
boolean ok = m.matches();
result.add(new BooleanType(ok).noExtensions());
}
+ }
} else {
result.add(new BooleanType(false).noExtensions());
}
@@ -4955,13 +4993,16 @@ 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 {
+ } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) {
String st = convertToString(focus.get(0));
if (Utilities.noString(st)) {
result.add(new BooleanType(false).noExtensions());
@@ -4980,7 +5021,7 @@ public class FHIRPathEngine {
private List funcLength(ExecutionContext context, List focus, ExpressionNode exp) {
List result = new ArrayList();
- if (focus.size() == 1) {
+ if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) {
String s = convertToString(focus.get(0));
result.add(new IntegerType(s.length()).noExtensions());
}
@@ -5000,13 +5041,16 @@ 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 {
+ } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) {
String s = convertToString(focus.get(0));
if (s == null) {
result.add(new BooleanType(false).noExtensions());
@@ -5019,7 +5063,7 @@ public class FHIRPathEngine {
private List funcLower(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException {
List result = new ArrayList();
- if (focus.size() == 1) {
+ if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) {
String s = convertToString(focus.get(0));
if (!Utilities.noString(s)) {
result.add(new StringType(s.toLowerCase()).noExtensions());
@@ -5030,7 +5074,7 @@ public class FHIRPathEngine {
private List funcUpper(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException {
List result = new ArrayList();
- if (focus.size() == 1) {
+ if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) {
String s = convertToString(focus.get(0));
if (!Utilities.noString(s)) {
result.add(new StringType(s.toUpperCase()).noExtensions());
@@ -5041,7 +5085,7 @@ public class FHIRPathEngine {
private List funcToChars(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException {
List result = new ArrayList();
- if (focus.size() == 1) {
+ if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) {
String s = convertToString(focus.get(0));
for (char c : s.toCharArray()) {
result.add(new StringType(String.valueOf(c)).noExtensions());
@@ -5053,12 +5097,15 @@ 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 {
+ } else if (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion) {
String s = convertToString(focus.get(0));
if (s == null) {
result.add(new IntegerType(0).noExtensions());
@@ -5076,10 +5123,13 @@ public class FHIRPathEngine {
int i2 = -1;
if (exp.parameterCount() == 2) {
List n2 = execute(context, focus, exp.getParameters().get(1), true);
+ if (n2.isEmpty()|| !n2.get(0).isPrimitive() || !Utilities.isInteger(n2.get(0).primitiveValue())) {
+ return new ArrayList();
+ }
i2 = Integer.parseInt(n2.get(0).primitiveValue());
}
- if (focus.size() == 1) {
+ if (focus.size() == 1 && (focus.get(0).hasType(FHIR_TYPES_STRING) || doImplicitStringConversion)) {
String sw = convertToString(focus.get(0));
String s;
if (i1 < 0 || i1 >= sw.length()) {
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) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java
index 588a540f0..203612486 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java
@@ -170,6 +170,6 @@ public class TypesUtilities {
}
public static boolean isPrimitive(String code) {
- return Utilities.existsInList(code, "boolean", "integer", "integer64", "string", "decimal", "uri", "url", "canonical", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "markdown", "unsignedInt", "positiveInt", "xhtml");
+ return Utilities.existsInList(code, "boolean", "integer", "integer64", "string", "decimal", "uri", "url", "canonical", "base64Binary", "instant", "date", "dateTime", "time", "code", "oid", "id", "uuid", "markdown", "unsignedInt", "positiveInt", "xhtml");
}
}
\ No newline at end of file
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
index e47a74817..a181728fd 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
@@ -173,7 +173,8 @@ public class I18nConstants {
public static final String FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET = "FHIRPATH_RESOLVE_DISCRIMINATOR_NO_TARGET";
public static final String FHIRPATH_RIGHT_VALUE_PLURAL = "FHIRPATH_RIGHT_VALUE_PLURAL";
public static final String FHIRPATH_RIGHT_VALUE_WRONG_TYPE = "FHIRPATH_RIGHT_VALUE_WRONG_TYPE";
- public static final String FHIRPATH_STRING_ONLY = "FHIRPATH_STRING_ONLY";
+ public static final String FHIRPATH_STRING_ORD_ONLY = "FHIRPATH_STRING_ORD_ONLY";
+ public static final String FHIRPATH_STRING_SING_ONLY = "FHIRPATH_STRING_SING_ONLY";
public static final String FHIRPATH_UNABLE_BOOLEAN = "FHIRPATH_UNABLE_BOOLEAN";
public static final String FHIRPATH_UNKNOWN_CONSTANT = "FHIRPATH_UNKNOWN_CONSTANT";
public static final String FHIRPATH_UNKNOWN_CONTEXT = "FHIRPATH_UNKNOWN_CONTEXT";
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java
index 90121542b..ed4b5298a 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java
@@ -244,7 +244,7 @@ public class PackageClient {
public void findDependents(Set list, String id) {
CommaSeparatedStringBuilder params = new CommaSeparatedStringBuilder("&");
- params.append("dependency="+id);
+ params.append("dependency="+id.replace("#", "|"));
try {
JsonArray json = fetchJsonArray(Utilities.pathURL(address, "catalog?")+params.toString());
for (JsonElement e : json) {
diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties
index 947160694..9f9dda45e 100644
--- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties
+++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties
@@ -557,7 +557,8 @@ FHIRPATH_WRONG_PARAM_TYPE = Error evaluating FHIRPath expression: The parameter
FHIRPATH_ORDERED_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered collections
FHIRPATH_REFERENCE_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, uri, canonical or Reference but found {1}
FHIRPATH_CODED_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, code, uri, Coding, CodeableConcept but found {1}
-FHIRPATH_STRING_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered string, uri, code, id but found {1}
+FHIRPATH_STRING_ORD_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on ordered collection of string, uri, code, id but found {1}
+FHIRPATH_STRING_SING_ONLY = Error evaluating FHIRPath expression: The function {0} can only be used on string, uri, code, id but found {1}
FHIRPATH_NO_COLLECTION = Error evaluating FHIRPath expression: The function {0} can only be used on a singleton value but found {1}
FHIRPATH_NOT_IMPLEMENTED = Error evaluating FHIRPath expression: The function {0} is not implemented
FHIRPATH_PARAM_WRONG = Error evaluating FHIRPath expression: The expression type {0} is not supported for parameter {1} on function {2}
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
index 14ab315a1..4188ebdd7 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
@@ -171,6 +171,7 @@ import org.hl7.fhir.validation.instance.type.StructureDefinitionValidator;
import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.validation.instance.utils.ChildIterator;
import org.hl7.fhir.validation.instance.utils.ElementInfo;
+import org.hl7.fhir.validation.instance.utils.FHIRPathExpressionFixer;
import org.hl7.fhir.validation.instance.utils.IndexedElement;
import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ResolvedReference;
@@ -210,8 +211,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
public CanonicalResourceLookupResult(String error) {
this.error = error;
}
-
}
+
private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list";
private static final String EXECUTION_ID = "validator.execution.id";
private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)";
@@ -3461,7 +3462,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
TypedElementDefinition ted = null;
- String fp = fixExpr(discriminator, null);
+ String fp = FHIRPathExpressionFixer.fixExpr(discriminator, null);
ExpressionNode expr = null;
try {
expr = fpe.parse(fp);
@@ -4068,7 +4069,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
try {
- n = fpe.parse(fixExpr(expression.toString(), null));
+ n = fpe.parse(FHIRPathExpressionFixer.fixExpr(expression.toString(), null));
} catch (FHIRLexerException e) {
if (STACK_TRACE) e.printStackTrace();
throw new FHIRException(context.formatMessage(I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, expression, profile.getUrl(), path, e.getMessage()));
@@ -5590,7 +5591,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
List invErrors = null;
// We key based on inv.expression rather than inv.key because expressions can change in derived profiles and aren't guaranteed to be consistent across profiles.
- String key = fixExpr(inv.getExpression(), inv.getKey());
+ String key = FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey());
if (!invMap.keySet().contains(key)) {
invErrors = new ArrayList();
invMap.put(key, invErrors);
@@ -5643,7 +5644,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (n == null) {
long t = System.nanoTime();
try {
- n = fpe.parse(fixExpr(inv.getExpression(), inv.getKey()));
+ n = fpe.parse(FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey()));
} catch (FHIRLexerException e) {
rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, false, I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getUrl(), path, e.getMessage());
return;
@@ -5665,25 +5666,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ex.printStackTrace();
}
if (!ok) {
- if (!Utilities.noString(msg)) {
- msg = "'" + inv.getHuman()+"' (" + msg + ")";
- } else if (wantInvariantInMessage) {
- msg = "'" + inv.getHuman()+"' [" + n.toString() + "]";
- } else {
- msg = context.formatMessage(I18nConstants.INV_FAILED, "'" + inv.getHuman()+"'");
+ if (wantInvariantInMessage) {
+ msg = msg + " (inv = " + n.toString() + ")";
}
+ if (!Utilities.noString(msg)) {
+ msg = msg + " (log: " + msg + ")";
+ }
+ msg = context.formatMessage(I18nConstants.INV_FAILED, inv.getKey() + ": '" + inv.getHuman()+"'")+msg;
+
if (inv.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice") &&
ToolingExtensions.readBooleanExtension(inv, "http://hl7.org/fhir/StructureDefinition/elementdefinition-bestpractice")) {
if (bpWarnings == BestPracticeWarningLevel.Hint)
- hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": " + msg);
+ hint(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, msg);
else if (bpWarnings == BestPracticeWarningLevel.Warning)
- warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg);
+ warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, msg);
else if (bpWarnings == BestPracticeWarningLevel.Error)
- rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg);
+ rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, msg);
} else if (inv.getSeverity() == ConstraintSeverity.ERROR) {
- rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg);
+ rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, msg);
} else if (inv.getSeverity() == ConstraintSeverity.WARNING) {
- warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, inv.getKey() + ": '" + inv.getHuman()+"' " + msg);
+ warning(errors, IssueType.INVARIANT, element.line(), element.col(), path, ok, msg);
}
}
}
@@ -5838,7 +5840,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
try {
ExpressionNode n = (ExpressionNode) inv.getUserData("validator.expression.cache");
if (n == null) {
- n = fpe.parse(fixExpr(inv.getExpression(), inv.getKey()));
+ n = fpe.parse(FHIRPathExpressionFixer.fixExpr(inv.getExpression(), inv.getKey()));
inv.setUserData("validator.expression.cache", n);
}
fpe.check(null, sd.getKind() == StructureDefinitionKind.RESOURCE ? sd.getType() : "DomainResource", ed.getPath(), n);
@@ -5852,93 +5854,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
- private String fixExpr(String expr, String key) {
- // this is a hack work around for past publication of wrong FHIRPath expressions
- // R4
- // waiting for 4.0.2
- //TODO is this expression below correct? @grahamegrieve
- if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
- return "probability.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))";
- }
- if ("enableWhen.count() > 2 implies enableBehavior.exists()".equals(expr)) {
- return "enableWhen.count() >= 2 implies enableBehavior.exists()";
- }
- if ("txt-2".equals(key)) {
- return "htmlChecks2()";
- }
-
- // clarification in FHIRPath spec
- if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) {
- return "name.matches('^[A-Z]([A-Za-z0-9_]){0,254}$')";
- }
- if ("eld-19".equals(key)) {
- return "path.matches('^[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\.[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\[x\\\\])?(\\\\:[^\\\\s\\\\.]+)?)*$')";
- }
- if ("eld-20".equals(key)) {
- return "path.matches('^[A-Za-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*(\\\\[x])?)*$')";
- }
-
- // handled in 4.0.1
- if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) {
- return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())";
- }
- if ("isModifier implies isModifierReason.exists()".equals(expr)) {
- return "(isModifier.exists() and isModifier) implies isModifierReason.exists()";
- }
- if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) {
- return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))";
- }
- if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) {
- return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()";
- }
- if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) {
- return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()";
- }
-
- // R3
- if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) {
- return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')";
- }
- if ("value.empty() or code!=component.code".equals(expr)) {
- return "value.empty() or (code in component.code).not()";
- }
- if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) {
- return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)";
- }
- if ("element.all(definition and min and max)".equals(expr)) {
- return "element.all(definition.exists() and min.exists() and max.exists())";
- }
- if ("telecom or endpoint".equals(expr)) {
- return "telecom.exists() or endpoint.exists()";
- }
- if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) {
- return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)";
- }
- if ("searchType implies type = 'string'".equals(expr)) {
- return "searchType.exists() implies type = 'string'";
- }
- if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) {
- return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')";
- }
- if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) {
- return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())";
- }
- if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))".equals(expr)) {
- return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))";
- }
- if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))".equals(expr)) {
- return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))";
- }
- if ("probability is decimal implies probability.as(decimal) <= 100".equals(expr)) {
- if (key.equals("ras-2")) {
- return "probability.empty() or (probability is decimal implies probability.as(decimal) <= 100)";
- }
- }
- if ("".equals(expr)) {
- return "";
- }
- return expr;
- }
public IEvaluationContext getExternalHostServices() {
return externalHostServices;
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java
new file mode 100644
index 000000000..fad76a3de
--- /dev/null
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java
@@ -0,0 +1,117 @@
+package org.hl7.fhir.validation.instance.utils;
+
+import org.hl7.fhir.utilities.Utilities;
+
+public class FHIRPathExpressionFixer {
+
+
+ public static String fixExpr(String expr, String key) {
+ // this is a hack work around for past publication of wrong FHIRPath expressions
+ // R4
+ // waiting for 4.0.2
+ //TODO is this expression below correct? @grahamegrieve
+ if ("probability is decimal implies (probability as decimal) <= 100".equals(expr)) {
+ return "probability.empty() or ((probability is decimal) implies ((probability as decimal) <= 100))";
+ }
+ if ("enableWhen.count() > 2 implies enableBehavior.exists()".equals(expr)) {
+ return "enableWhen.count() >= 2 implies enableBehavior.exists()";
+ }
+ if ("txt-2".equals(key)) {
+ return "htmlChecks2()";
+ }
+
+ // fixes to string functions in FHIRPath
+ // ref-1
+ if (expr.equals("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids')) or (reference='#' and %rootResource!=%resource)")) { // R5
+ return "reference.exists() implies ("+expr+")";
+ }
+ if (expr.equals("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))")) { // R4/R4B
+ return "reference.exists() implies (reference = '#' or ("+expr+"))";
+ }
+ if (expr.equals("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))")) { // STU3
+ return "reference.exists() implies (reference = '#' or (reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))))";
+ }
+ // bld-8
+ if (expr.equals("fullUrl.contains('/_history/').not()")) { // R4
+ return "fullUrl.exists() implies fullUrl.contains('/_history/').not()";
+ }
+ if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) {
+ return "name.exists() implies name.matches('^[A-Z]([A-Za-z0-9_]){0,254}$')";
+ }
+ // canonical
+ if (expr.equals("name.matches('[A-Z]([A-Za-z0-9_]){0,254}')")) {
+ return ("name.exists() implies name.matches('[A-Z]([A-Za-z0-9_]){0,254}')");
+ }
+
+
+ // clarification in FHIRPath spec
+ if ("eld-19".equals(key)) {
+ return "path.matches('^[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\.[^\\\\s\\\\.,:;\\\\\\'\"\\\\/|?!@#$%&*()\\\\[\\\\]{}]{1,64}(\\\\[x\\\\])?(\\\\:[^\\\\s\\\\.]+)?)*$')";
+ }
+ if ("eld-20".equals(key)) {
+ return "path.matches('^[A-Za-z][A-Za-z0-9]*(\\\\.[a-z][A-Za-z0-9]*(\\\\[x])?)*$')";
+ }
+
+ // handled in 4.0.1
+ if ("(component.empty() and hasMember.empty()) implies (dataAbsentReason or value)".equals(expr)) {
+ return "(component.empty() and hasMember.empty()) implies (dataAbsentReason.exists() or value.exists())";
+ }
+ if ("isModifier implies isModifierReason.exists()".equals(expr)) {
+ return "(isModifier.exists() and isModifier) implies isModifierReason.exists()";
+ }
+ if ("(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().not() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))".equals(expr)) {
+ return "(%resource.kind = 'logical' or element.first().path.startsWith(%resource.type)) and (element.tail().empty() or element.tail().all(path.startsWith(%resource.differential.element.first().path.replaceMatches('\\\\..*','')&'.')))";
+ }
+ if ("differential.element.all(id) and differential.element.id.trace('ids').isDistinct()".equals(expr)) {
+ return "differential.element.all(id.exists()) and differential.element.id.trace('ids').isDistinct()";
+ }
+ if ("snapshot.element.all(id) and snapshot.element.id.trace('ids').isDistinct()".equals(expr)) {
+ return "snapshot.element.all(id.exists()) and snapshot.element.id.trace('ids').isDistinct()";
+ }
+
+ // R3
+ if ("(code or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')".equals(expr)) {
+ return "(code.exists() or value.empty()) and (system.empty() or system = 'urn:iso:std:iso:4217')";
+ }
+ if ("value.empty() or code!=component.code".equals(expr)) {
+ return "value.empty() or (code in component.code).not()";
+ }
+ if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) {
+ return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)";
+ }
+ if ("element.all(definition and min and max)".equals(expr)) {
+ return "element.all(definition.exists() and min.exists() and max.exists())";
+ }
+ if ("telecom or endpoint".equals(expr)) {
+ return "telecom.exists() or endpoint.exists()";
+ }
+ if ("(code or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)".equals(expr)) {
+ return "(code.exists() or value.empty()) and (system.empty() or system = %ucum) and (value.empty() or value > 0)";
+ }
+ if ("searchType implies type = 'string'".equals(expr)) {
+ return "searchType.exists() implies type = 'string'";
+ }
+ if ("abatement.empty() or (abatement as boolean).not() or clinicalStatus='resolved' or clinicalStatus='remission' or clinicalStatus='inactive'".equals(expr)) {
+ return "abatement.empty() or (abatement is boolean).not() or (abatement as boolean).not() or (clinicalStatus = 'resolved') or (clinicalStatus = 'remission') or (clinicalStatus = 'inactive')";
+ }
+ if ("(component.empty() and related.empty()) implies (dataAbsentReason or value)".equals(expr)) {
+ return "(component.empty() and related.empty()) implies (dataAbsentReason.exists() or value.exists())";
+ }
+ if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))".equals(expr)) {
+ return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))";
+ }
+ if ("reference.startsWith('#').not() or (reference.substring(1).trace('url') in %resource.contained.id.trace('ids'))".equals(expr)) {
+ return "(reference = '#') or reference.startsWith('#').not() or (reference.substring(1).trace('url') in %rootResource.contained.id.trace('ids'))";
+ }
+ if ("probability is decimal implies probability.as(decimal) <= 100".equals(expr)) {
+ if (key.equals("ras-2")) {
+ return "probability.empty() or (probability is decimal implies probability.as(decimal) <= 100)";
+ }
+ }
+ if ("".equals(expr)) {
+ return "";
+ }
+ return expr;
+ }
+
+}