various fixes

* Handle reslicing within the same profile
* Fix up wrong handling of context on exists() in FHIRPath
* Add hints to profiles around behavior of pattern on repeating elements
* Ignore all tooling extensions when validating
This commit is contained in:
Grahame Grieve 2022-04-05 15:52:33 +10:00
parent 87fed0cfc8
commit 8a05e4b2b2
16 changed files with 633 additions and 352 deletions

View File

@ -1,6 +1,10 @@
## Validator Changes
* no changes
* Handle reslicing within the same profile
* Fix up wrong handling of context on exists() in FHIRPath
* Add hints to profiles around behavior of pattern on repeating elements
* Ignore all tooling extensions when validating
*
## Other code changes

View File

@ -1172,7 +1172,15 @@ public class ProfileUtilities extends TranslatingUtilities {
if (diffMatches.get(0).hasType() && "Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode()) && !isValidType(diffMatches.get(0).getType().get(0), currentBase)) {
throw new DefinitionException(context.formatMessage(I18nConstants.VALIDATION_VAL_ILLEGAL_TYPE_CONSTRAINT, url, diffMatches.get(0).getPath(), diffMatches.get(0).getType().get(0), currentBase.typeSummary()));
}
if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
String id = diffMatches.get(0).getId();
String lid = tail(id);
if (lid.contains("/")) {
// the template comes from the snapshot of the base
generateIds(result.getElement(), url, srcSD.getType());
String baseId = id.substring(0, id.length()-lid.length()) + lid.substring(0, lid.indexOf("/")); // this is wrong if there's more than one reslice (todo: one thing at a time)
template = getById(result.getElement(), baseId);
} else if (diffMatches.get(0).hasType() && diffMatches.get(0).getType().size() == 1 && diffMatches.get(0).getType().get(0).hasProfile() && !"Reference".equals(diffMatches.get(0).getType().get(0).getWorkingCode())) {
CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
if (sd == null && xver != null && xver.matchingUrl(p.getValue())) {
@ -1932,6 +1940,15 @@ public class ProfileUtilities extends TranslatingUtilities {
return res;
}
private ElementDefinition getById(List<ElementDefinition> list, String baseId) {
for (ElementDefinition t : list) {
if (baseId.equals(t.getId())) {
return t;
}
}
return null;
}
private void updateConstraintSources(ElementDefinition ed, String url) {
for (ElementDefinitionConstraintComponent c : ed.getConstraint()) {
if (!c.hasSource()) {
@ -4844,6 +4861,8 @@ public class ProfileUtilities extends TranslatingUtilities {
String valueSet = ext.getExtensionString("valueSet");
String doco = ext.getExtensionString("documentation");
UsageContext usage = (ext.hasExtension("usage")) ? ext.getExtensionByUrl("usage").getValueUsageContext() : null;
boolean any = "any".equals(ext.getExtensionString("scope"));
//
// purpose: code - defines how the binding is used
// usage : UsageContext - defines the contexts in which this binding is used for it's purpose
@ -5275,7 +5294,9 @@ public class ProfileUtilities extends TranslatingUtilities {
}
private String tail(String path) {
if (path.contains("."))
if (path == null) {
return "";
} else if (path.contains("."))
return path.substring(path.lastIndexOf('.')+1);
else
return path;

View File

@ -0,0 +1,76 @@
package org.hl7.fhir.r5.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BuildExtensions extends ToolingExtensions {
public static final String EXT_DESCRIPTION = "http://hl7.org/fhir/build/StructureDefinition/description";
public static final String EXT_TITLE = "http://hl7.org/fhir/build/StructureDefinition/title";
public static final String EXT_EXAMPLE_TYPE = "http://hl7.org/fhir/build/CodeSystem/example-type";
public static final String EXT_NOT_REGISTERED = "http://hl7.org/fhir/build/StructureDefinition/registered";
public static final String EXT_IG = "http://hl7.org/fhir/build/StructureDefinition/ig";
public static final String EXT_EXAMPLE_FOR = "http://hl7.org/fhir/build/StructureDefinition/example-for";
public static final String EXT_PATH = "http://hl7.org/fhir/build/StructureDefinition/path";
public static final String EXT_FOOTER = "http://hl7.org/fhir/build/StructureDefinition/footer";
public static final String EXT_FOOTER2 = "http://hl7.org/fhir/build/StructureDefinition/footer2";
public static final String EXT_ENTERED_IN_ERROR_STATUS = "http://hl7.org/fhir/build/StructureDefinition/entered-in-error-status";
public static final String EXT_PROPOSED_ORDER = "http://hl7.org/fhir/build/StructureDefinition/proposed-order";
public static final String EXT_HINT = "http://hl7.org/fhir/build/StructureDefinition/hint";
public static final String EXT_LAYOUT = "http://hl7.org/fhir/build/StructureDefinition/layout";
public static final String EXT_TODO = "http://hl7.org/fhir/build/StructureDefinition/todo";
public static final String EXT_COMMITTEE_NOTES = "http://hl7.org/fhir/build/StructureDefinition/committee-notes";
public static final String EXT_UML_DIR = "http://hl7.org/fhir/build/StructureDefinition/uml-dir";
public static final String EXT_UML_BREAK = "http://hl7.org/fhir/build/StructureDefinition/uml-break";
public static final String EXT_SVG = "http://hl7.org/fhir/build/StructureDefinition/svg";
public static final String EXT_OCL = "http://hl7.org/fhir/build/StructureDefinition/ocl";
public static final String EXT_FIXED_NAME = "http://hl7.org/fhir/build/StructureDefinition/fixed-name";
public static final String EXT_TURTLE = "http://hl7.org/fhir/build/StructureDefinition/turtle";
public static final String EXT_NAME = "http://hl7.org/fhir/build/StructureDefinition/name";
public static final String EXT_V2_MAP = "http://hl7.org/fhir/build/StructureDefinition/v2-map";
public static final String EXT_V3_MAP = "http://hl7.org/fhir/build/StructureDefinition/v3-map";
public static final String EXT_BINDING_DEFINITION = "http://hl7.org/fhir/build/StructureDefinition/binding-definition";
public static final String EXT_URI = "http://hl7.org/fhir/build/StructureDefinition/uri";
public static final String EXT_WEBSITE = "http://hl7.org/fhir/build/StructureDefinition/website";
public static final String EXT_EMAIL = "http://hl7.org/fhir/build/StructureDefinition/email";
public static final String EXT_COPYRIGHT = "http://hl7.org/fhir/build/StructureDefinition/copyright";
public static final String EXT_CS_OID = "http://hl7.org/fhir/build/StructureDefinition/cs-oid";
public static final String EXT_VS_OID = "http://hl7.org/fhir/build/StructureDefinition/vs-oid";
public static final String EXT_STATUS = "http://hl7.org/fhir/build/StructureDefinition/status";
public static final String EXT_INTRODUCTION = "http://hl7.org/fhir/build/StructureDefinition/introduction";
public static final String EXT_NOTES = "http://hl7.org/fhir/build/StructureDefinition/notes";
public static final String EXT_CODE = "http://hl7.org/fhir/build/StructureDefinition/code";
public static final String EXT_TYPE = "http://hl7.org/fhir/build/StructureDefinition/type";
public static final String EXT_SUMMARY = "http://hl7.org/fhir/build/StructureDefinition/summary";
// public static final String EXT_EXPLANATION = "http://hl7.org/fhir/build/StructureDefinition/explanation";
public static final String EXT_NO_BINDING = "http://hl7.org/fhir/build/StructureDefinition/no-binding";
public static final String EXT_OP_EXAMPLE = "http://hl7.org/fhir/build/StructureDefinition/example";
public static final String EXT_OP_EXAMPLE_CONTENT = "content";
public static final String EXT_OP_EXAMPLE_COMMENT = "comment";
public static final String EXT_OP_EXAMPLE_RESPONSE = "response";
public static final String EXT_OP_EXAMPLE_LIST = "list";
public static final String EXT_TEMPLATE = "http://hl7.org/fhir/build/StructureDefinition/template";
public static final String EXT_BINDING_NAME = "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName";
public static List<String> allConsts() {
List<String> list = new ArrayList<>();
for (Field field : BuildExtensions.class.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
try {
list.add(field.get(field.getType()).toString());
} catch (Exception e) {
}
}
}
list.addAll(ToolingExtensions.allConsts());
return list;
}
}

View File

@ -84,12 +84,12 @@ import ca.uhn.fhir.util.ElementUtil;
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
@ -408,8 +408,8 @@ public class FHIRPathEngine {
* @param item
* @param name
* @param result
* @throws FHIRException
*/
* @throws FHIRException
*/
protected void getChildrenByName(Base item, String name, List<Base> result) throws FHIRException {
String tn = null;
if (isAllowPolymorphicNames()) {
@ -640,10 +640,10 @@ public class FHIRPathEngine {
} else if (left.getHour() > right.getHour()) {
return 1;
// hour is not a valid precision
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) {
// return 0;
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) {
// return null;
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) {
// return 0;
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) {
// return null;
}
if (left.getMinute() < right.getMinute()) {
@ -676,7 +676,7 @@ public class FHIRPathEngine {
* @throws FHIRException
* @
*/
public List<Base> evaluate(Base base, ExpressionNode ExpressionNode) throws FHIRException {
public List<Base> evaluate(Base base, ExpressionNode ExpressionNode) throws FHIRException {
List<Base> list = new ArrayList<Base>();
if (base != null) {
list.add(base);
@ -691,10 +691,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use
* @return
* @throws FHIRException
* @throws FHIRException
* @
*/
public List<Base> evaluate(Base base, String path) throws FHIRException {
public List<Base> evaluate(Base base, String path) throws FHIRException {
ExpressionNode exp = parse(path);
List<Base> list = new ArrayList<Base>();
if (base != null) {
@ -710,10 +710,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated
* @param ExpressionNode - the parsed ExpressionNode statement to use
* @return
* @throws FHIRException
* @throws FHIRException
* @
*/
public List<Base> evaluate(Object appContext, Resource focusResource, Resource rootResource, Base base, ExpressionNode ExpressionNode) throws FHIRException {
public List<Base> evaluate(Object appContext, Resource focusResource, Resource rootResource, Base base, ExpressionNode ExpressionNode) throws FHIRException {
List<Base> list = new ArrayList<Base>();
if (base != null) {
list.add(base);
@ -746,10 +746,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use
* @return
* @throws FHIRException
* @throws FHIRException
* @
*/
public List<Base> evaluate(Object appContext, Resource focusResource, Resource rootResource, Base base, String path) throws FHIRException {
public List<Base> evaluate(Object appContext, Resource focusResource, Resource rootResource, Base base, String path) throws FHIRException {
ExpressionNode exp = parse(path);
List<Base> list = new ArrayList<Base>();
if (base != null) {
@ -765,10 +765,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use
* @return
* @throws FHIRException
* @throws FHIRException
* @
*/
public boolean evaluateToBoolean(Resource focusResource, Resource rootResource, Base base, String path) throws FHIRException {
public boolean evaluateToBoolean(Resource focusResource, Resource rootResource, Base base, String path) throws FHIRException {
return convertToBoolean(evaluate(null, focusResource, rootResource, base, path));
}
@ -815,7 +815,7 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use
* @return
* @throws FHIRException
* @throws FHIRException
* @
*/
public String evaluateToString(Base base, String path) throws FHIRException {
@ -935,7 +935,7 @@ public class FHIRPathEngine {
return focusResource;
}
public Base getRootResource() {
return rootResource;
return rootResource;
}
public Base getThisItem() {
return thisItem;
@ -1386,8 +1386,8 @@ public class FHIRPathEngine {
return false;
}
private List<Base> execute(ExecutionContext context, List<Base> focus, ExpressionNode exp, boolean atEntry) throws FHIRException {
// System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString());
private List<Base> execute(ExecutionContext context, List<Base> focus, ExpressionNode exp, boolean atEntry) throws FHIRException {
// System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString());
List<Base> work = new ArrayList<Base>();
switch (exp.getKind()) {
case Unary:
@ -1441,13 +1441,13 @@ public class FHIRPathEngine {
} else {
work2 = execute(context, focus, next, true);
work = operate(context, work, last.getOperation(), work2, last);
// System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString());
// System.out.println("Result of {'"+last.toString()+" "+last.getOperation().toCode()+" "+next.toString()+"'}: "+focus.toString());
}
last = next;
next = next.getOpNext();
}
}
// System.out.println("Result of {'"+exp.toString()+"'}: "+work.toString());
// System.out.println("Result of {'"+exp.toString()+"'}: "+work.toString());
return work;
}
@ -1972,7 +1972,7 @@ public class FHIRPathEngine {
} else if (left instanceof DecimalType || right instanceof DecimalType) {
return decEqual(left.primitiveValue(), right.primitiveValue());
} else if (left.isPrimitive() && right.isPrimitive()) {
return Base.equals(left.primitiveValue(), right.primitiveValue());
return Base.equals(left.primitiveValue(), right.primitiveValue());
} else {
return Base.compareDeep(left, right, false);
}
@ -2183,7 +2183,7 @@ public class FHIRPathEngine {
private final static String[] FHIR_TYPES_STRING = new String[] {"string", "uri", "code", "oid", "id", "uuid", "sid", "markdown", "base64Binary", "canonical", "url"};
private List<Base> opLessThan(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
private List<Base> opLessThan(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
@ -2231,7 +2231,7 @@ public class FHIRPathEngine {
return new ArrayList<Base>();
}
private List<Base> opGreater(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
private List<Base> opGreater(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>();
if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) {
@ -2278,7 +2278,7 @@ public class FHIRPathEngine {
return new ArrayList<Base>();
}
private List<Base> opLessOrEqual(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
private List<Base> opLessOrEqual(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
if (left.size() == 0 || right.size() == 0) {
return new ArrayList<Base>();
}
@ -2328,7 +2328,7 @@ public class FHIRPathEngine {
return new ArrayList<Base>();
}
private List<Base> opGreaterOrEqual(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
private List<Base> opGreaterOrEqual(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
if (left.size() == 0 || right.size() == 0) {
return new ArrayList<Base>();
}
@ -2376,34 +2376,34 @@ public class FHIRPathEngine {
return new ArrayList<Base>();
}
private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
boolean ans = false;
String url = right.get(0).primitiveValue();
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url);
if (vs != null) {
for (Base l : left) {
if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) {
private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
boolean ans = false;
String url = right.get(0).primitiveValue();
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url);
if (vs != null) {
for (Base l : left) {
if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) {
if (worker.validateCode(terminologyServiceOptions.guessSystem() , TypeConvertor.castToCoding(l), vs).isOk()) {
ans = true;
}
} else if (l.fhirType().equals("Coding")) {
if (worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCoding(l), vs).isOk()) {
ans = true;
}
} else if (l.fhirType().equals("CodeableConcept")) {
CodeableConcept cc = TypeConvertor.castToCodeableConcept(l);
ValidationResult vr = worker.validateCode(terminologyServiceOptions, cc, vs);
// System.out.println("~~~ "+DataRenderer.display(worker, cc)+ " memberOf "+url+": "+vr.toString());
if (vr.isOk()) {
ans = true;
}
} else {
// System.out.println("unknown type in opMemberOf: "+l.fhirType());
}
}
}
return makeBoolean(ans);
}
} else if (l.fhirType().equals("Coding")) {
if (worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCoding(l), vs).isOk()) {
ans = true;
}
} else if (l.fhirType().equals("CodeableConcept")) {
CodeableConcept cc = TypeConvertor.castToCodeableConcept(l);
ValidationResult vr = worker.validateCode(terminologyServiceOptions, cc, vs);
// System.out.println("~~~ "+DataRenderer.display(worker, cc)+ " memberOf "+url+": "+vr.toString());
if (vr.isOk()) {
ans = true;
}
} else {
// System.out.println("unknown type in opMemberOf: "+l.fhirType());
}
}
}
return makeBoolean(ans);
}
private List<Base> opIn(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
if (left.size() == 0) {
@ -2432,7 +2432,7 @@ public class FHIRPathEngine {
private List<Base> opContains(List<Base> left, List<Base> right, ExpressionNode expr) {
if (left.size() == 0 || right.size() == 0) {
return new ArrayList<Base>();
return new ArrayList<Base>();
}
boolean ans = true;
for (Base r : right) {
@ -2936,7 +2936,7 @@ public class FHIRPathEngine {
}
}
private List<Base> execute(ExecutionContext context, Base item, ExpressionNode exp, boolean atEntry) throws FHIRException {
private List<Base> execute(ExecutionContext context, Base item, ExpressionNode exp, boolean atEntry) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (atEntry && context.appInfo != null && hostServices != null) {
// we'll see if the name matches a constant known by the context.
@ -2946,7 +2946,7 @@ public class FHIRPathEngine {
return result;
}
}
if (atEntry && Character.isUpperCase(exp.getName().charAt(0))) {// special case for start up
if (atEntry && exp.getName() != null && Character.isUpperCase(exp.getName().charAt(0))) {// special case for start up
StructureDefinition sd = worker.fetchTypeDefinition(item.fhirType());
if (sd == null) {
// logical model
@ -3006,12 +3006,14 @@ public class FHIRPathEngine {
if (exp.getFunction() == Function.Is || exp.getFunction() == Function.As || exp.getFunction() == Function.OfType) {
paramTypes.add(new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
} else {
int i = 0;
for (ExpressionNode expr : exp.getParameters()) {
if (exp.getFunction() == Function.Where || exp.getFunction() == Function.All || exp.getFunction() == Function.Select || exp.getFunction() == Function.Repeat || exp.getFunction() == Function.Aggregate) {
if (isExpressionParameter(exp, i)) {
paramTypes.add(executeType(changeThis(context, focus), focus, expr, true));
} else {
paramTypes.add(executeType(context, focus, expr, true));
paramTypes.add(executeType(context, context.thisItem, expr, true));
}
i++;
}
}
switch (exp.getFunction()) {
@ -3344,6 +3346,17 @@ public class FHIRPathEngine {
throw new Error("not Implemented yet");
}
private boolean isExpressionParameter(ExpressionNode exp, int i) {
switch (i) {
case 0:
return exp.getFunction() == Function.Where || exp.getFunction() == Function.Exists || exp.getFunction() == Function.All || exp.getFunction() == Function.Select || exp.getFunction() == Function.Repeat || exp.getFunction() == Function.Aggregate;
case 1:
return exp.getFunction() == Function.Trace;
default:
return false;
}
}
private void checkParamTypes(ExpressionNode expr, String funcName, List<TypeDetails> paramTypes, TypeDetails... typeSet) throws PathEngineException {
int i = 0;
@ -3390,9 +3403,9 @@ public class FHIRPathEngine {
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.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());
}
@ -3426,7 +3439,7 @@ public class FHIRPathEngine {
// return s.equals("boolean") || s.equals("integer") || s.equals("decimal") || s.equals("base64Binary") || s.equals("instant") || s.equals("string") || s.equals("uri") || s.equals("date") || s.equals("dateTime") || s.equals("time") || s.equals("code") || s.equals("oid") || s.equals("id") || s.equals("unsignedInt") || s.equals("positiveInt") || s.equals("markdown");
// }
private List<Base> evaluateFunction(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> evaluateFunction(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
switch (exp.getFunction()) {
case Empty : return funcEmpty(context, focus, exp);
case Not : return funcNot(context, focus, exp);
@ -3534,24 +3547,24 @@ public class FHIRPathEngine {
}
}
private List<Base> funcSqrt(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
private List<Base> funcSqrt(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
if (focus.size() != 1) {
throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size());
}
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.sqrt(d)));
} catch (Exception e) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.sqrt(d)));
} catch (Exception e) {
// just return nothing
}
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal");
}
return result;
}
}
private List<Base> funcAbs(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
@ -3561,12 +3574,12 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.abs(d)));
} catch (Exception e) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.abs(d)));
} catch (Exception e) {
// just return nothing
}
} else if (base.hasType("Quantity")) {
Quantity qty = (Quantity) base;
result.add(qty.copy().setValue(qty.getValue().abs()));
@ -3584,11 +3597,11 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue());
try {result.add(new IntegerType((int) Math.ceil(d)));
} catch (Exception e) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {result.add(new IntegerType((int) Math.ceil(d)));
} catch (Exception e) {
// just return nothing
}
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal");
}
@ -3602,12 +3615,12 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new IntegerType((int) Math.floor(d)));
} catch (Exception e) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new IntegerType((int) Math.floor(d)));
} catch (Exception e) {
// just return nothing
}
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal");
}
@ -3622,12 +3635,12 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.exp(d)));
} catch (Exception e) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.exp(d)));
} catch (Exception e) {
// just return nothing
}
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal");
@ -3643,12 +3656,12 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.log(d)));
} catch (Exception e) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.log(d)));
} catch (Exception e) {
// just return nothing
}
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal");
}
@ -3696,12 +3709,12 @@ public class FHIRPathEngine {
throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal");
}
Double e = Double.parseDouble(n1.get(0).primitiveValue());
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.pow(d, e)));
} catch (Exception ex) {
// just return nothing
}
Double d = Double.parseDouble(base.primitiveValue());
try {
result.add(new DecimalType(Math.pow(d, e)));
} catch (Exception ex) {
// just return nothing
}
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal");
}
@ -3715,11 +3728,11 @@ public class FHIRPathEngine {
Base base = focus.get(0);
List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
String s = base.primitiveValue();
if (s.contains(".")) {
s = s.substring(0, s.indexOf("."));
}
result.add(new IntegerType(s));
String s = base.primitiveValue();
if (s.contains(".")) {
s = s.substring(0, s.indexOf("."));
}
result.add(new IntegerType(s));
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal");
}
@ -3750,26 +3763,26 @@ public class FHIRPathEngine {
}
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
private List<Base> funcEncode(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
private List<Base> funcEncode(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
String param = nl.get(0).primitiveValue();
@ -3788,7 +3801,7 @@ public class FHIRPathEngine {
}
}
return result;
}
}
private List<Base> funcDecode(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
@ -4047,7 +4060,7 @@ public class FHIRPathEngine {
} else if (l.fhirType().equals("CodeableConcept")) {
return makeBoolean(worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCodeableConcept(l), vs).isOk());
} else {
// System.out.println("unknown type in funcMemberOf: "+l.fhirType());
// System.out.println("unknown type in funcMemberOf: "+l.fhirType());
return new ArrayList<Base>();
}
}
@ -4190,18 +4203,18 @@ public class FHIRPathEngine {
}
private List<Base> funcToDateTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
// List<Base> result = new ArrayList<Base>();
// result.add(new BooleanType(convertToBoolean(focus)));
// return result;
throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime");
}
// List<Base> result = new ArrayList<Base>();
// result.add(new BooleanType(convertToBoolean(focus)));
// return result;
throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime");
}
private List<Base> funcToTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
// List<Base> result = new ArrayList<Base>();
// result.add(new BooleanType(convertToBoolean(focus)));
// return result;
// List<Base> result = new ArrayList<Base>();
// result.add(new BooleanType(convertToBoolean(focus)));
// return result;
throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime");
}
}
private List<Base> funcToDecimal(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
@ -4253,7 +4266,7 @@ public class FHIRPathEngine {
result.add(item);
}
}
for (Base item : execute(context, focus, exp.getParameters().get(0), true)) {
for (Base item : execute(context, baseToList(context.thisItem), exp.getParameters().get(0), true)) {
if (!doContains(result, item)) {
result.add(item);
}
@ -4402,10 +4415,21 @@ public class FHIRPathEngine {
pc.add(item);
added.addAll(execute(changeThis(context, item), pc, exp.getParameters().get(0), false));
}
more = !added.isEmpty();
result.addAll(added);
more = false;
current.clear();
current.addAll(added);
for (Base b : added) {
boolean isnew = true;
for (Base t : result) {
if (b.equalsDeep(t)) {
isnew = false;
}
}
if (isnew) {
result.add(b);
current.add(b);
more = true;
}
}
}
return result;
}
@ -4593,125 +4617,125 @@ public class FHIRPathEngine {
return result;
}
private List<Base> funcAllFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean all = true;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
if (v != Equality.False) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
} else {
boolean all = true;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
}
Equality v = asBool(item, true);
private List<Base> funcAllFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean all = true;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
if (v != Equality.False) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
}
return result;
}
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
} else {
boolean all = true;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
}
private List<Base> funcAnyFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean any = false;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
Equality v = asBool(item, true);
if (v != Equality.False) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
}
return result;
}
private List<Base> funcAnyFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean any = false;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
if (v == Equality.False) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
} else {
boolean any = false;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
}
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
} else {
boolean any = false;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
}
Equality v = asBool(item, true);
Equality v = asBool(item, true);
if (v == Equality.False) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
}
return result;
}
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
}
return result;
}
private List<Base> funcAllTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean all = true;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
private List<Base> funcAllTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean all = true;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
if (v != Equality.True) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
} else {
boolean all = true;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
}
Equality v = asBool(item, true);
if (v != Equality.True) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
}
return result;
}
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
} else {
boolean all = true;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
}
Equality v = asBool(item, true);
if (v != Equality.True) {
all = false;
break;
}
}
result.add(new BooleanType(all).noExtensions());
}
return result;
}
private List<Base> funcAnyTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean any = false;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
private List<Base> funcAnyTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) {
boolean any = false;
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
pc.clear();
pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp);
if (v == Equality.True) {
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
} else {
boolean any = false;
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
} else {
boolean any = false;
for (Base item : focus) {
if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
@ -4719,16 +4743,16 @@ public class FHIRPathEngine {
Equality v = asBool(item, true);
if (v == Equality.True) {
any = true;
break;
}
any = true;
break;
}
}
result.add(new BooleanType(any).noExtensions());
}
return result;
}
}
return result;
}
private boolean canConvertToBoolean(Base item) {
private boolean canConvertToBoolean(Base item) {
return (item.isBooleanPrimitive());
}
@ -4778,7 +4802,7 @@ public class FHIRPathEngine {
return result;
}
private List<Base> funcMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> funcMatches(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
@ -4798,29 +4822,30 @@ public class FHIRPathEngine {
return result;
}
private List<Base> funcMatchesFull(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));
if (focus.size() == 1 && !Utilities.noString(sw)) {
String st = convertToString(focus.get(0));
if (Utilities.noString(st)) {
result.add(new BooleanType(false).noExtensions());
} else {
Pattern p = Pattern.compile("(?s)" + sw);
Matcher m = p.matcher(st);
boolean ok = m.matches();
result.add(new BooleanType(ok).noExtensions());
}
} else {
result.add(new BooleanType(false).noExtensions());
}
return result;
}
private List<Base> funcContains(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> funcMatchesFull(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));
if (focus.size() == 1 && !Utilities.noString(sw)) {
String st = convertToString(focus.get(0));
if (Utilities.noString(st)) {
result.add(new BooleanType(false).noExtensions());
} else {
Pattern p = Pattern.compile("(?s)" + sw);
Matcher m = p.matcher(st);
boolean ok = m.matches();
result.add(new BooleanType(ok).noExtensions());
}
} else {
result.add(new BooleanType(false).noExtensions());
}
return result;
}
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));
if (focus.size() != 1) {
result.add(new BooleanType(false).noExtensions());
} else if (Utilities.noString(sw)) {
@ -4836,6 +4861,12 @@ public class FHIRPathEngine {
return result;
}
private List<Base> baseToList(Base b) {
List<Base> res = new ArrayList<>();
res.add(b);
return res;
}
private List<Base> funcLength(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> result = new ArrayList<Base>();
if (focus.size() == 1) {
@ -4856,7 +4887,7 @@ public class FHIRPathEngine {
return result;
}
private List<Base> funcStartsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> funcStartsWith(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
@ -4927,7 +4958,7 @@ public class FHIRPathEngine {
return result;
}
private List<Base> funcSubstring(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> funcSubstring(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true);
int i1 = Integer.parseInt(n1.get(0).primitiveValue());
@ -5197,7 +5228,7 @@ public class FHIRPathEngine {
}
private List<Base> funcWhere(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> funcWhere(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
List<Base> pc = new ArrayList<Base>();
for (Base item : focus) {
@ -5225,7 +5256,7 @@ public class FHIRPathEngine {
}
private List<Base> funcItem(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
private List<Base> funcItem(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>();
String s = convertToString(execute(context, focus, exp.getParameters().get(0), true));
if (Utilities.isInteger(s) && Integer.parseInt(s) < focus.size()) {
@ -5236,7 +5267,7 @@ public class FHIRPathEngine {
private List<Base> funcEmpty(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> result = new ArrayList<Base>();
result.add(new BooleanType(ElementUtil.isEmpty(focus)).noExtensions());
result.add(new BooleanType(ElementUtil.isEmpty(focus)).noExtensions());
return result;
}
@ -5576,7 +5607,7 @@ public class FHIRPathEngine {
if (t.getPath().endsWith(".extension") && t.hasSliceName()) {
System.out.println("t: "+t.getId());
StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ?
null : worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue());
null : worker.fetchResource(StructureDefinition.class, t.getType().get(0).getProfile().get(0).getValue());
while (exsd != null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension")) {
exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition());
}

View File

@ -1,5 +1,8 @@
package org.hl7.fhir.r5.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -61,6 +64,7 @@ POSSIBILITY OF SUCH DAMAGE.
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -941,5 +945,21 @@ public class ToolingExtensions {
return false;
}
public static List<String> allConsts() {
List<String> list = new ArrayList<>();
for (Field field : ToolingExtensions.class.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
try {
list.add(field.get(field.getType()).toString());
} catch (Exception e) {
}
}
}
return list;
}
}

View File

@ -377,6 +377,8 @@ public class I18nConstants {
public static final String SD_ED_BIND_NO_BINDABLE = "SD_ED_BIND_NO_BINDABLE";
public static final String SD_ED_BIND_MULTIPLE_TYPES = "SD_ED_BIND_MULTIPLE_TYPES";
public static final String SD_VALUE_TYPE_IILEGAL = "SD_VALUE_TYPE_IILEGAL";
public static final String SD_VALUE_TYPE_REPEAT_HINT = "SD_VALUE_TYPE_REPEAT_HINT";
public static final String SD_VALUE_TYPE_REPEAT_WARNING_DOTNET = "SD_VALUE_TYPE_REPEAT_WARNING_DOTNET";
public static final String SD_NO_TYPES_OR_CONTENTREF = "SD_NO_TYPES_OR_CONTENTREF";
public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG";

View File

@ -543,7 +543,7 @@ FHIRPATH_LOCATION = (at {0})
FHIRPATH_UNKNOWN_CONTEXT = Unknown context evaluating FHIRPath expression: {0}
FHIRPATH_UNKNOWN_CONTEXT_ELEMENT = Unknown context element evaluating FHIRPath expression: {0}
FHIRPATH_ALIAS_COLLECTION = Attempt to alias a collection, not a singleton evaluating FHIRPath expression
FHIRPATH_UNKNOWN_NAME = Error evaluating FHIRPath expression: The name {0} is not valid for any of the possible types: {2}
FHIRPATH_UNKNOWN_NAME = Error evaluating FHIRPath expression: The name {0} is not valid for any of the possible types: {1}
FHIRPATH_UNKNOWN_CONSTANT = Error evaluating FHIRPath expression: Invalid FHIR Constant {0}
FHIRPATH_CANNOT_USE = Error evaluating FHIRPath expression: Cannot use {0} in this context because {1}
FHIRPATH_CANT_COMPARE = Error evaluating FHIRPath expression: Unable to compare values of type {0} and {1}
@ -655,6 +655,8 @@ CODESYSTEM_CS_NO_SUPPLEMENT = CodeSystem {0} is a supplement, so can't be used a
CODESYSTEM_CS_SUPP_CANT_CHECK = CodeSystem {0} cannot be found, so can't check if concepts are valid
CODESYSTEM_CS_SUPP_INVALID_CODE = The code ''{1}'' is not declared in the base CodeSystem {0} so is not valid in the supplement
SD_VALUE_TYPE_IILEGAL = The element {0} has a {1} of type {2}, which is not in the list of allowed types ({3})
SD_VALUE_TYPE_REPEAT_HINT = The repeating element has a {1}. The {1} will apply to all the repeats (this has not been clear to all users)
SD_VALUE_TYPE_REPEAT_WARNING_DOTNET = The repeating element has a {1} value for a primitive type. The DotNet validator will not apply this to all the repeats - this is an error
SD_NO_TYPES_OR_CONTENTREF = The element {0} has no assigned types, and no content reference
CODESYSTEM_CS_UNK_EXPANSION = The code provided ({2}) is not in the value set {0}, and a code is required from this value set. The system {1} is unknown.
BUNDLE_SEARCH_NOSELF = SearchSet Bundles should have a self link that specifies what the search was

View File

@ -132,6 +132,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.DataRenderer;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.utils.BuildExtensions;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
@ -1686,6 +1687,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
} else if (SpecialExtensions.isKnownExtension(url)) {
ex = SpecialExtensions.getDefinition(url);
} else if (Utilities.existsInList(url, BuildExtensions.allConsts())) {
// nothing
} else if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, allowUnknownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN_NOTHERE, url)) {
hint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, isKnownExtension(url), I18nConstants.EXTENSION_EXT_UNKNOWN, url);
}
@ -1761,6 +1764,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
res.add(tr.getWorkingCode());
}
}
// special hacks
if (ex.getUrl().equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type")) {
res.add("uri");
res.add("url");
}
return res;
}

View File

@ -165,6 +165,9 @@ public class StructureDefinitionValidator extends BaseValidator {
// in a snapshot, we validate that fixedValue, pattern, and defaultValue, if present, are all of the right type
if (snapshot && (element.getIdBase() != null) && (element.getIdBase().contains("."))) {
if (rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), !typeCodes.isEmpty() || element.hasChild("contentReference"), I18nConstants.SD_NO_TYPES_OR_CONTENTREF, element.getIdBase())) {
// if we see fixed[x] or pattern[x] applied to a repeating element, we'll give the user a hint
boolean repeating = !Utilities.existsInList(element.getChildValue("max"), "0", "1");
Element v = element.getNamedChild("defaultValue");
if (v != null) {
rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "defaultValue", v.fhirType(), typeCodes);
@ -172,15 +175,31 @@ public class StructureDefinitionValidator extends BaseValidator {
v = element.getNamedChild("fixed");
if (v != null) {
rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "fixed", v.fhirType(), typeCodes);
hint(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_HINT, element.getIdBase(), "fixed");
if (isPrimitiveType(v.fhirType())) {
warning(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_WARNING_DOTNET, element.getIdBase(), "fixed");
}
}
v = element.getNamedChild("pattern");
if (v != null) {
rule(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), typeCodes.contains(v.fhirType()), I18nConstants.SD_VALUE_TYPE_IILEGAL, element.getIdBase(), "pattern", v.fhirType(), typeCodes);
hint(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_HINT, element.getIdBase(), "pattern");
if (isPrimitiveType(v.fhirType())) {
warning(errors, IssueType.EXCEPTION, stack.push(v, -1, null, null).getLiteralPath(), !repeating, I18nConstants.SD_VALUE_TYPE_REPEAT_WARNING_DOTNET, element.getIdBase(), "pattern");
}
}
}
// if we see fixed[x] or pattern[x] applied to a repeating element, we'll give the user a hint
}
}
private boolean isPrimitiveType(String fhirType) {
StructureDefinition sd = context.fetchTypeDefinition(fhirType);
return sd != null && sd.getKind() == StructureDefinitionKind.PRIMITIVETYPE;
}
private String boundType(Set<String> typeCodes) {
for (String tc : typeCodes) {
if (Utilities.existsInList(tc, "code", "Coding", "CodeableConcept", "Quantity", "CodeableReference")) {
@ -355,7 +374,7 @@ public class StructureDefinitionValidator extends BaseValidator {
private void validateTargetProfile(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
String p = profile.primitiveValue();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p);
if (code.equals("Reference")) {
if (code.equals("Reference") || code.equals("CodeableReference")) {
if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
StructureDefinition t = determineBaseType(sd);
if (t == null) {

View File

@ -234,7 +234,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
for (JsonElement je : content.getAsJsonArray("profiles")) {
String filename = je.getAsString();
String contents = TestingUtilities.loadTestResource("validator", filename);
StructureDefinition sd = loadProfile(filename, contents, messages);
StructureDefinition sd = loadProfile(filename, contents, messages, val.isDebug());
val.getContext().cacheResource(sd);
}
}
@ -293,7 +293,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
String contents = TestingUtilities.loadTestResource("validator", filename);
System.out.println("Name: " + name + " - profile : " + profile.get("source").getAsString());
version = content.has("version") ? content.get("version").getAsString() : Constants.VERSION;
sd = loadProfile(filename, contents, messages);
sd = loadProfile(filename, contents, messages, val.isDebug());
val.getContext().cacheResource(sd);
}
val.setAssumeValidRestReferences(profile.has("assumeValidRestReferences") ? profile.get("assumeValidRestReferences").getAsBoolean() : false);
@ -348,9 +348,10 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
return res;
}
public StructureDefinition loadProfile(String filename, String contents, List<ValidationMessage> messages) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
public StructureDefinition loadProfile(String filename, String contents, List<ValidationMessage> messages, boolean debug) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
StructureDefinition sd = (StructureDefinition) loadResource(filename, contents);
ProfileUtilities pu = new ProfileUtilities(TestingUtilities.getSharedWorkerContext(version), messages, null);
pu.setDebug(debug);
if (!sd.hasSnapshot()) {
StructureDefinition base = TestingUtilities.getSharedWorkerContext(version).fetchResource(StructureDefinition.class, sd.getBaseDefinition());
pu.generateSnapshot(base, sd, sd.getUrl(), null, sd.getTitle());

View File

@ -82,3 +82,25 @@ v: {
"system" : "urn:ietf:bcp:47"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
"code" : "IRCP",
"display" : "information recipient"
}, "url": "http://hl7.org/fhir/ValueSet/participation-role-type", "version": "4.5.0", "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "versionFlexible":"false"}####
v: {
"severity" : "error",
"error" : "Error from server: Unable to find value set http://hl7.org/fhir/ValueSet/provenance-participant-type",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-RoleClass",
"code" : "PROV",
"display" : "healthcare provider"
}, "url": "http://hl7.org/fhir/ValueSet/participation-role-type", "version": "4.5.0", "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"NO_MEMBERSHIP_CHECK", "versionFlexible":"false"}####
v: {
"severity" : "error",
"error" : "Error from server: Unable to find value set http://hl7.org/fhir/ValueSet/provenance-participant-type",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,22 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/security-source-type",
"code" : "4",
"display" : "Application Server"
}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "Application Server",
"code" : "4",
"system" : "http://terminology.hl7.org/CodeSystem/security-source-type"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/security-source-type",
"code" : "4"
}, "url": "http://hl7.org/fhir/ValueSet/audit-source-type--0", "version": "4.5.0", "lang":"null", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "Application Server",
"code" : "4",
"system" : "http://terminology.hl7.org/CodeSystem/security-source-type"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,11 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-ActReason",
"code" : "HTEST"
}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "test health data",
"code" : "HTEST",
"system" : "http://terminology.hl7.org/CodeSystem/v3-ActReason"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,21 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
"code" : "IRCP"
}, "url": "http://hl7.org/fhir/ValueSet/participation-role-type--5", "version": "4.5.0", "lang":"null", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "information recipient",
"code" : "IRCP",
"system" : "http://terminology.hl7.org/CodeSystem/v3-ParticipationType"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
"code" : "IRCP"
}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "information recipient",
"code" : "IRCP",
"system" : "http://terminology.hl7.org/CodeSystem/v3-ParticipationType"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,21 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-RoleClass",
"code" : "PROV"
}, "url": "http://hl7.org/fhir/ValueSet/participation-role-type--1", "version": "4.5.0", "lang":"null", "useServer":"true", "useClient":"false", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "healthcare provider",
"code" : "PROV",
"system" : "http://terminology.hl7.org/CodeSystem/v3-RoleClass"
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://terminology.hl7.org/CodeSystem/v3-RoleClass",
"code" : "PROV"
}, "valueSet" :null, "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
v: {
"display" : "healthcare provider",
"code" : "PROV",
"system" : "http://terminology.hl7.org/CodeSystem/v3-RoleClass"
}
-------------------------------------------------------------------------------------

View File

@ -19,7 +19,7 @@
<properties>
<hapi_fhir_version>5.4.0</hapi_fhir_version>
<validator_test_case_version>1.1.95</validator_test_case_version>
<validator_test_case_version>1.1.96-SNAPSHOT</validator_test_case_version>
<junit_jupiter_version>5.7.1</junit_jupiter_version>
<junit_platform_launcher_version>1.7.1</junit_platform_launcher_version>
<maven_surefire_version>3.0.0-M5</maven_surefire_version>