diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 9622adf3d..76df95f1f 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,2 +1,6 @@
-Other changes:
-* Fix trailing slashes for JAVA_HOME tests
+Validator:
+* fix bug in client caching for unidentified value sets
+* look for codesystem definitions in local context
+* fix path error in Questionnaire Response validation
+* fix up r4b list of canonical resource types
+* better version specific resolution of special canonical resources
\ No newline at end of file
diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml
index 7f0e5b37f..7277bef27 100644
--- a/org.hl7.fhir.convertors/pom.xml
+++ b/org.hl7.fhir.convertors/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml
index a67967b5e..306b161f2 100644
--- a/org.hl7.fhir.dstu2/pom.xml
+++ b/org.hl7.fhir.dstu2/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml
index b375c8ed4..a696de0b8 100644
--- a/org.hl7.fhir.dstu2016may/pom.xml
+++ b/org.hl7.fhir.dstu2016may/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml
index 5177f99c7..37ed55327 100644
--- a/org.hl7.fhir.dstu3/pom.xml
+++ b/org.hl7.fhir.dstu3/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml
index 72cee3441..d52d47b9f 100644
--- a/org.hl7.fhir.r4/pom.xml
+++ b/org.hl7.fhir.r4/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml
index 42cce980e..b29cf71fb 100644
--- a/org.hl7.fhir.r5/pom.xml
+++ b/org.hl7.fhir.r5/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java
index f504f22d8..188176752 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java
@@ -691,8 +691,8 @@ public class ProfileUtilities extends TranslatingUtilities {
}
if (!e.hasUserData(GENERATED_IN_SNAPSHOT)) {
b.append(e.hasId() ? "id: "+e.getId() : "path: "+e.getPath());
+ ce++;
if (e.hasId()) {
- ce++;
String msg = "No match found in the generated snapshot: check that the path and definitions are legal in the differential (including order)";
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+e.getId(), msg, ValidationMessage.IssueSeverity.ERROR));
}
@@ -2470,7 +2470,7 @@ public class ProfileUtilities extends TranslatingUtilities {
int i = 0;
while (i < markdown.length()) {
if (i < markdown.length()-3 && markdown.substring(i, i+2).equals("](")) {
- int j = i + 2;
+ int j = i + 2;
while (j < markdown.length() && markdown.charAt(j) != ')')
j++;
if (j < markdown.length()) {
@@ -2492,7 +2492,8 @@ public class ProfileUtilities extends TranslatingUtilities {
i = i + 1;
} else {
b.append("](");
- b.append(webUrl);
+ // disabled 7-Dec 2021 GDG - we don't want to fool with relative URLs at all?
+ // b.append(webUrl);
i = i + 1;
}
} else
@@ -4773,7 +4774,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private void genFixedValue(HierarchicalTableGenerator gen, Row erow, DataType value, boolean snapshot, boolean pattern, String corePath, boolean skipnoValue) {
String ref = pkp.getLinkFor(corePath, value.fhirType());
- if (ref != null) {
+ if (ref != null && ref.contains(".html")) {
ref = ref.substring(0, ref.indexOf(".html"))+"-definitions.html#";
} else {
ref = "?gen-fv?";
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java
index ae301a6a4..a7378609c 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java
@@ -53,6 +53,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.NoTerminologyServiceException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.conformance.ProfileUtilities;
+import org.hl7.fhir.r5.context.BaseWorkerContext.ResourceProxy;
import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion;
import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory;
@@ -111,6 +112,7 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorCla
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.r5.utils.ToolingExtensions;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.OIDUtils;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.ToolingClientLogger;
@@ -130,6 +132,35 @@ import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{
+ public class ResourceProxy {
+ private Resource resource;
+ private CanonicalResourceProxy proxy;
+
+ public ResourceProxy(Resource resource) {
+ super();
+ this.resource = resource;
+ }
+ public ResourceProxy(CanonicalResourceProxy proxy) {
+ super();
+ this.proxy = proxy;
+ }
+
+ public Resource getResource() {
+ return resource != null ? resource : proxy.getResource();
+ }
+
+ public String getUrl() {
+ if (resource == null) {
+ return proxy.getUrl();
+ } else if (resource instanceof CanonicalResource) {
+ return ((CanonicalResource) resource).getUrl();
+ } else {
+ return null;
+ }
+ }
+
+ }
+
public class MetadataResourceVersionComparator implements Comparator {
private List list;
@@ -166,7 +197,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private boolean isTxCaching;
private Set cached = new HashSet<>();
- private Map> allResourcesById = new HashMap>();
+ private Map> allResourcesById = new HashMap>();
// all maps are to the full URI
private CanonicalResourceManager codeSystems = new CanonicalResourceManager(false);
private Set supportedCodeSystems = new HashSet();
@@ -279,6 +310,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void registerResourceFromPackage(CanonicalResourceProxy r, PackageVersion packageInfo) throws FHIRException {
synchronized (lock) {
+ Map map = allResourcesById.get(r.getType());
+ if (map == null) {
+ map = new HashMap();
+ allResourcesById.put(r.getType(), map);
+ }
+ map.put(r.getId(), new ResourceProxy(r));
String url = r.getUrl();
if (!allowLoadingDuplicates && hasResource(r.getType(), url)) {
@@ -341,12 +378,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void cacheResourceFromPackage(Resource r, PackageVersion packageInfo) throws FHIRException {
synchronized (lock) {
- Map map = allResourcesById.get(r.fhirType());
+ Map map = allResourcesById.get(r.fhirType());
if (map == null) {
- map = new HashMap();
+ map = new HashMap();
allResourcesById.put(r.fhirType(), map);
}
- map.put(r.getId(), r);
+ if ((packageInfo == null || !packageInfo.isExamplesPackage()) || !map.containsKey(r.getId())) {
+ map.put(r.getId(), new ResourceProxy(r));
+ }
if (r instanceof CodeSystem || r instanceof NamingSystem) {
oidCache.clear();
@@ -892,6 +931,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
@Override
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) {
+ ValidationContextCarrier ctxt = new ValidationContextCarrier();
+ return validateCode(options, code, vs, ctxt);
+ }
+
+ @Override
+ public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt) {
if (options == null) {
options = ValidationOptions.defaults();
}
@@ -912,7 +957,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (options.isUseClient()) {
// ok, first we try to validate locally
try {
- ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
+ ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this, ctxt);
if (!vsc.isServerSide(code.getSystem())) {
res = vsc.validateCode(code);
if (txCache != null) {
@@ -1033,13 +1078,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (vs != null) {
- if (isTxCaching && cacheId != null && cached.contains(vs.getUrl()+"|"+vs.getVersion())) {
+ if (isTxCaching && cacheId != null && vs.getUrl() != null && cached.contains(vs.getUrl()+"|"+vs.getVersion())) {
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+vs.getVersion() : "")));
} else if (options.getVsAsUrl()){
pin.addParameter().setName("url").setValue(new StringType(vs.getUrl()));
} else {
pin.addParameter().setName("valueSet").setResource(vs);
- cached.add(vs.getUrl()+"|"+vs.getVersion());
+ if (vs.getUrl() != null) {
+ cached.add(vs.getUrl()+"|"+vs.getVersion());
+ }
}
cache = true;
addDependentResources(pin, vs);
@@ -1246,8 +1293,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return fetchResourceWithException(cls, uri, null);
}
- @SuppressWarnings("unchecked")
public T fetchResourceWithException(Class class_, String uri, CanonicalResource source) throws FHIRException {
+ return fetchResourceWithException(class_, uri, null, source);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T fetchResourceWithException(Class class_, String uri, String version, CanonicalResource source) throws FHIRException {
if (uri == null) {
return null;
}
@@ -1257,7 +1308,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
synchronized (lock) {
- String version = null;
if (uri.contains("|")) {
version = uri.substring(uri.lastIndexOf("|")+1);
uri = uri.substring(0, uri.lastIndexOf("|"));
@@ -1305,13 +1355,23 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (questionnaires.has(uri)) {
return (T) questionnaires.get(uri, version);
}
- for (Map rt : allResourcesById.values()) {
- for (Resource r : rt.values()) {
- if (r instanceof CanonicalResource) {
- CanonicalResource mr = (CanonicalResource) r;
- if (uri.equals(mr.getUrl())) {
- return (T) mr;
- }
+ if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) {
+ return null;
+ }
+
+ // it might be a special URL.
+ if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
+ Resource res = null; // findTxValueSet(uri);
+ if (res != null) {
+ return (T) res;
+ }
+ }
+ for (Map rt : allResourcesById.values()) {
+ for (ResourceProxy r : rt.values()) {
+ if (uri.equals(r.getUrl())) {
+ if (version == null || version == r.getResource().getMeta().getVersionId()) {
+ return (T) r.getResource();
+ }
}
}
}
@@ -1355,20 +1415,6 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (class_ == Questionnaire.class) {
return (T) questionnaires.get(uri, version);
}
- if (class_ == null) {
- if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet")) {
- return null;
- }
-
- // it might be a special URL.
- if (Utilities.isAbsoluteUrl(uri) || uri.startsWith("ValueSet/")) {
- Resource res = null; // findTxValueSet(uri);
- if (res != null) {
- return (T) res;
- }
- }
- return null;
- }
if (supportedCodeSystems.contains(uri)) {
return null;
}
@@ -1494,13 +1540,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (questionnaires.has(uri)) {
return (T) questionnaires.get(uri, version);
}
- for (Map rt : allResourcesById.values()) {
- for (Resource r : rt.values()) {
- if (r instanceof CanonicalResource) {
- CanonicalResource mr = (CanonicalResource) r;
- if (uri.equals(mr.getUrl())) {
- return (T) mr;
- }
+ for (Map rt : allResourcesById.values()) {
+ for (ResourceProxy r : rt.values()) {
+ if (uri.equals(r.getUrl())) {
+ return (T) r.getResource();
}
}
}
@@ -1574,7 +1617,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
String[] parts = uri.split("\\/");
if (!Utilities.noString(type) && parts.length == 1) {
if (allResourcesById.containsKey(type)) {
- return allResourcesById.get(type).get(parts[0]);
+ return allResourcesById.get(type).get(parts[0]).getResource();
} else {
return null;
}
@@ -1585,7 +1628,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
throw new Error(formatMessage(I18nConstants.RESOURCE_TYPE_MISMATCH_FOR___, type, uri));
}
}
- return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]);
+ return allResourcesById.get(parts[parts.length-2]).get(parts[parts.length-1]).getResource();
} else {
throw new Error(formatMessage(I18nConstants.UNABLE_TO_PROCESS_REQUEST_FOR_RESOURCE_FOR___, type, uri));
}
@@ -1608,6 +1651,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
+ public T fetchResource(Class class_, String uri, String version) {
+ try {
+ return fetchResourceWithException(class_, uri, version, null);
+ } catch (FHIRException e) {
+ throw new Error(e);
+ }
+ }
+
@Override
public boolean hasResource(Class class_, String uri) {
try {
@@ -1697,13 +1748,13 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
public void dropResource(String fhirType, String id) {
synchronized (lock) {
- Map map = allResourcesById.get(fhirType);
+ Map map = allResourcesById.get(fhirType);
if (map == null) {
- map = new HashMap();
+ map = new HashMap();
allResourcesById.put(fhirType, map);
}
if (map.containsKey(id)) {
- map.remove(id);
+ map.remove(id); // this is a challenge because we might have more than one resource with this id (different versions)
}
if (fhirType.equals("StructureDefinition")) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java
index 73b60a05e..34bfc6099 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/CanonicalResourceManager.java
@@ -236,6 +236,9 @@ public class CanonicalResourceManager {
}
}
CachedCanonicalResource existing = cr.hasVersion() ? map.get(cr.getUrl()+"|"+cr.getVersion()) : map.get(cr.getUrl()+"|#0");
+ if (existing != null && (cr.getPackageInfo() != null && cr.getPackageInfo().isExamplesPackage())) {
+ return;
+ }
if (existing != null) {
list.remove(existing);
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java
index 7f90e56c1..ed0afccfc 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java
@@ -45,6 +45,7 @@ import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.TerminologyCache.CacheToken;
+import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.ParserType;
import org.hl7.fhir.r5.model.Bundle;
@@ -64,11 +65,13 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.TranslationServices;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
+import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationOptions;
import com.google.gson.JsonSyntaxException;
@@ -167,6 +170,9 @@ public interface IWorkerContext {
public String getVersion() {
return version;
}
+ public boolean isExamplesPackage() {
+ return !(id.startsWith("hl7.fhir.") && id.endsWith(".example"));
+ }
}
public class PackageDetails extends PackageVersion {
@@ -190,13 +196,14 @@ public interface IWorkerContext {
}
}
+
public interface ICanonicalResourceLocator {
void findResource(Object caller, String url); // if it can be found, put it in the context
}
public interface IContextResourceLoader {
/**
- * @return List of the resource types that shoud be loaded
+ * @return List of the resource types that should be loaded
*/
String[] getTypes();
@@ -241,7 +248,6 @@ public interface IWorkerContext {
IContextResourceLoader getNewLoader(NpmPackage npm) throws JsonSyntaxException, IOException;
}
-
/**
* Get the versions of the definitions loaded in context
* @return
@@ -334,6 +340,7 @@ public interface IWorkerContext {
*/
public T fetchResource(Class class_, String uri);
public T fetchResourceWithException(Class class_, String uri) throws FHIRException;
+ public T fetchResource(Class class_, String uri, String version);
/** has the same functionality as fetchResource, but passes in information about the source of the
* reference (this may affect resolution of version)
@@ -741,6 +748,8 @@ public interface IWorkerContext {
* @return
*/
public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs);
+
+ public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs, ValidationContextCarrier ctxt);
public void validateCodeBatch(ValidationOptions options, List extends CodingValidationRequest> codes, ValueSet vs);
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java
index 139206271..8f0d1a1c1 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ListRenderer.java
@@ -59,7 +59,8 @@ public class ListRenderer extends ResourceRenderer {
shortForRef(td, list.get("subject"));
}
if (list.has("encounter")) {
- shortForRef(td.tx("Encounter: "), list.get("encounter"));
+ td.tx("Encounter: ");
+ shortForRef(td, list.get("encounter"));
}
if (list.has("source")) {
td.tx("Source: ");
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java
index b2f6739c3..df2000946 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java
@@ -703,7 +703,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
}
if (i.hasAnswerValueSet()) {
XhtmlNode ans = item(ul, "Answers");
- if (Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
+ if (!Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
ValueSet vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
if (vs == null) {
ans.tx(i.getAnswerValueSet());
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java
index 6334320a5..65f59608d 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java
@@ -202,7 +202,7 @@ public abstract class ResourceRenderer extends DataRenderer {
}
// what to display: if text is provided, then that. if the reference was resolved, then show the name, or the generated narrative
String display = r.hasDisplayElement() ? r.getDisplay() : null;
- String name = tr != null && tr.getResource() != null ? tr.getResource().getNameFromResource() : null;
+ String name = tr != null && tr.getResource() != null ? tr.getResource().getNameFromResource() : null;
if (display == null && (tr == null || tr.getResource() == null)) {
c.addText(r.getReference());
@@ -214,6 +214,11 @@ public abstract class ResourceRenderer extends DataRenderer {
if ((tr == null || !tr.getReference().startsWith("#")) && name != null) {
x.addText(" \""+name+"\"");
}
+ if (r.hasExtension(ToolingExtensions.EXT_TARGET_ID)) {
+ x.addText("(#"+r.getExtensionString(ToolingExtensions.EXT_TARGET_ID)+")");
+ } else if (r.hasExtension(ToolingExtensions.EXT_TARGET_PATH)) {
+ x.addText("(#/"+r.getExtensionString(ToolingExtensions.EXT_TARGET_PATH)+")");
+ }
} else {
if (display != null) {
c.addText(display);
@@ -272,6 +277,11 @@ public abstract class ResourceRenderer extends DataRenderer {
}
return null;
}
+ String version = null;
+ if (url.contains("/_history/")) {
+ version = url.substring(url.indexOf("/_history/")+10);
+ url = url.substring(0, url.indexOf("/_history/"));
+ }
if (rcontext != null) {
BundleEntryComponent bundleResource = rcontext.resolve(url);
@@ -279,7 +289,7 @@ public abstract class ResourceRenderer extends DataRenderer {
String bundleUrl = "#" + bundleResource.getResource().getResourceType().name() + "_" + bundleResource.getResource().getId();
return new ResourceWithReference(bundleUrl, new ResourceWrapperDirect(this.context, bundleResource.getResource()));
}
- org.hl7.fhir.r5.elementmodel.Element bundleElement = rcontext.resolveElement(url);
+ org.hl7.fhir.r5.elementmodel.Element bundleElement = rcontext.resolveElement(url, version);
if (bundleElement != null) {
String bundleUrl = null;
Element br = bundleElement.getNamedChild("resource");
@@ -292,7 +302,7 @@ public abstract class ResourceRenderer extends DataRenderer {
}
}
- Resource ae = getContext().getWorker().fetchResource(null, url);
+ Resource ae = getContext().getWorker().fetchResource(null, url, version);
if (ae != null)
return new ResourceWithReference(url, new ResourceWrapperDirect(this.context, ae));
else if (context.getResolver() != null) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java
index 84f0a748d..13cbb5977 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java
@@ -101,7 +101,7 @@ public class Resolver {
return null;
}
- public org.hl7.fhir.r5.elementmodel.Element resolveElement(String value) {
+ public org.hl7.fhir.r5.elementmodel.Element resolveElement(String value, String version) {
if (value.startsWith("#")) {
if (resourceElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element r : resourceElement.getChildrenByName("contained")) {
@@ -115,10 +115,18 @@ public class Resolver {
if (containerElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element be : containerElement.getChildren("entry")) {
org.hl7.fhir.r5.elementmodel.Element res = be.getNamedChild("resource");
- if (value.equals(be.getChildValue("fullUrl")))
- return be;
- if (value.equals(res.fhirType()+"/"+res.getChildValue("id")))
- return be;
+ if (res != null) {
+ if (value.equals(be.getChildValue("fullUrl"))) {
+ if (checkVersion(version, res)) {
+ return be;
+ }
+ }
+ if (value.equals(res.fhirType()+"/"+res.getChildValue("id"))) {
+ if (checkVersion(version, res)) {
+ return be;
+ }
+ }
+ }
}
}
}
@@ -126,13 +134,27 @@ public class Resolver {
if (containerElement != null) {
for (org.hl7.fhir.r5.elementmodel.Element p : containerElement.getChildren("parameter")) {
org.hl7.fhir.r5.elementmodel.Element res = p.getNamedChild("resource");
- if (res != null && value.equals(res.fhirType()+"/"+res.getChildValue("id")))
- return p;
+ if (res != null && value.equals(res.fhirType()+"/"+res.getChildValue("id"))) {
+ if (checkVersion(version, res)) {
+ return p;
+ }
+ }
}
}
}
return null;
}
+
+ private boolean checkVersion(String version, org.hl7.fhir.r5.elementmodel.Element res) {
+ if (version == null) {
+ return true;
+ } else if (!res.hasChild("meta")) {
+ return false;
+ } else {
+ org.hl7.fhir.r5.elementmodel.Element meta = res.getNamedChild("meta");
+ return version.equals(meta.getChildValue("version"));
+ }
+ }
}
public static class ResourceWithReference {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java
index 1e0667674..93d72845e 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java
@@ -56,6 +56,9 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
+import org.hl7.fhir.r5.utils.ToolingExtensions;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
@@ -69,12 +72,52 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
private IWorkerContext context;
private Map inner = new HashMap<>();
private ValidationOptions options;
+ private ValidationContextCarrier localContext;
+ private List localSystems = new ArrayList<>();
public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context) {
this.valueset = source;
this.context = context;
this.options = options;
}
+
+ public ValueSetCheckerSimple(ValidationOptions options, ValueSet source, IWorkerContext context, ValidationContextCarrier ctxt) {
+ this.valueset = source;
+ this.context = context;
+ this.options = options;
+ this.localContext = ctxt;
+ analyseValueSet();
+ }
+
+ private void analyseValueSet() {
+ if (localContext != null) {
+ if (valueset != null) {
+ for (ConceptSetComponent i : valueset.getCompose().getInclude()) {
+ analyseComponent(i);
+ }
+ for (ConceptSetComponent i : valueset.getCompose().getExclude()) {
+ analyseComponent(i);
+ }
+ }
+ }
+ }
+
+ private void analyseComponent(ConceptSetComponent i) {
+ if (i.getSystemElement().hasExtension(ToolingExtensions.EXT_VALUESET_SYSTEM)) {
+ String ref = i.getSystemElement().getExtensionString(ToolingExtensions.EXT_VALUESET_SYSTEM);
+ if (ref.startsWith("#")) {
+ String id = ref.substring(1);
+ for (ValidationContextResourceProxy t : localContext.getResources()) {
+ CodeSystem cs = (CodeSystem) t.loadContainedResource(id, CodeSystem.class);
+ if (cs != null) {
+ localSystems.add(cs);
+ }
+ }
+ } else {
+ throw new Error("Not done yet #2: "+ref);
+ }
+ }
+ }
public ValidationResult validateCode(CodeableConcept code) throws FHIRException {
// first, we validate the codings themselves
@@ -85,7 +128,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (!c.hasSystem()) {
warnings.add(context.formatMessage(I18nConstants.CODING_HAS_NO_SYSTEM__CANNOT_VALIDATE));
}
- CodeSystem cs = context.fetchCodeSystem(c.getSystem());
+ CodeSystem cs = resolveCodeSystem(c.getSystem());
ValidationResult res = null;
if (cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) {
res = context.validateCode(options.noClient(), c, null);
@@ -124,6 +167,19 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
}
+ public CodeSystem resolveCodeSystem(String system) {
+ for (CodeSystem t : localSystems) {
+ if (t.getUrl().equals(system)) {
+ return t;
+ }
+ }
+ CodeSystem cs = context.fetchCodeSystem(system);
+ if (cs == null) {
+ cs = findSpecialCodeSystem(system);
+ }
+ return cs;
+ }
+
public ValidationResult validateCode(Coding code) throws FHIRException {
String warningMessage = null;
// first, we validate the concept itself
@@ -144,11 +200,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
}
inExpansion = checkExpansion(code);
inInclude = checkInclude(code);
- CodeSystem cs = context.fetchCodeSystem(system);
- if (cs == null) {
- cs = findSpecialCodeSystem(system);
- }
- System.out.print("");
+ CodeSystem cs = resolveCodeSystem(system);
if (cs == null) {
warningMessage = "Unable to resolve system "+system;
if (!inExpansion) {
@@ -499,7 +551,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (vsi.hasFilter()) {
return null;
}
- CodeSystem cs = context.fetchCodeSystem(vsi.getSystem());
+ CodeSystem cs = resolveCodeSystem(vsi.getSystem());
if (cs == null) {
return null;
}
@@ -605,7 +657,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
if (!system.equals(vsi.getSystem()))
return false;
// ok, we need the code system
- CodeSystem cs = context.fetchCodeSystem(system);
+ CodeSystem cs = resolveCodeSystem(system);
if (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT)) {
// make up a transient value set with
ValueSet vs = new ValueSet();
@@ -710,7 +762,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe
return inner.get(url);
}
ValueSet vs = context.fetchResource(ValueSet.class, url);
- ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context);
+ ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context, localContext);
inner.put(url, vsc);
return vsc;
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java
index 6872e5e63..7ca4ba646 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java
@@ -5539,7 +5539,7 @@ public class FHIRPathEngine {
throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_NAME, expr.getName());
}
} else if (expr.getKind() == Kind.Group) {
- throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP);
+ throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP, expr.toString());
} else if (expr.getKind() == Kind.Constant) {
throw makeException(expr, I18nConstants.FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST);
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java
index 2c40dff4d..ef912788e 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java
@@ -195,6 +195,9 @@ public class ToolingExtensions {
public static final String EXT_XML_NAME = "http://hl7.org/fhir/StructureDefinition/elementdefinition-xml-name";
public static final String EXT_BINDING_STYLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-binding-style";
public static final String EXT_BINARY_FORMAT = "http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format";
+ public static final String EXT_TARGET_ID = "http://hl7.org/fhir/StructureDefinition/targetElement";
+ public static final String EXT_TARGET_PATH = "http://hl7.org/fhir/StructureDefinition/targetPath";
+ public static final String EXT_VALUESET_SYSTEM = "http://hl7.org/fhir/StructureDefinition/valueset-system";
// specific extension helpers
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/ValidationContextCarrier.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/ValidationContextCarrier.java
new file mode 100644
index 000000000..313919812
--- /dev/null
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/ValidationContextCarrier.java
@@ -0,0 +1,81 @@
+package org.hl7.fhir.r5.utils.validation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r5.elementmodel.Element;
+import org.hl7.fhir.r5.model.DomainResource;
+import org.hl7.fhir.r5.model.Questionnaire;
+import org.hl7.fhir.r5.model.Resource;
+import org.hl7.fhir.utilities.validation.ValidationMessage;
+
+public class ValidationContextCarrier {
+ /**
+ *
+ * When the validator is calling validateCode, it typically has a partially loaded resource that may provide
+ * additional resources that are relevant to the validation. This is a handle back into the validator context
+ * to ask for the resource to be fully loaded if it becomes relevant. Note that the resource may fail to load
+ * (e.g. if it's part of what's being validated) and if it does, the validator will record the validation
+ * issues before throwing an error
+ *
+ * This is a reference back int
+ *
+ */
+ public interface IValidationContextResourceLoader {
+ public Resource loadContainedResource(List errors, String path, Element resource, String id, Class extends Resource> class1) throws FHIRException;
+ }
+
+ /**
+ * A list of resources that provide context - typically, a container resource, and a bundle resource.
+ * iterate these in order looking for contained resources
+ *
+ */
+ public static class ValidationContextResourceProxy {
+
+ // either a resource
+ private Resource resource;
+
+
+ // or an element and a loader
+ private Element element;
+ private IValidationContextResourceLoader loader;
+ private List errors;
+ private String path;
+
+ public ValidationContextResourceProxy(Resource resource) {
+ this.resource = resource;
+ }
+
+ public ValidationContextResourceProxy(List errors, String path, Element element, IValidationContextResourceLoader loader) {
+ this.errors = errors;
+ this.path = path;
+ this.element = element;
+ this.loader = loader;
+ }
+
+ public Resource loadContainedResource(String id, Class extends Resource> class1) throws FHIRException {
+ if (resource == null) {
+ Resource res = loader.loadContainedResource(errors, path, element, id, class1);
+ return res;
+ } else {
+ if (resource instanceof DomainResource) {
+ for (Resource r : ((DomainResource) resource).getContained()) {
+ if (r.getId().equals(id)) {
+ if (class1.isInstance(r))
+ return r;
+ }
+ }
+ }
+ return null;
+ }
+ }
+ }
+
+ private List resources = new ArrayList<>();
+
+ public List getResources() {
+ return resources;
+ }
+
+}
diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml
index 1950d77f4..5035c39a6 100644
--- a/org.hl7.fhir.report/pom.xml
+++ b/org.hl7.fhir.report/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml
index 57d4f00a0..20e06f6c1 100644
--- a/org.hl7.fhir.utilities/pom.xml
+++ b/org.hl7.fhir.utilities/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SIDUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SIDUtilities.java
index 69664a879..7f7d11101 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SIDUtilities.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SIDUtilities.java
@@ -10,11 +10,9 @@ public class SIDUtilities {
public static List codeSystemList() {
List codeSystems = new ArrayList<>();
codeSystems.add("http://hl7.org/fhir/sid/ndc");
- codeSystems.add("http://hl7.org/fhir/sid/icd-10");
codeSystems.add("http://hl7.org/fhir/sid/icpc2");
codeSystems.add("http://hl7.org/fhir/sid/icd-9");
codeSystems.add("http://hl7.org/fhir/sid/icd-10");
- codeSystems.add("http://hl7.org/fhir/sid/icpc2");
codeSystems.add("http://hl7.org/fhir/sid/cvx");
codeSystems.add("http://hl7.org/fhir/sid/srt");
codeSystems.add("http://hl7.org/fhir/sid/icd-10-vn");
@@ -55,8 +53,38 @@ public class SIDUtilities {
allSystems.addAll(idSystemList());
return allSystems;
}
-
-
-
+ public static boolean isInvalidVersion(String u, String v) {
+ if (v == null) {
+ return false;
+ } else {
+ if (idSystemList().contains(u)) {
+ return true;
+ } else {
+ switch (u) {
+ case "http://hl7.org/fhir/sid/ndc":
+ return v.matches("[\\d]{8}");
+ case "http://hl7.org/fhir/sid/icpc2":
+ return false;
+ case "http://hl7.org/fhir/sid/icd-10":
+ return false;
+ case "http://hl7.org/fhir/sid/icd-9":
+ return false;
+ case "http://hl7.org/fhir/sid/cvx":
+ return v.matches("[\\d]{8}");
+ case "http://hl7.org/fhir/sid/srt":
+ return false;
+ case "http://hl7.org/fhir/sid/icd-10-vn":
+ return false;
+ case "http://hl7.org/fhir/sid/icd-10-cm":
+ return false;
+ case "http://hl7.org/fhir/sid/icd-9-cm":
+ return false;
+ default:
+ return true;
+ }
+ }
+ }
+ }
+
}
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java
index 18640920f..6582909c3 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/VersionUtilities.java
@@ -397,7 +397,6 @@ public class VersionUtilities {
}
if (isR4Ver(version)) {
-
res.add("CodeSystem");
res.add("ActivityDefinition");
res.add("CapabilityStatement");
@@ -429,6 +428,38 @@ public class VersionUtilities {
res.add("TestScript");
res.add("ValueSet");
}
+ if (isR4BVer(version)) {
+ res.add("ActivityDefinition");
+ res.add("CapabilityStatement");
+ res.add("ChargeItemDefinition");
+ res.add("Citation");
+ res.add("CodeSystem");
+ res.add("CompartmentDefinition");
+ res.add("ConceptMap");
+ res.add("EventDefinition");
+ res.add("Evidence");
+ res.add("EvidenceReport");
+ res.add("EvidenceVariable");
+ res.add("ExampleScenario");
+ res.add("GraphDefinition");
+ res.add("ImplementationGuide");
+ res.add("Library");
+ res.add("Measure");
+ res.add("MessageDefinition");
+ res.add("NamingSystem");
+ res.add("OperationDefinition");
+ res.add("PlanDefinition");
+ res.add("Questionnaire");
+ res.add("ResearchDefinition");
+ res.add("ResearchElementDefinition");
+ res.add("SearchParameter");
+ res.add("StructureDefinition");
+ res.add("StructureMap");
+ res.add("SubscriptionTopic");
+ res.add("TerminologyCapabilities");
+ res.add("TestScript");
+ res.add("ValueSet");
+ }
if (isR5Ver(version) || "current".equals(version)) {
@@ -479,5 +510,16 @@ public class VersionUtilities {
return mm1 != null && mm2 != null && mm1.equals(mm2);
}
+ public static boolean isR5VerOrLater(String version) {
+ if (version == null) {
+ return false;
+ }
+ if (version.startsWith(CURRENT_VERSION) || version.equals("current")) {
+ return true;
+ }
+ String v = getMajMin(version);
+ return v.compareTo("4.5") >= 0;
+ }
+
}
\ No newline at end of file
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
index 0e0a141dc..1a67ff30d 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
@@ -1,5 +1,6 @@
package org.hl7.fhir.utilities.i18n;
+import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
public class I18nConstants {
@@ -483,6 +484,8 @@ public class I18nConstants {
public static final String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_CHARS = "TYPE_SPECIFIC_CHECKS_DT_DECIMAL_CHARS";
public static final String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_RANGE = "Type_Specific_Checks_DT_Decimal_Range";
public static final String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID = "Type_Specific_Checks_DT_Decimal_Valid";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_GT = "Type_Specific_Checks_DT_Decimal_GT";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_DECIMAL_LT = "Type_Specific_Checks_DT_Decimal_LT";
public static final String TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_IETF_SYSTEM_VALUE = "TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_IETF_SYSTEM_VALUE";
public static final String TYPE_SPECIFIC_CHECKS_DT_IDENTIFIER_SYSTEM = "Type_Specific_Checks_DT_Identifier_System";
public static final String TYPE_SPECIFIC_CHECKS_DT_ID_VALID = "Type_Specific_Checks_DT_ID_Valid";
@@ -511,6 +514,7 @@ public class I18nConstants {
public static final String TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE = "TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE";
public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE";
public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE_NC = "TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE_NC";
public static final String TYPE_SPECIFIC_CHECKS_DT_UUID_STRAT = "Type_Specific_Checks_DT_UUID_Strat";
public static final String TYPE_SPECIFIC_CHECKS_DT_UUID_VALID = "Type_Specific_Checks_DT_UUID_Valid";
public static final String UNABLE_TO_CONNECT_TO_TERMINOLOGY_SERVER = "Unable_to_connect_to_terminology_server";
@@ -659,6 +663,36 @@ public class I18nConstants {
public static final String CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL = "CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL";
public static final String CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_WRONG = "CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_WRONG";
public static final String CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_MISSING = "CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_MISSING";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_QTY = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_QTY";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_SYSTEM_MISMATCH = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_SYSTEM_MISMATCH";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_CODE_MISMATCH = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_CODE_MISMATCH";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_VALUE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_VALUE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_VALUE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_VALUE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_SYSTEM = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_SYSTEM";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_SYSTEM = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_SYSTEM";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CODE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CODE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_CODE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_CODE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_UCUM_SVC = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_UCUM_SVC";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CONVERT = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CONVERT";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG_UCUM = "TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG_UCUM";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_QTY = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_QTY";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_SYSTEM_MISMATCH = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_SYSTEM_MISMATCH";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_CODE_MISMATCH = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_CODE_MISMATCH";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_VALUE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_VALUE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_VALUE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_VALUE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_SYSTEM = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_SYSTEM";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_SYSTEM = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_SYSTEM";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CODE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CODE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_CODE = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_CODE";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_UCUM_SVC = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_UCUM_SVC";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CONVERT = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CONVERT";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG_UCUM = "TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG_UCUM";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_ERROR = "TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_ERROR";
+ public static final String TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_WARNING = "TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_WARNING";
}
+
+
diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties
index 2536ee23e..bf567fc41 100644
--- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties
+++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties
@@ -186,6 +186,8 @@ Type_Specific_Checks_DT_DateTime_Valid = Not a valid date/time ({0})
Type_Specific_Checks_DT_Date_Valid = Not a valid date ({0})
Type_Specific_Checks_DT_Decimal_Range = The value ''{0}'' is outside the range of commonly/reasonably supported decimals
Type_Specific_Checks_DT_Decimal_Valid = The value ''{0}'' is not a valid decimal
+Type_Specific_Checks_DT_Decimal_GT = value is greater than permitted maximum value of {0}
+Type_Specific_Checks_DT_Decimal_LT = value is less than permitted minimum value of {0}
Type_Specific_Checks_DT_ID_Valid = id value ''{0}'' is not valid
Type_Specific_Checks_DT_Identifier_System = Identifier.system must be an absolute reference, not a local reference
Type_Specific_Checks_DT_Instant_Valid = Not a valid instant ({0})
@@ -571,7 +573,7 @@ FHIRPATH_DISCRIMINATOR_TYPE_NONE = illegal use of ofType() in discriminator - no
FHIRPATH_DISCRIMINATOR_TYPE_MULTIPLE = illegal use of ofType() in discriminator - Multiple possible types on {0}
FHIRPATH_DISCRIMINATOR_NO_CODE = illegal use of ofType() in discriminator - Type has no code on {0}
FHIRPATH_DISCRIMINATOR_BAD_NAME = illegal function name {0}() in discriminator
-FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP = illegal expression syntax in discriminator (group)
+FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_GROUP = illegal expression syntax in discriminator (group ''{0}'')
FHIRPATH_DISCRIMINATOR_BAD_SYNTAX_CONST = illegal expression syntax in discriminator (const)
FHIRPATH_DISCRIMINATOR_CANT_FIND = Unable to resolve discriminator in definitions: {0} in profile {1} on element {2}, looking in profile {3}
FHIRPATH_DISCRIMINATOR_CANT_FIND_EXTENSION = Unable to resolve discriminator {0} on {2} found in the definitions because the extension {1} wasn''t found in the profile {3}
@@ -643,6 +645,7 @@ SD_ED_BIND_NO_BINDABLE = The element {0} has a binding, but no bindable types ar
DISCRIMINATOR_BAD_PATH = Error processing path expression for discriminator: {0} (src = ''{1}'')
SLICING_CANNOT_BE_EVALUATED = Slicing cannot be evaluated: {0}
TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE = Canonical URL ''{0}'' does not resolve
+TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE_NC = Canonical URL ''{0}'' exists, but can't be loaded, so it can't be checked for validity
TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE = Canonical URL ''{0}'' refers to a resource that has the wrong type. Found {1} expecting one of {2}
CODESYSTEM_CS_NO_SUPPLEMENT = CodeSystem {0} is a supplement, so can't be used as a value in Coding.system
CODESYSTEM_CS_SUPP_CANT_CHECK = CodeSystem {0} cannot be found, so can't check if concepts are valid
@@ -670,4 +673,32 @@ CODESYSTEM_CS_NONHL7_MISSING_ELEMENT = CodeSystems SHOULD have a stated value fo
CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL = CodeSystems SHOULD NOT have a stated value for the {0} element when they are a supplement
CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_WRONG = CodeSystem Supplements SHALL have a content value of 'supplement'
CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_MISSING = CodeSystem Supplements with a content value of 'supplement' SHALL have a supplements elemnet that specifies which code system is being supplemented
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_QTY = Found {0} of type {2} in the profile validating a Quantity (so it must be a Quantity)
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_SYSTEM_MISMATCH = The minValue in the profile has a system of {0} which is different to the system in the value {1} so the minimum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_CODE_MISMATCH = The minValue in the profile has a system code of {0} which is different to the system code in the value {1} so the minimum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_VALUE = The minValue in the profile doesn't have an actual value, so the minimum value can't be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_VALUE = The quantity doesn't have an actual value, so the minimum value can't be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_SYSTEM = The minValue in the profile has no system so the minimum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_SYSTEM = The value has no system so the minimum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CODE = The minValue in the profile has no code so the minimum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_CODE = The value has no code so the minimum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_UCUM_SVC = There is no UCUM service, and the UCUM codes aren't identical, so the minimum value can't be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CONVERT = Unable to convert value {0} from unit {1} to minValue unit {2} based on UCUM definitions; minimum value is not valid
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG = The value in the instance ({2}) is less than the specified minimum value ({3})
+TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG_UCUM = The value in the instance ({0} {1}) is less than the specified minValue ({2} {3}) after UCUM conversion
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_QTY = Found {0} of type {2} in the profile validating a Quantity (so it must be a Quantity)
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_SYSTEM_MISMATCH = The maxValue in the profile has a system of {0} which is different to the system in the value {1} so the maximum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_CODE_MISMATCH = The maxValue in the profile has a system code of {0} which is different to the system code in the value {1} so the maximum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_VALUE = The maxValue in the profile doesn't have an actual value, so the maximum value can't be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_VALUE = The quantity doesn't have an actual value, so the maximum value can't be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_SYSTEM = The maxValue in the profile has no system so the maximum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_SYSTEM = The value has no system so the maximum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CODE = The maxValue in the profile has no code so the maximum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_CODE = The value has no code so the maximum value cannot be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_UCUM_SVC = There is no UCUM service, and the UCUM codes aren't identical, so the maximum value can't be checked
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CONVERT = Unable to convert value {0} from unit {1} to maxValue unit {2} based on UCUM definitions; maximum value is not valid
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG = The value in the instance ({2}) is greater than the specified maximum value ({3})
+TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG_UCUM = The value in the instance ({0} {1}) is greater than the specified maxValue ({2} {3}) after UCUM conversion
+TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_ERROR = Base64 encoded values are not allowed to contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway
+TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_WARNING = Base64 encoded values SHOULD not contain any whitespace (per RFC 4648). Note that non-validating readers are encouraged to accept whitespace anyway
diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml
index f73bfeca3..55d7534e0 100644
--- a/org.hl7.fhir.validation.cli/pom.xml
+++ b/org.hl7.fhir.validation.cli/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhir
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
../pom.xml
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java
index fe10e899f..74c3102a1 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java
@@ -44,6 +44,7 @@ import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
import org.hl7.fhir.r5.utils.XVerExtensionManager.XVerExtensionStatus;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.IValidationContextResourceLoader;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.validation.ValidationMessage;
@@ -61,7 +62,8 @@ import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isBlank;
-public class BaseValidator {
+public class BaseValidator implements IValidationContextResourceLoader {
+
public class TrackedLocationRelatedMessage {
private Object location;
private ValidationMessage vmsg;
@@ -996,7 +998,8 @@ public class BaseValidator {
}
}
- protected Resource loadContainedResource(List errors, String path, Element resource, String id, Class class1) throws FHIRException {
+ @Override
+ public Resource loadContainedResource(List errors, String path, Element resource, String id, Class extends Resource> class1) throws FHIRException {
for (Element contained : resource.getChildren("contained")) {
if (contained.getIdBase().equals(id)) {
return loadFoundResource(errors, path, contained, class1);
@@ -1005,7 +1008,7 @@ public class BaseValidator {
return null;
}
- protected Resource loadFoundResource(List errors, String path, Element resource, Class class1) throws FHIRException {
+ protected Resource loadFoundResource(List errors, String path, Element resource, Class extends Resource> class1) throws FHIRException {
try {
FhirPublication v = FhirPublication.fromCode(context.getVersion());
ByteArrayOutputStream bs = new ByteArrayOutputStream();
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java
index 245a22782..c714663b1 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidationEngine.java
@@ -3,6 +3,9 @@ package org.hl7.fhir.validation;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
+
+import org.fhir.ucum.UcumEssenceService;
+import org.fhir.ucum.UcumException;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
@@ -227,6 +230,15 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
context = SimpleWorkerContext.fromDefinitions(source, ValidatorUtils.loaderForVersion(version), new PackageVersion(src));
ValidatorUtils.grabNatives(getBinaries(), source, "http://hl7.org/fhir");
}
+ // ucum-essence.xml should be in the class path. if it's not, ask about how to sort this out
+ // on https://chat.fhir.org/#narrow/stream/179167-hapi
+ try {
+ ClassLoader classLoader = ValidationEngine.class.getClassLoader();
+ InputStream ue = classLoader.getResourceAsStream("ucum-essence.xml");
+ context.setUcumService(new UcumEssenceService(ue));
+ } catch (Exception e) {
+ throw new FHIRException("Error loading UCUM from embedded ucum-essence.xml: "+e.getMessage(), e);
+ }
initContext(tt);
}
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
index ed248afdd..a2b3820d9 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
@@ -37,6 +37,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
@@ -51,6 +52,7 @@ import java.util.UUID;
import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
+import org.fhir.ucum.Decimal;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
@@ -2112,52 +2114,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
rule(errors, IssueType.INVALID, e.line(), e.col(), path, Utilities.isAbsoluteUrl(url),
node.isContained() ? I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_CONTAINED : I18nConstants.TYPE_SPECIFIC_CHECKS_CANONICAL_ABSOLUTE, url);
} else {
- // now, do we check the URI target?
- if (fetcher != null && !type.equals("uuid")) {
- boolean found;
- try {
- found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) ||
- SpecialExtensions.isKnownExtension(url) || isXverUrl(url) || fetcher.resolveURL(this, hostContext, path, url, type);
- } catch (IOException e1) {
- found = false;
- }
- if (!found) {
- if (type.equals("canonical")) {
- ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, hostContext, path, url);
- if (rp == ReferenceValidationPolicy.CHECK_EXISTS || rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE) {
- rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url);
- } else {
- hint(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url);
- }
- } else {
- if (url.contains("hl7.org") || url.contains("fhir.org")) {
- rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url);
- } else if (url.contains("example.org") || url.contains("acme.com")) {
- rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE, url);
- } else {
- warning(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url);
- }
- }
- } else {
- if (type.equals("canonical")) {
- ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, hostContext, path, url);
- if (rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE || rp == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS || rp == ReferenceValidationPolicy.CHECK_VALID) {
- try {
- Resource r = fetcher.fetchCanonicalResource(this, url);
- if (r == null) {
- rule(errors, IssueType.INVALID, e.line(), e.col(), path, found, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url);
- } else if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, isCorrectCanonicalType(r, context), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE, url, r.fhirType(), listExpectedCanonicalTypes(context))) {
- if (rp == ReferenceValidationPolicy.CHECK_VALID) {
- // todo....
- }
- }
- } catch (Exception ex) {
- // won't happen
- }
- }
- }
- }
- }
+ validateReference(hostContext, errors, path, type, context, e, url);
}
}
if (type.equals(ID) && !"Resource.id".equals(context.getBase().getPath())) {
@@ -2214,6 +2171,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!ok) {
String value = encoded.length() < 100 ? encoded : "(snip)";
rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_BASE64_VALID, value);
+ } else {
+ boolean wsok = !base64HasWhitespace(encoded);
+ if (VersionUtilities.isR5VerOrLater(this.context.getVersion())) {
+ rule(errors, IssueType.INVALID, e.line(), e.col(), path, wsok, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_ERROR);
+ } else {
+ warning(errors, IssueType.INVALID, e.line(), e.col(), path, wsok, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_BASE64_NO_WS_WARNING);
+ }
}
if (ok && context.hasExtension("http://hl7.org/fhir/StructureDefinition/maxSize")) {
int size = countBase64DecodedBytes(encoded);
@@ -2248,8 +2212,18 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (type.equals("decimal")) {
if (e.primitiveValue() != null) {
DecimalStatus ds = Utilities.checkDecimal(e.primitiveValue(), true, false);
- if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID, e.primitiveValue()))
+ if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, ds == DecimalStatus.OK || ds == DecimalStatus.RANGE, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_VALID, e.primitiveValue())) {
warning(errors, IssueType.VALUE, e.line(), e.col(), path, ds != DecimalStatus.RANGE, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_RANGE, e.primitiveValue());
+ try {
+ Decimal v = new Decimal(e.getValue());
+ rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxValueIntegerType() ||
+ !context.getMaxValueIntegerType().hasValue() || checkDecimalMaxValue(v, context.getMaxValueDecimalType().getValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_GT, (context.hasMaxValueIntegerType() ? context.getMaxValueIntegerType() : ""));
+ rule(errors, IssueType.INVALID, e.line(), e.col(), path, !context.hasMinValueIntegerType() ||
+ !context.getMinValueIntegerType().hasValue() || checkDecimalMinValue(v, context.getMaxValueDecimalType().getValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_LT, (context.hasMinValueIntegerType() ? context.getMinValueIntegerType() : ""));
+ } catch (Exception ex) {
+ // should never happen?
+ }
+ }
}
if (context.hasExtension("http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces")) {
int dp = e.primitiveValue().contains(".") ? e.primitiveValue().substring(e.primitiveValue().indexOf(".")+1).length() : 0;
@@ -2304,6 +2278,67 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// for nothing to check
}
+ public void validateReference(ValidatorHostContext hostContext, List errors, String path, String type, ElementDefinition context, Element e, String url) {
+ // now, do we check the URI target?
+ if (fetcher != null && !type.equals("uuid")) {
+ boolean found;
+ try {
+ found = isDefinitionURL(url) || (allowExamples && (url.contains("example.org") || url.contains("acme.com")) || url.contains("acme.org")) || (url.startsWith("http://hl7.org/fhir/tools")) ||
+ SpecialExtensions.isKnownExtension(url) || isXverUrl(url);
+ if (!found) {
+ found = fetcher.resolveURL(this, hostContext, path, url, type);
+ }
+ } catch (IOException e1) {
+ found = false;
+ }
+ if (!found) {
+ if (type.equals("canonical")) {
+ ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, hostContext, path, url);
+ if (rp == ReferenceValidationPolicy.CHECK_EXISTS || rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE) {
+ rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url);
+ } else {
+ hint(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE, url);
+ }
+ } else {
+ if (url.contains("hl7.org") || url.contains("fhir.org")) {
+ rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url);
+ } else if (url.contains("example.org") || url.contains("acme.com")) {
+ rule(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE, url);
+ } else {
+ warning(errors, IssueType.INVALID, e.line(), e.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URL_RESOLVE, url);
+ }
+ }
+ } else {
+ if (type.equals("canonical")) {
+ ReferenceValidationPolicy rp = policyAdvisor == null ? ReferenceValidationPolicy.CHECK_VALID : policyAdvisor.policyForReference(this, hostContext, path, url);
+ if (rp == ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE || rp == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS || rp == ReferenceValidationPolicy.CHECK_VALID) {
+ try {
+ Resource r = null;
+ if (url.startsWith("#")) {
+ r = loadContainedResource(errors, path, hostContext.getRootResource(), url.substring(1), Resource.class);
+ }
+ if (r == null) {
+ fetcher.fetchCanonicalResource(this, url);
+ }
+ if (r == null) {
+ r = this.context.fetchResource(Resource.class, url);
+ }
+ if (r == null) {
+ warning(errors, IssueType.INVALID, e.line(), e.col(), path, rp != ReferenceValidationPolicy.CHECK_VALID, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_RESOLVE_NC, url);
+ } else if (rule(errors, IssueType.INVALID, e.line(), e.col(), path, isCorrectCanonicalType(r, context), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_CANONICAL_TYPE, url, r.fhirType(), listExpectedCanonicalTypes(context))) {
+ if (rp == ReferenceValidationPolicy.CHECK_VALID) {
+ // todo....
+ }
+ }
+ } catch (Exception ex) {
+ // won't happen
+ }
+ }
+ }
+ }
+ }
+ }
+
private List listExpectedCanonicalTypes(ElementDefinition context) {
List res = new ArrayList<>();
TypeRefComponent tr = context.getType("canonical");
@@ -2335,11 +2370,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean isCorrectCanonicalType(Resource r, CanonicalType p) {
String url = p.getValue();
- if (url != null && url.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
- url = url.substring("http://hl7.org/fhir/StructureDefinition/".length());
- return Utilities.existsInList(url, "Resource", "CanonicalResource") || url.equals(r.fhirType());
+ String t = null;
+ if (url.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
+ t = url.substring("http://hl7.org/fhir/StructureDefinition/".length());
+ } else {
+ StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
+ if (sd != null) {
+ t = sd.getType();
+ }
+ }
+ if (t == null ) {
+ return false;
+ } else {
+ return Utilities.existsInList(t, "Resource", "CanonicalResource") || t.equals(r.fhirType());
}
- return false;
}
private boolean isCanonicalURLElement(Element e) {
@@ -2408,6 +2452,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok;
}
+ private boolean base64HasWhitespace(String theEncoded) {
+ if (theEncoded == null) {
+ return false;
+ }
+ for (int i = 0; i < theEncoded.length(); i++) {
+ char nextChar = theEncoded.charAt(i);
+ if (Character.isWhitespace(nextChar)) {
+ return true;
+ }
+ }
+ return false;
+
+ }
+
private int countBase64DecodedBytes(String theEncoded) {
Base64InputStream inputStream = new Base64InputStream(new ByteArrayInputStream(theEncoded.getBytes(StandardCharsets.UTF_8)));
@@ -2615,7 +2673,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".code", focus.getNamedChild("code"), fixed.getCodeElement(), fixedSource, "code", focus, pattern);
}
- private void checkQuantity(List theErrors, String thePath, Element element, StructureDefinition theProfile, ElementDefinition definition, NodeStack theStack) {
+ private void checkQuantity(List errors, String path, Element element, StructureDefinition theProfile, ElementDefinition definition, NodeStack theStack) {
+ String value = element.hasChild("value") ? element.getNamedChild("value").getValue() : null;
String unit = element.hasChild("unit") ? element.getNamedChild("unit").getValue() : null;
String system = element.hasChild("system") ? element.getNamedChild("system").getValue() : null;
String code = element.hasChild("code") ? element.getNamedChild("code").getValue() : null;
@@ -2623,24 +2682,122 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// todo: allowedUnits http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits - codeableConcept, or canonical(ValueSet)
// todo: http://hl7.org/fhir/StructureDefinition/iso21090-PQ-translation
- if (definition.hasExtension("http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces")) {
- String dec = element.getChildValue("value");
- int dp = dec.contains(".") ? dec.substring(dec.indexOf(".")+1).length() : 0;
+ if (!Utilities.noString(value) && definition.hasExtension("http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces")) {
+ int dp = value.contains(".") ? value.substring(value.indexOf(".")+1).length() : 0;
int def = Integer.parseInt(ToolingExtensions.readStringExtension(definition, "http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces"));
- rule(theErrors, IssueType.STRUCTURE, element.line(), element.col(), thePath, dp <= def, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_CHARS, dp, def);
+ rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, dp <= def, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_DECIMAL_CHARS, dp, def);
}
-
+
if (system != null || code != null ) {
- checkCodedElement(theErrors, thePath, element, theProfile, definition, false, false, theStack, code, system, null, unit);
+ checkCodedElement(errors, path, element, theProfile, definition, false, false, theStack, code, system, null, unit);
}
-
+
if (code != null && "http://unitsofmeasure.org".equals(system)) {
int b = code.indexOf("{");
int e = code.indexOf("}");
if (b >= 0 && e > 0 && b < e) {
- bpCheck(theErrors, IssueType.BUSINESSRULE, element.line(), element.col(), thePath, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS, code.substring(b, e+1));
+ bpCheck(errors, IssueType.BUSINESSRULE, element.line(), element.col(), path, !code.contains("{"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS, code.substring(b, e+1));
}
}
+
+ if (definition.hasMinValue()) {
+ if (warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(value), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_VALUE) &&
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, definition.getMinValue() instanceof Quantity, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_QTY, definition.getMinValue().fhirType())) {
+ Quantity min = definition.getMinValueQuantity();
+ if (warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(min.getSystem()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_SYSTEM) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(system), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_SYSTEM) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, system.equals(min.getSystem()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_SYSTEM_MISMATCH, system, min.getSystem()) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(min.getCode()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CODE) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(code), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_NO_CODE) &&
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, definition.getMinValueQuantity().hasValue(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_VALUE)) {
+ if (code.equals(min.getCode())) {
+ // straight value comparison
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, checkDecimalMinValue(value, min.getValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG, value, min.getValue().toString());
+ } else if ("http://unitsofmeasure.org".equals(system)) {
+ if (warning(errors, IssueType.INVALID, element.line(), element.col(), path, context.getUcumService() != null, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_NO_UCUM_SVC)) {
+ Decimal v = convertUcumValue(value, code, min.getCode());
+ if (rule(errors, IssueType.INVALID, element.line(), element.col(), path, v != null, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_MIN_NO_CONVERT, value, code, min.getCode())) {
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, checkDecimalMinValue(v, min.getValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_VALUE_WRONG_UCUM, value, code, min.getValue().toString(), min.getCode());
+ }
+ }
+ } else {
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MIN_CODE_MISMATCH, code, min.getCode());
+ }
+ }
+ }
+ }
+
+ if (definition.hasMaxValue()) {
+ if (warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(value), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_VALUE) &&
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, definition.getMaxValue() instanceof Quantity, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_QTY, definition.getMaxValue().fhirType())) {
+ Quantity max = definition.getMaxValueQuantity();
+ if (warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(max.getSystem()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_SYSTEM) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(system), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_SYSTEM) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, system.equals(max.getSystem()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_SYSTEM_MISMATCH, system, max.getSystem()) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(max.getCode()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CODE) &&
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, !Utilities.noString(code), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_NO_CODE) &&
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, definition.getMaxValueQuantity().hasValue(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_VALUE)) {
+ if (code.equals(max.getCode())) {
+ // straight value comparison
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, checkDecimalMaxValue(value, max.getValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG, value, max.getValue().toString());
+ } else if ("http://unitsofmeasure.org".equals(system)) {
+ if (warning(errors, IssueType.INVALID, element.line(), element.col(), path, context.getUcumService() != null, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_NO_UCUM_SVC)) {
+ Decimal v = convertUcumValue(value, code, max.getCode());
+ if (rule(errors, IssueType.INVALID, element.line(), element.col(), path, v != null, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_MIN_NO_CONVERT, value, code, max.getCode())) {
+ rule(errors, IssueType.INVALID, element.line(), element.col(), path, checkDecimalMaxValue(v, max.getValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_VALUE_WRONG_UCUM, value, code, max.getValue().toString(), max.getCode());
+ }
+ }
+ } else {
+ warning(errors, IssueType.INVALID, element.line(), element.col(), path, false, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_QTY_MAX_CODE_MISMATCH, code, max.getCode());
+ }
+ }
+ }
+ }
+ }
+
+ private Decimal convertUcumValue(String value, String code, String minCode) {
+ try {
+ Decimal v = new Decimal(value);
+ return context.getUcumService().convert(v, code, minCode);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private boolean checkDecimalMaxValue(Decimal value, BigDecimal min) {
+ try {
+ Decimal m = new Decimal(min.toString());
+ return value.comparesTo(m) <= 0;
+ } catch (Exception e) {
+ return false; // this will be another error somewhere else?
+ }
+ }
+
+ private boolean checkDecimalMaxValue(String value, BigDecimal min) {
+ try {
+ BigDecimal v = new BigDecimal(value);
+ return v.compareTo(min) <= 0;
+ } catch (Exception e) {
+ return false; // this will be another error somewhere else
+ }
+ }
+
+ private boolean checkDecimalMinValue(Decimal value, BigDecimal min) {
+ try {
+ Decimal m = new Decimal(min.toString());
+ return value.comparesTo(m) >= 0;
+ } catch (Exception e) {
+ return false; // this will be another error somewhere else?
+ }
+ }
+
+ private boolean checkDecimalMinValue(String value, BigDecimal min) {
+ try {
+ BigDecimal v = new BigDecimal(value);
+ return v.compareTo(min) >= 0;
+ } catch (Exception e) {
+ return false; // this will be another error somewhere else
+ }
}
private void checkAttachment(List errors, String path, Element element, StructureDefinition theProfile, ElementDefinition definition, boolean theInCodeableConcept, boolean theCheckDisplayInContext, NodeStack theStack) {
@@ -5408,8 +5565,6 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), resourceName.equals(defn.getType()), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE,
defn.getType(), resourceName, defn.getUrl())) {
start(hostContext, errors, element, element, defn, stack); // root is both definition and type
- } else {
- System.out.println("what?");
}
}
}
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java
index 73c70bda2..947f3ec59 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/QuestionnaireValidator.java
@@ -10,6 +10,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.attoparser.config.ParseConfiguration.ElementBalancing;
import org.hl7.fhir.convertors.conv10_50.VersionConvertor_10_50;
import org.hl7.fhir.convertors.conv14_50.VersionConvertor_14_50;
import org.hl7.fhir.convertors.conv30_50.VersionConvertor_30_50;
@@ -40,6 +41,8 @@ import org.hl7.fhir.r5.model.TimeType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
+import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
@@ -52,6 +55,7 @@ import org.hl7.fhir.validation.cli.utils.QuestionnaireMode;
import org.hl7.fhir.validation.TimeTracker;
import org.hl7.fhir.validation.instance.EnableWhenEvaluator;
import org.hl7.fhir.validation.instance.EnableWhenEvaluator.QStack;
+import org.hl7.fhir.validation.instance.type.QuestionnaireValidator.ElementWithIndex;
import org.hl7.fhir.validation.instance.type.QuestionnaireValidator.QuestionnaireWithContext;
import org.hl7.fhir.validation.instance.utils.NodeStack;
import org.hl7.fhir.validation.instance.utils.ValidatorHostContext;
@@ -60,6 +64,26 @@ import ca.uhn.fhir.util.ObjectUtil;
public class QuestionnaireValidator extends BaseValidator {
+ public class ElementWithIndex {
+
+ private Element element;
+ private int index;
+
+ public ElementWithIndex(Element element, int index) {
+ this.element = element;
+ this.index = index;
+ }
+
+ public Element getElement() {
+ return element;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ }
+
public static class QuestionnaireWithContext {
private Questionnaire q;
private Element container;
@@ -88,7 +112,7 @@ public class QuestionnaireValidator extends BaseValidator {
public Questionnaire q() {
return q;
}
-
+
}
private EnableWhenEvaluator myEnableWhenEvaluator;
@@ -263,8 +287,9 @@ public class QuestionnaireValidator extends BaseValidator {
if (answers.size() > 1)
rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEA);
+ int i = 0;
for (Element answer : answers) {
- NodeStack ns = stack.push(answer, -1, null, null);
+ NodeStack ns = stack.push(answer, i, null, null);
if (qItem.getType() != null) {
switch (qItem.getType()) {
case GROUP:
@@ -337,12 +362,15 @@ public class QuestionnaireValidator extends BaseValidator {
case NULL:
// no validation
break;
+ case QUESTION:
+ throw new Error("Shouldn't get here?");
}
}
if (qItem.getType() != QuestionnaireItemType.GROUP) {
// if it's a group, we already have an error before getting here, so no need to hammer away on that
validateQuestionannaireResponseItems(hostContext, qsrc, qItem.getItem(), errors, answer, stack, inProgress, questionnaireResponseRoot, qstack);
}
+ i++;
}
if (qItem.getType() == null) {
fail(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTYPE, qItem.getLinkId());
@@ -363,14 +391,13 @@ public class QuestionnaireValidator extends BaseValidator {
return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP;
}
- private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
- if (elements.size() > 1)
- rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEI, qItem.getLinkId());
- int i = 0;
- for (Element element : elements) {
- NodeStack ns = stack.push(element, i, null, null);
- validateQuestionnaireResponseItem(hostcontext, qsrc, qItem, errors, element, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element));
- i++;
+ private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) {
+ if (elements.size() > 1) {
+ rule(errors, IssueType.INVALID, elements.get(1).getElement().line(), elements.get(1).getElement().col(), stack.getLiteralPath(), qItem.getRepeats(), I18nConstants.QUESTIONNAIRE_QR_ITEM_ONLYONEI, qItem.getLinkId());
+ }
+ for (ElementWithIndex element : elements) {
+ NodeStack ns = stack.push(element.getElement(), element.getIndex(), null, null);
+ validateQuestionnaireResponseItem(hostcontext, qsrc, qItem, errors, element.getElement(), ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, element.getElement()));
}
}
@@ -386,8 +413,9 @@ public class QuestionnaireValidator extends BaseValidator {
List items = new ArrayList();
element.getNamedChildren("item", items);
// now, sort into stacks
- Map> map = new HashMap>();
+ Map> map = new HashMap>();
int lastIndex = -1;
+ int counter = 0;
for (Element item : items) {
String linkId = item.getNamedChildValue("linkId");
if (rule(errors, IssueType.REQUIRED, item.line(), item.col(), stack.getLiteralPath(), !Utilities.noString(linkId), I18nConstants.QUESTIONNAIRE_QR_ITEM_NOLINKID)) {
@@ -396,7 +424,7 @@ public class QuestionnaireValidator extends BaseValidator {
QuestionnaireItemComponent qItem = findQuestionnaireItem(qsrc, linkId);
if (qItem != null) {
rule(errors, IssueType.STRUCTURE, item.line(), item.col(), stack.getLiteralPath(), index > -1, misplacedItemError(qItem));
- NodeStack ns = stack.push(item, -1, null, null);
+ NodeStack ns = stack.push(item, counter, null, null);
validateQuestionnaireResponseItem(hostContext, qsrc, qItem, errors, item, ns, inProgress, questionnaireResponseRoot, qstack.push(qItem, item));
} else
rule(errors, IssueType.NOTFOUND, item.line(), item.col(), stack.getLiteralPath(), index > -1, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTFOUND, linkId);
@@ -407,29 +435,28 @@ public class QuestionnaireValidator extends BaseValidator {
// If an item has a child called "linkId" but no child called "answer",
// we'll treat it as not existing for the purposes of enableWhen validation
if (item.hasChildren("answer") || item.hasChildren("item")) {
- List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>());
- mapItem.add(item);
+ List mapItem = map.computeIfAbsent(linkId, key -> new ArrayList<>());
+ mapItem.add(new ElementWithIndex(item, counter));
}
}
}
+ counter++;
}
// ok, now we have a list of known items, grouped by linkId. We've made an error for anything out of order
for (QuestionnaireItemComponent qItem : qItems) {
- List mapItem = map.get(qItem.getLinkId());
+ List mapItem = map.get(qItem.getLinkId());
validateQuestionnaireResponseItem(hostContext, qsrc, errors, element, stack, inProgress, questionnaireResponseRoot, qItem, mapItem, qstack);
}
}
- public void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) {
+ public void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QuestionnaireItemComponent qItem, List mapItem, QStack qstack) {
boolean enabled = myEnableWhenEvaluator.isQuestionEnabled(hostContext, qItem, qstack, fpe);
if (mapItem != null) {
if (!enabled) {
- int i = 0;
- for (Element e : mapItem) {
- NodeStack ns = stack.push(e, i, e.getProperty().getDefinition(), e.getProperty().getDefinition());
- rule(errors, IssueType.INVALID, e.line(), e.col(), ns.getLiteralPath(), enabled, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTENABLED2, qItem.getLinkId());
- i++;
+ for (ElementWithIndex e : mapItem) {
+ NodeStack ns = stack.push(e.getElement(), e.getElement().getIndex(), e.getElement().getProperty().getDefinition(), e.getElement().getProperty().getDefinition());
+ rule(errors, IssueType.INVALID, e.getElement().line(), e.getElement().col(), ns.getLiteralPath(), enabled, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOTENABLED2, qItem.getLinkId());
}
}
@@ -516,7 +543,8 @@ public class QuestionnaireValidator extends BaseValidator {
}
long t = System.nanoTime();
- ValidationResult res = context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, vs);
+ ValidationContextCarrier vc = makeValidationContext(errors, qSrc);
+ ValidationResult res = context.validateCode(new ValidationOptions(stack.getWorkingLang()), c, vs, vc);
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (!res.isOk()) {
txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_BADOPTION, c.getSystem(), c.getCode());
@@ -529,6 +557,16 @@ public class QuestionnaireValidator extends BaseValidator {
}
}
+ private ValidationContextCarrier makeValidationContext(List errors, QuestionnaireWithContext qSrc) {
+ ValidationContextCarrier vc = new ValidationContextCarrier();
+ if (qSrc.container == null) {
+ vc.getResources().add(new ValidationContextResourceProxy(qSrc.q));
+ } else {
+ vc.getResources().add(new ValidationContextResourceProxy(errors, qSrc.containerPath, qSrc.container, this));
+ }
+ return vc;
+ }
+
private void validateAnswerCode(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, boolean theOpenChoice) {
Element v = answer.getNamedChild("valueCoding");
NodeStack ns = stack.push(v, -1, null, null);
diff --git a/pom.xml b/pom.xml
index daff785df..6f274ab64 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,12 +14,12 @@
HAPI FHIR
-->
org.hl7.fhir.core
- 5.6.8-SNAPSHOT
+ 5.6.11-SNAPSHOT
pom
5.1.0
- 1.1.79
+ 1.1.82
5.7.1
1.7.1
3.0.0-M5