Merge pull request #782 from hapifhir/gg-202204-various

various fixes
This commit is contained in:
Grahame Grieve 2022-04-06 09:28:09 +10:00 committed by GitHub
commit 76411218ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 689 additions and 366 deletions

1
.gitignore vendored
View File

@ -293,7 +293,6 @@ local.properties
/org.hl7.fhir.r5/src/test/resources/snapshot-generation/t9-actual.xml /org.hl7.fhir.r5/src/test/resources/snapshot-generation/t9-actual.xml
/org.hl7.fhir.r5/src/test/resources/snapshot-generation/dv1-actual.xml /org.hl7.fhir.r5/src/test/resources/snapshot-generation/dv1-actual.xml
/org.hl7.fhir.r5/src/test/resources/snapshot-generation/t45-actual.xml /org.hl7.fhir.r5/src/test/resources/snapshot-generation/t45-actual.xml
/org.hl7.fhir.r4b
/org.hl7.fhir.r5/src/test/resources/gen/gen.xml /org.hl7.fhir.r5/src/test/resources/gen/gen.xml
/org.hl7.fhir.r5/src/test/resources/graphql/*.out /org.hl7.fhir.r5/src/test/resources/graphql/*.out
/org.hl7.fhir.validation/src/test/resources/comparison/output /org.hl7.fhir.validation/src/test/resources/comparison/output

View File

@ -1,6 +1,10 @@
## Validator Changes ## 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 ## Other code changes

View File

@ -1168,7 +1168,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)) { 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())); 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); CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
if (sd == null && xver != null && xver.matchingUrl(p.getValue())) { if (sd == null && xver != null && xver.matchingUrl(p.getValue())) {
@ -1928,6 +1936,15 @@ public class ProfileUtilities extends TranslatingUtilities {
return res; 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) { private void updateConstraintSources(ElementDefinition ed, String url) {
for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { for (ElementDefinitionConstraintComponent c : ed.getConstraint()) {
if (!c.hasSource()) { if (!c.hasSource()) {
@ -2507,19 +2524,22 @@ public class ProfileUtilities extends TranslatingUtilities {
if (webUrl != null) { if (webUrl != null) {
// also, must touch up the markdown // also, must touch up the markdown
if (element.hasDefinition()) if (element.hasDefinition())
element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); element.setDefinition(processRelativeUrls(element.getDefinition(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
if (element.hasComment()) if (element.hasComment())
element.setComment(processRelativeUrls(element.getComment(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); element.setComment(processRelativeUrls(element.getComment(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
if (element.hasRequirements()) if (element.hasRequirements())
element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); element.setRequirements(processRelativeUrls(element.getRequirements(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
if (element.hasMeaningWhenMissing()) if (element.hasMeaningWhenMissing())
element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); element.setMeaningWhenMissing(processRelativeUrls(element.getMeaningWhenMissing(), webUrl, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, false));
} }
} }
return element; return element;
} }
public static String processRelativeUrls(String markdown, String webUrl, String basePath, List<String> resourceNames, Set<String> filenames, boolean processRelatives) { public static String processRelativeUrls(String markdown, String webUrl, String basePath, List<String> resourceNames, Set<String> baseFilenames, Set<String> localFilenames, boolean processRelatives) {
if (markdown == null) {
return "";
}
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
int i = 0; int i = 0;
while (i < markdown.length()) { while (i < markdown.length()) {
@ -2540,7 +2560,7 @@ public class ProfileUtilities extends TranslatingUtilities {
// This code is trying to guess which relative references are actually to the // This code is trying to guess which relative references are actually to the
// base specification. // base specification.
// //
if (isLikelySourceURLReference(url, resourceNames, filenames)) { if (isLikelySourceURLReference(url, resourceNames, baseFilenames, localFilenames)) {
b.append("]("); b.append("](");
b.append(basePath); b.append(basePath);
i = i + 1; i = i + 1;
@ -2549,7 +2569,7 @@ public class ProfileUtilities extends TranslatingUtilities {
// disabled 7-Dec 2021 GDG - we don't want to fool with relative URLs at all? // disabled 7-Dec 2021 GDG - we don't want to fool with relative URLs at all?
// re-enabled 11-Feb 2022 GDG - we do want to do this. At least, $assemble in davinci-dtr, where the markdown comes from the SDC IG, and an SDC local reference must be changed to point to SDC. in this case, it's called when generating snapshots // re-enabled 11-Feb 2022 GDG - we do want to do this. At least, $assemble in davinci-dtr, where the markdown comes from the SDC IG, and an SDC local reference must be changed to point to SDC. in this case, it's called when generating snapshots
// added processRelatives parameter to deal with this (well, to try) // added processRelatives parameter to deal with this (well, to try)
if (processRelatives && webUrl != null) { if (processRelatives && webUrl != null && !issLocalFileName(url, localFilenames)) {
// System.out.println("Making "+url+" relative to '"+webUrl+"'"); // System.out.println("Making "+url+" relative to '"+webUrl+"'");
b.append(webUrl); b.append(webUrl);
} else { } else {
@ -2570,7 +2590,19 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
public static boolean isLikelySourceURLReference(String url, List<String> resourceNames, Set<String> filenames) { public static boolean issLocalFileName(String url, Set<String> localFilenames) {
if (localFilenames != null) {
for (String n : localFilenames) {
if (url.startsWith(n.toLowerCase())) {
return true;
}
}
}
return false;
}
public static boolean isLikelySourceURLReference(String url, List<String> resourceNames, Set<String> baseFilenames, Set<String> localFilenames) {
if (resourceNames != null) { if (resourceNames != null) {
for (String n : resourceNames) { for (String n : resourceNames) {
if (url.startsWith(n.toLowerCase()+".html")) { if (url.startsWith(n.toLowerCase()+".html")) {
@ -2581,8 +2613,15 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
} }
} }
if (filenames != null) { if (localFilenames != null) {
for (String n : filenames) { for (String n : localFilenames) {
if (url.startsWith(n.toLowerCase())) {
return false;
}
}
}
if (baseFilenames != null) {
for (String n : baseFilenames) {
if (url.startsWith(n.toLowerCase())) { if (url.startsWith(n.toLowerCase())) {
return true; return true;
} }
@ -2814,7 +2853,9 @@ public class ProfileUtilities extends TranslatingUtilities {
ElementDefinition e = profile.getSnapshot().getElement().get(0); ElementDefinition e = profile.getSnapshot().getElement().get(0);
String webroot = profile.getUserString("webroot"); String webroot = profile.getUserString("webroot");
base.setDefinition(processRelativeUrls(e.getDefinition(), webroot, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, true)); if (e.hasDefinition()) {
base.setDefinition(processRelativeUrls(e.getDefinition(), webroot, baseSpecUrl(), context.getResourceNames(), masterSourceFileNames, null, true));
}
base.setShort(e.getShort()); base.setShort(e.getShort());
if (e.hasCommentElement()) if (e.hasCommentElement())
base.setCommentElement(e.getCommentElement()); base.setCommentElement(e.getCommentElement());
@ -5247,7 +5288,9 @@ public class ProfileUtilities extends TranslatingUtilities {
} }
private String tail(String path) { private String tail(String path) {
if (path.contains(".")) if (path == null) {
return "";
} else if (path.contains("."))
return path.substring(path.lastIndexOf('.')+1); return path.substring(path.lastIndexOf('.')+1);
else else
return path; return path;

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)) { 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())); 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); CanonicalType p = diffMatches.get(0).getType().get(0).getProfile().get(0);
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue()); StructureDefinition sd = context.fetchResource(StructureDefinition.class, p.getValue());
if (sd == null && xver != null && xver.matchingUrl(p.getValue())) { if (sd == null && xver != null && xver.matchingUrl(p.getValue())) {
@ -1932,6 +1940,15 @@ public class ProfileUtilities extends TranslatingUtilities {
return res; 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) { private void updateConstraintSources(ElementDefinition ed, String url) {
for (ElementDefinitionConstraintComponent c : ed.getConstraint()) { for (ElementDefinitionConstraintComponent c : ed.getConstraint()) {
if (!c.hasSource()) { if (!c.hasSource()) {
@ -4844,6 +4861,8 @@ public class ProfileUtilities extends TranslatingUtilities {
String valueSet = ext.getExtensionString("valueSet"); String valueSet = ext.getExtensionString("valueSet");
String doco = ext.getExtensionString("documentation"); String doco = ext.getExtensionString("documentation");
UsageContext usage = (ext.hasExtension("usage")) ? ext.getExtensionByUrl("usage").getValueUsageContext() : null; 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 // purpose: code - defines how the binding is used
// usage : UsageContext - defines the contexts in which this binding is used for it's purpose // 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) { private String tail(String path) {
if (path.contains(".")) if (path == null) {
return "";
} else if (path.contains("."))
return path.substring(path.lastIndexOf('.')+1); return path.substring(path.lastIndexOf('.')+1);
else else
return path; 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, Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: 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. 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 this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. 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 endorse or promote products derived from this software without specific
prior written permission. prior written permission.
@ -408,8 +408,8 @@ public class FHIRPathEngine {
* @param item * @param item
* @param name * @param name
* @param result * @param result
* @throws FHIRException * @throws FHIRException
*/ */
protected void getChildrenByName(Base item, String name, List<Base> result) throws FHIRException { protected void getChildrenByName(Base item, String name, List<Base> result) throws FHIRException {
String tn = null; String tn = null;
if (isAllowPolymorphicNames()) { if (isAllowPolymorphicNames()) {
@ -640,10 +640,10 @@ public class FHIRPathEngine {
} else if (left.getHour() > right.getHour()) { } else if (left.getHour() > right.getHour()) {
return 1; return 1;
// hour is not a valid precision // hour is not a valid precision
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) { // } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.YEAR && dateRight.getPrecision() == TemporalPrecisionEnum.YEAR) {
// return 0; // return 0;
// } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) { // } else if (dateLeft.getPrecision() == TemporalPrecisionEnum.HOUR || dateRight.getPrecision() == TemporalPrecisionEnum.HOUR) {
// return null; // return null;
} }
if (left.getMinute() < right.getMinute()) { if (left.getMinute() < right.getMinute()) {
@ -676,7 +676,7 @@ public class FHIRPathEngine {
* @throws FHIRException * @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>(); List<Base> list = new ArrayList<Base>();
if (base != null) { if (base != null) {
list.add(base); list.add(base);
@ -691,10 +691,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated * @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use * @param path - the FHIR Path statement to use
* @return * @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); ExpressionNode exp = parse(path);
List<Base> list = new ArrayList<Base>(); List<Base> list = new ArrayList<Base>();
if (base != null) { if (base != null) {
@ -710,10 +710,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated * @param base - the object against which the path is being evaluated
* @param ExpressionNode - the parsed ExpressionNode statement to use * @param ExpressionNode - the parsed ExpressionNode statement to use
* @return * @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>(); List<Base> list = new ArrayList<Base>();
if (base != null) { if (base != null) {
list.add(base); list.add(base);
@ -746,10 +746,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated * @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use * @param path - the FHIR Path statement to use
* @return * @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); ExpressionNode exp = parse(path);
List<Base> list = new ArrayList<Base>(); List<Base> list = new ArrayList<Base>();
if (base != null) { if (base != null) {
@ -765,10 +765,10 @@ public class FHIRPathEngine {
* @param base - the object against which the path is being evaluated * @param base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use * @param path - the FHIR Path statement to use
* @return * @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)); 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 base - the object against which the path is being evaluated
* @param path - the FHIR Path statement to use * @param path - the FHIR Path statement to use
* @return * @return
* @throws FHIRException * @throws FHIRException
* @ * @
*/ */
public String evaluateToString(Base base, String path) throws FHIRException { public String evaluateToString(Base base, String path) throws FHIRException {
@ -935,7 +935,7 @@ public class FHIRPathEngine {
return focusResource; return focusResource;
} }
public Base getRootResource() { public Base getRootResource() {
return rootResource; return rootResource;
} }
public Base getThisItem() { public Base getThisItem() {
return thisItem; return thisItem;
@ -1386,8 +1386,8 @@ public class FHIRPathEngine {
return false; return false;
} }
private List<Base> execute(ExecutionContext context, List<Base> focus, ExpressionNode exp, boolean atEntry) throws FHIRException { private List<Base> execute(ExecutionContext context, List<Base> focus, ExpressionNode exp, boolean atEntry) throws FHIRException {
// System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString()); // System.out.println("Evaluate {'"+exp.toString()+"'} on "+focus.toString());
List<Base> work = new ArrayList<Base>(); List<Base> work = new ArrayList<Base>();
switch (exp.getKind()) { switch (exp.getKind()) {
case Unary: case Unary:
@ -1441,13 +1441,13 @@ public class FHIRPathEngine {
} else { } else {
work2 = execute(context, focus, next, true); work2 = execute(context, focus, next, true);
work = operate(context, work, last.getOperation(), work2, last); 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; last = next;
next = next.getOpNext(); next = next.getOpNext();
} }
} }
// System.out.println("Result of {'"+exp.toString()+"'}: "+work.toString()); // System.out.println("Result of {'"+exp.toString()+"'}: "+work.toString());
return work; return work;
} }
@ -1972,7 +1972,7 @@ public class FHIRPathEngine {
} else if (left instanceof DecimalType || right instanceof DecimalType) { } else if (left instanceof DecimalType || right instanceof DecimalType) {
return decEqual(left.primitiveValue(), right.primitiveValue()); return decEqual(left.primitiveValue(), right.primitiveValue());
} else if (left.isPrimitive() && right.isPrimitive()) { } else if (left.isPrimitive() && right.isPrimitive()) {
return Base.equals(left.primitiveValue(), right.primitiveValue()); return Base.equals(left.primitiveValue(), right.primitiveValue());
} else { } else {
return Base.compareDeep(left, right, false); 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 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) if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>(); return new ArrayList<Base>();
@ -2231,7 +2231,7 @@ public class FHIRPathEngine {
return new ArrayList<Base>(); 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) if (left.size() == 0 || right.size() == 0)
return new ArrayList<Base>(); return new ArrayList<Base>();
if (left.size() == 1 && right.size() == 1 && left.get(0).isPrimitive() && right.get(0).isPrimitive()) { 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>(); 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) { if (left.size() == 0 || right.size() == 0) {
return new ArrayList<Base>(); return new ArrayList<Base>();
} }
@ -2328,7 +2328,7 @@ public class FHIRPathEngine {
return new ArrayList<Base>(); 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) { if (left.size() == 0 || right.size() == 0) {
return new ArrayList<Base>(); return new ArrayList<Base>();
} }
@ -2376,34 +2376,34 @@ public class FHIRPathEngine {
return new ArrayList<Base>(); return new ArrayList<Base>();
} }
private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { private List<Base> opMemberOf(ExecutionContext context, List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
boolean ans = false; boolean ans = false;
String url = right.get(0).primitiveValue(); String url = right.get(0).primitiveValue();
ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url); ValueSet vs = hostServices != null ? hostServices.resolveValueSet(context.appInfo, url) : worker.fetchResource(ValueSet.class, url);
if (vs != null) { if (vs != null) {
for (Base l : left) { for (Base l : left) {
if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) { if (Utilities.existsInList(l.fhirType(), "code", "string", "uri")) {
if (worker.validateCode(terminologyServiceOptions.guessSystem() , TypeConvertor.castToCoding(l), vs).isOk()) { if (worker.validateCode(terminologyServiceOptions.guessSystem() , TypeConvertor.castToCoding(l), vs).isOk()) {
ans = true; ans = true;
} }
} else if (l.fhirType().equals("Coding")) { } else if (l.fhirType().equals("Coding")) {
if (worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCoding(l), vs).isOk()) { if (worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCoding(l), vs).isOk()) {
ans = true; ans = true;
} }
} else if (l.fhirType().equals("CodeableConcept")) { } else if (l.fhirType().equals("CodeableConcept")) {
CodeableConcept cc = TypeConvertor.castToCodeableConcept(l); CodeableConcept cc = TypeConvertor.castToCodeableConcept(l);
ValidationResult vr = worker.validateCode(terminologyServiceOptions, cc, vs); ValidationResult vr = worker.validateCode(terminologyServiceOptions, cc, vs);
// System.out.println("~~~ "+DataRenderer.display(worker, cc)+ " memberOf "+url+": "+vr.toString()); // System.out.println("~~~ "+DataRenderer.display(worker, cc)+ " memberOf "+url+": "+vr.toString());
if (vr.isOk()) { if (vr.isOk()) {
ans = true; ans = true;
} }
} else { } else {
// System.out.println("unknown type in opMemberOf: "+l.fhirType()); // System.out.println("unknown type in opMemberOf: "+l.fhirType());
} }
} }
} }
return makeBoolean(ans); return makeBoolean(ans);
} }
private List<Base> opIn(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException { private List<Base> opIn(List<Base> left, List<Base> right, ExpressionNode expr) throws FHIRException {
if (left.size() == 0) { if (left.size() == 0) {
@ -2432,7 +2432,7 @@ public class FHIRPathEngine {
private List<Base> opContains(List<Base> left, List<Base> right, ExpressionNode expr) { private List<Base> opContains(List<Base> left, List<Base> right, ExpressionNode expr) {
if (left.size() == 0 || right.size() == 0) { if (left.size() == 0 || right.size() == 0) {
return new ArrayList<Base>(); return new ArrayList<Base>();
} }
boolean ans = true; boolean ans = true;
for (Base r : right) { 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>(); List<Base> result = new ArrayList<Base>();
if (atEntry && context.appInfo != null && hostServices != null) { if (atEntry && context.appInfo != null && hostServices != null) {
// we'll see if the name matches a constant known by the context. // we'll see if the name matches a constant known by the context.
@ -2946,7 +2946,7 @@ public class FHIRPathEngine {
return result; 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()); StructureDefinition sd = worker.fetchTypeDefinition(item.fhirType());
if (sd == null) { if (sd == null) {
// logical model // logical model
@ -3006,12 +3006,14 @@ public class FHIRPathEngine {
if (exp.getFunction() == Function.Is || exp.getFunction() == Function.As || exp.getFunction() == Function.OfType) { if (exp.getFunction() == Function.Is || exp.getFunction() == Function.As || exp.getFunction() == Function.OfType) {
paramTypes.add(new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String)); paramTypes.add(new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
} else { } else {
int i = 0;
for (ExpressionNode expr : exp.getParameters()) { 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)); paramTypes.add(executeType(changeThis(context, focus), focus, expr, true));
} else { } else {
paramTypes.add(executeType(context, focus, expr, true)); paramTypes.add(executeType(context, context.thisItem, expr, true));
} }
i++;
} }
} }
switch (exp.getFunction()) { switch (exp.getFunction()) {
@ -3344,6 +3346,17 @@ public class FHIRPathEngine {
throw new Error("not Implemented yet"); 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 { private void checkParamTypes(ExpressionNode expr, String funcName, List<TypeDetails> paramTypes, TypeDetails... typeSet) throws PathEngineException {
int i = 0; int i = 0;
@ -3390,9 +3403,9 @@ public class FHIRPathEngine {
private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException { private void checkContextPrimitive(TypeDetails focus, String name, boolean canQty, ExpressionNode expr) throws PathEngineException {
if (canQty) { if (canQty) {
if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) { if (!focus.hasType(primitiveTypes) && !focus.hasType("Quantity")) {
throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString()); throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), "Quantity, "+primitiveTypes.toString());
} }
} else if (!focus.hasType(primitiveTypes)) { } else if (!focus.hasType(primitiveTypes)) {
throw makeException(expr, I18nConstants.FHIRPATH_PRIMITIVE_ONLY, name, focus.describe(), primitiveTypes.toString()); 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"); // 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()) { switch (exp.getFunction()) {
case Empty : return funcEmpty(context, focus, exp); case Empty : return funcEmpty(context, focus, exp);
case Not : return funcNot(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) { if (focus.size() != 1) {
throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size()); throw makeException(expr, I18nConstants.FHIRPATH_FOCUS_PLURAL, "sqrt", focus.size());
} }
Base base = focus.get(0); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try { try {
result.add(new DecimalType(Math.sqrt(d))); result.add(new DecimalType(Math.sqrt(d)));
} catch (Exception e) { } catch (Exception e) {
// just return nothing // just return nothing
} }
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal");
} }
return result; return result;
} }
private List<Base> funcAbs(ExecutionContext context, List<Base> focus, ExpressionNode expr) { private List<Base> funcAbs(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
@ -3561,12 +3574,12 @@ public class FHIRPathEngine {
Base base = focus.get(0); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try { try {
result.add(new DecimalType(Math.abs(d))); result.add(new DecimalType(Math.abs(d)));
} catch (Exception e) { } catch (Exception e) {
// just return nothing // just return nothing
} }
} else if (base.hasType("Quantity")) { } else if (base.hasType("Quantity")) {
Quantity qty = (Quantity) base; Quantity qty = (Quantity) base;
result.add(qty.copy().setValue(qty.getValue().abs())); result.add(qty.copy().setValue(qty.getValue().abs()));
@ -3584,11 +3597,11 @@ public class FHIRPathEngine {
Base base = focus.get(0); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try {result.add(new IntegerType((int) Math.ceil(d))); try {result.add(new IntegerType((int) Math.ceil(d)));
} catch (Exception e) { } catch (Exception e) {
// just return nothing // just return nothing
} }
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ceiling", "(focus)", base.fhirType(), "integer or decimal"); 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); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try { try {
result.add(new IntegerType((int) Math.floor(d))); result.add(new IntegerType((int) Math.floor(d)));
} catch (Exception e) { } catch (Exception e) {
// just return nothing // just return nothing
} }
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "floor", "(focus)", base.fhirType(), "integer or decimal"); 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); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try { try {
result.add(new DecimalType(Math.exp(d))); result.add(new DecimalType(Math.exp(d)));
} catch (Exception e) { } catch (Exception e) {
// just return nothing // just return nothing
} }
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "exp", "(focus)", base.fhirType(), "integer or decimal"); 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); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try { try {
result.add(new DecimalType(Math.log(d))); result.add(new DecimalType(Math.log(d)));
} catch (Exception e) { } catch (Exception e) {
// just return nothing // just return nothing
} }
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "ln", "(focus)", base.fhirType(), "integer or decimal"); 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"); throw makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "0", "Multiple Values", "integer or decimal");
} }
Double e = Double.parseDouble(n1.get(0).primitiveValue()); Double e = Double.parseDouble(n1.get(0).primitiveValue());
Double d = Double.parseDouble(base.primitiveValue()); Double d = Double.parseDouble(base.primitiveValue());
try { try {
result.add(new DecimalType(Math.pow(d, e))); result.add(new DecimalType(Math.pow(d, e)));
} catch (Exception ex) { } catch (Exception ex) {
// just return nothing // just return nothing
} }
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "power", "(focus)", base.fhirType(), "integer or decimal"); 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); Base base = focus.get(0);
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) { if (base.hasType("integer", "decimal", "unsignedInt", "positiveInt")) {
String s = base.primitiveValue(); String s = base.primitiveValue();
if (s.contains(".")) { if (s.contains(".")) {
s = s.substring(0, s.indexOf(".")); s = s.substring(0, s.indexOf("."));
} }
result.add(new IntegerType(s)); result.add(new IntegerType(s));
} else { } else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "integer or decimal"); 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(); private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) { public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2]; char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) { for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF; int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4]; hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
} }
return new String(hexChars); return new String(hexChars);
} }
public static byte[] hexStringToByteArray(String s) { public static byte[] hexStringToByteArray(String s) {
int len = s.length(); int len = s.length();
byte[] data = new byte[len / 2]; byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 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)); data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
} }
return data; 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); List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
String param = nl.get(0).primitiveValue(); String param = nl.get(0).primitiveValue();
@ -3788,7 +3801,7 @@ public class FHIRPathEngine {
} }
} }
return result; return result;
} }
private List<Base> funcDecode(ExecutionContext context, List<Base> focus, ExpressionNode exp) { private List<Base> funcDecode(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> nl = execute(context, focus, exp.getParameters().get(0), true); List<Base> nl = execute(context, focus, exp.getParameters().get(0), true);
@ -4047,7 +4060,7 @@ public class FHIRPathEngine {
} else if (l.fhirType().equals("CodeableConcept")) { } else if (l.fhirType().equals("CodeableConcept")) {
return makeBoolean(worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCodeableConcept(l), vs).isOk()); return makeBoolean(worker.validateCode(terminologyServiceOptions, TypeConvertor.castToCodeableConcept(l), vs).isOk());
} else { } else {
// System.out.println("unknown type in funcMemberOf: "+l.fhirType()); // System.out.println("unknown type in funcMemberOf: "+l.fhirType());
return new ArrayList<Base>(); return new ArrayList<Base>();
} }
} }
@ -4190,18 +4203,18 @@ public class FHIRPathEngine {
} }
private List<Base> funcToDateTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) { private List<Base> funcToDateTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
// List<Base> result = new ArrayList<Base>(); // List<Base> result = new ArrayList<Base>();
// result.add(new BooleanType(convertToBoolean(focus))); // result.add(new BooleanType(convertToBoolean(focus)));
// return result; // return result;
throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime"); throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toDateTime");
} }
private List<Base> funcToTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) { private List<Base> funcToTime(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
// List<Base> result = new ArrayList<Base>(); // List<Base> result = new ArrayList<Base>();
// result.add(new BooleanType(convertToBoolean(focus))); // result.add(new BooleanType(convertToBoolean(focus)));
// return result; // return result;
throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime"); throw makeException(expr, I18nConstants.FHIRPATH_NOT_IMPLEMENTED, "toTime");
} }
private List<Base> funcToDecimal(ExecutionContext context, List<Base> focus, ExpressionNode expr) { private List<Base> funcToDecimal(ExecutionContext context, List<Base> focus, ExpressionNode expr) {
@ -4253,7 +4266,7 @@ public class FHIRPathEngine {
result.add(item); 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)) { if (!doContains(result, item)) {
result.add(item); result.add(item);
} }
@ -4402,10 +4415,21 @@ public class FHIRPathEngine {
pc.add(item); pc.add(item);
added.addAll(execute(changeThis(context, item), pc, exp.getParameters().get(0), false)); added.addAll(execute(changeThis(context, item), pc, exp.getParameters().get(0), false));
} }
more = !added.isEmpty(); more = false;
result.addAll(added);
current.clear(); 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; return result;
} }
@ -4593,125 +4617,125 @@ public class FHIRPathEngine {
return result; return result;
} }
private List<Base> funcAllFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { private List<Base> funcAllFalse(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) { if (exp.getParameters().size() == 1) {
boolean all = true; boolean all = true;
List<Base> pc = new ArrayList<Base>(); List<Base> pc = new ArrayList<Base>();
for (Base item : focus) { for (Base item : focus) {
pc.clear(); pc.clear();
pc.add(item); pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true); List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp); 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);
if (v != Equality.False) { if (v != Equality.False) {
all = false; all = false;
break; break;
} }
} }
result.add(new BooleanType(all).noExtensions()); result.add(new BooleanType(all).noExtensions());
} } else {
return result; 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 { Equality v = asBool(item, true);
List<Base> result = new ArrayList<Base>(); if (v != Equality.False) {
if (exp.getParameters().size() == 1) { all = false;
boolean any = false; break;
List<Base> pc = new ArrayList<Base>(); }
for (Base item : focus) { }
pc.clear(); result.add(new BooleanType(all).noExtensions());
pc.add(item); }
List<Base> res = execute(context, pc, exp.getParameters().get(0), true); return result;
Equality v = asBool(res, exp); }
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) { if (v == Equality.False) {
any = true; any = true;
break; break;
} }
} }
result.add(new BooleanType(any).noExtensions()); result.add(new BooleanType(any).noExtensions());
} else { } else {
boolean any = false; boolean any = false;
for (Base item : focus) { for (Base item : focus) {
if (!canConvertToBoolean(item)) { if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); 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) { if (v == Equality.False) {
any = true; any = true;
break; break;
} }
} }
result.add(new BooleanType(any).noExtensions()); result.add(new BooleanType(any).noExtensions());
} }
return result; return result;
} }
private List<Base> funcAllTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { private List<Base> funcAllTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) { if (exp.getParameters().size() == 1) {
boolean all = true; boolean all = true;
List<Base> pc = new ArrayList<Base>(); List<Base> pc = new ArrayList<Base>();
for (Base item : focus) { for (Base item : focus) {
pc.clear(); pc.clear();
pc.add(item); pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true); List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp); Equality v = asBool(res, exp);
if (v != Equality.True) { if (v != Equality.True) {
all = false; all = false;
break; break;
} }
} }
result.add(new BooleanType(all).noExtensions()); result.add(new BooleanType(all).noExtensions());
} else { } else {
boolean all = true; boolean all = true;
for (Base item : focus) { for (Base item : focus) {
if (!canConvertToBoolean(item)) { if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
} }
Equality v = asBool(item, true); Equality v = asBool(item, true);
if (v != Equality.True) { if (v != Equality.True) {
all = false; all = false;
break; break;
} }
} }
result.add(new BooleanType(all).noExtensions()); result.add(new BooleanType(all).noExtensions());
} }
return result; return result;
} }
private List<Base> funcAnyTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException { private List<Base> funcAnyTrue(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (exp.getParameters().size() == 1) { if (exp.getParameters().size() == 1) {
boolean any = false; boolean any = false;
List<Base> pc = new ArrayList<Base>(); List<Base> pc = new ArrayList<Base>();
for (Base item : focus) { for (Base item : focus) {
pc.clear(); pc.clear();
pc.add(item); pc.add(item);
List<Base> res = execute(context, pc, exp.getParameters().get(0), true); List<Base> res = execute(context, pc, exp.getParameters().get(0), true);
Equality v = asBool(res, exp); Equality v = asBool(res, exp);
if (v == Equality.True) { if (v == Equality.True) {
any = true; any = true;
break; break;
} }
} }
result.add(new BooleanType(any).noExtensions()); result.add(new BooleanType(any).noExtensions());
} else { } else {
boolean any = false; boolean any = false;
for (Base item : focus) { for (Base item : focus) {
if (!canConvertToBoolean(item)) { if (!canConvertToBoolean(item)) {
throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean");
@ -4719,16 +4743,16 @@ public class FHIRPathEngine {
Equality v = asBool(item, true); Equality v = asBool(item, true);
if (v == Equality.True) { if (v == Equality.True) {
any = true; any = true;
break; break;
} }
} }
result.add(new BooleanType(any).noExtensions()); result.add(new BooleanType(any).noExtensions());
} }
return result; return result;
} }
private boolean canConvertToBoolean(Base item) { private boolean canConvertToBoolean(Base item) {
return (item.isBooleanPrimitive()); return (item.isBooleanPrimitive());
} }
@ -4778,7 +4802,7 @@ public class FHIRPathEngine {
return result; 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>(); List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
@ -4798,29 +4822,30 @@ public class FHIRPathEngine {
return result; return result;
} }
private List<Base> funcMatchesFull(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>(); List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); 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) { if (focus.size() != 1) {
result.add(new BooleanType(false).noExtensions()); result.add(new BooleanType(false).noExtensions());
} else if (Utilities.noString(sw)) { } else if (Utilities.noString(sw)) {
@ -4836,6 +4861,12 @@ public class FHIRPathEngine {
return result; 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) { private List<Base> funcLength(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
if (focus.size() == 1) { if (focus.size() == 1) {
@ -4856,7 +4887,7 @@ public class FHIRPathEngine {
return result; 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>(); List<Base> result = new ArrayList<Base>();
String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true)); String sw = convertToString(execute(context, focus, exp.getParameters().get(0), true));
@ -4927,7 +4958,7 @@ public class FHIRPathEngine {
return result; 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> result = new ArrayList<Base>();
List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true); List<Base> n1 = execute(context, focus, exp.getParameters().get(0), true);
int i1 = Integer.parseInt(n1.get(0).primitiveValue()); 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> result = new ArrayList<Base>();
List<Base> pc = new ArrayList<Base>(); List<Base> pc = new ArrayList<Base>();
for (Base item : focus) { 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>(); List<Base> result = new ArrayList<Base>();
String s = convertToString(execute(context, focus, exp.getParameters().get(0), true)); String s = convertToString(execute(context, focus, exp.getParameters().get(0), true));
if (Utilities.isInteger(s) && Integer.parseInt(s) < focus.size()) { 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) { private List<Base> funcEmpty(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> result = new ArrayList<Base>(); List<Base> result = new ArrayList<Base>();
result.add(new BooleanType(ElementUtil.isEmpty(focus)).noExtensions()); result.add(new BooleanType(ElementUtil.isEmpty(focus)).noExtensions());
return result; return result;
} }
@ -5576,7 +5607,7 @@ public class FHIRPathEngine {
if (t.getPath().endsWith(".extension") && t.hasSliceName()) { if (t.getPath().endsWith(".extension") && t.hasSliceName()) {
System.out.println("t: "+t.getId()); System.out.println("t: "+t.getId());
StructureDefinition exsd = (t.getType() == null || t.getType().isEmpty() || t.getType().get(0).getProfile().isEmpty()) ? 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")) { while (exsd != null && !exsd.getBaseDefinition().equals("http://hl7.org/fhir/StructureDefinition/Extension")) {
exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition()); exsd = worker.fetchResource(StructureDefinition.class, exsd.getBaseDefinition());
} }

View File

@ -1,5 +1,8 @@
package org.hl7.fhir.r5.utils; package org.hl7.fhir.r5.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -61,6 +64,7 @@ POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -941,5 +945,21 @@ public class ToolingExtensions {
return false; 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_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_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_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 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_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG";
public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_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 = Unknown context evaluating FHIRPath expression: {0}
FHIRPATH_UNKNOWN_CONTEXT_ELEMENT = Unknown context element 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_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_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_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} 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_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 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_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 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. 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 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.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.DataRenderer; import org.hl7.fhir.r5.renderers.DataRenderer;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass; 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.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
@ -1686,6 +1687,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} else if (SpecialExtensions.isKnownExtension(url)) { } else if (SpecialExtensions.isKnownExtension(url)) {
ex = SpecialExtensions.getDefinition(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)) { } 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); 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()); 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; 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 // 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 (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 (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"); Element v = element.getNamedChild("defaultValue");
if (v != null) { 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); 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"); v = element.getNamedChild("fixed");
if (v != null) { 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); 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"); v = element.getNamedChild("pattern");
if (v != null) { 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); 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) { private String boundType(Set<String> typeCodes) {
for (String tc : typeCodes) { for (String tc : typeCodes) {
if (Utilities.existsInList(tc, "code", "Coding", "CodeableConcept", "Quantity", "CodeableReference")) { 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) { private void validateTargetProfile(List<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
String p = profile.primitiveValue(); String p = profile.primitiveValue();
StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); 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)) { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) {
StructureDefinition t = determineBaseType(sd); StructureDefinition t = determineBaseType(sd);
if (t == null) { if (t == null) {

View File

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

View File

@ -82,3 +82,25 @@ v: {
"system" : "urn:ietf:bcp:47" "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> <properties>
<hapi_fhir_version>5.4.0</hapi_fhir_version> <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_jupiter_version>5.7.1</junit_jupiter_version>
<junit_platform_launcher_version>1.7.1</junit_platform_launcher_version> <junit_platform_launcher_version>1.7.1</junit_platform_launcher_version>
<maven_surefire_version>3.0.0-M5</maven_surefire_version> <maven_surefire_version>3.0.0-M5</maven_surefire_version>