Merge pull request #886 from hapifhir/gg-202207-fhirpath
Gg 202207 fhirpath
This commit is contained in:
commit
f3fa8cae24
|
@ -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();
|
||||
|
|
|
@ -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<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());
|
||||
}
|
||||
Base base = focus.get(0);
|
||||
|
@ -4208,17 +4224,23 @@ public class FHIRPathEngine {
|
|||
|
||||
private List<Base> funcReplace(ExecutionContext context, List<Base> focus, ExpressionNode expr) throws FHIRException, PathEngineException {
|
||||
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) {
|
||||
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<Base> funcReplaceMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
String regex = convertToString(execute(context, focus, exp.getParameters().get(0), true));
|
||||
String repl = convertToString(execute(context, focus, exp.getParameters().get(1), true));
|
||||
List<Base> regexB = execute(context, focus, exp.getParameters().get(0), 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) {
|
||||
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<Base> funcEndsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> funcMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> funcContains(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> funcLength(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
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<Base> funcStartsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> funcLower(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
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<Base> funcUpper(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
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<Base> funcToChars(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
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<Base> funcIndexOf(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> 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<Base>();
|
||||
}
|
||||
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()) {
|
||||
|
|
|
@ -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<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());
|
||||
}
|
||||
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 {
|
||||
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) {
|
||||
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<Base> funcReplaceMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
List<Base> result = new ArrayList<Base>();
|
||||
String regex = convertToString(execute(context, focus, exp.getParameters().get(0), true));
|
||||
String repl = convertToString(execute(context, focus, exp.getParameters().get(1), true));
|
||||
List<Base> regexB = execute(context, focus, exp.getParameters().get(0), 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) {
|
||||
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 {
|
||||
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) {
|
||||
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<Base> funcMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
String st = convertToString(focus.get(0));
|
||||
if (Utilities.noString(st)) {
|
||||
|
@ -4973,10 +4994,13 @@ public class FHIRPathEngine {
|
|||
|
||||
private List<Base> funcContains(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> funcStartsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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<Base> funcIndexOf(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
|
||||
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) {
|
||||
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) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -244,7 +244,7 @@ public class PackageClient {
|
|||
|
||||
public void findDependents(Set<String> 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) {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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<ValidationMessage> 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<ValidationMessage>();
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue