diff --git a/.github/workflows/bidi-checker.yml b/.github/workflows/bidi-checker.yml new file mode 100644 index 000000000..cae645b5f --- /dev/null +++ b/.github/workflows/bidi-checker.yml @@ -0,0 +1,28 @@ +name: BIDI CHECK + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [ master ] + pull_request: + branches: [ master ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + bidi_checker_job: + runs-on: ubuntu-latest + name: Check for bidi unicode characters in repo + steps: + # Checkout the repo code. IMPORTANT, this step is needed to populate the directory defined by GITHUB_WORKSPACE + - name: Checkout repo + uses: actions/checkout@v1 + id: checkout + # Run the check for bidi characters. + - name: Check for bidi characters + id: bidi_check + uses: HL7/bidi-checker-action@v1.5 + - name: Get the output time + run: echo "The time was ${{ steps.bidi_check.outputs.time }}" diff --git a/README.md b/README.md index cb4698831..67420ef39 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ | :---: | | [![Build Status][Badge-BuildPipeline]][Link-BuildPipeline] | -This is the core object handling code, with utilities (including validator), for the FHIR specification. +This is the java core object handling code, with utilities (including validator), for the FHIR specification. included in this repo: * org.fhir.fhir.utilities: Shared code used by all the other projects - including the internationalization code @@ -17,6 +17,11 @@ included in this repo: * org.fhir.fhir.validation: The FHIR Java validator (note: based on R5 internally, but validates all the above versions) * org.fhir.fhir.validation.cli: Holder project for releasing the FHIR validator as as single fat jar (will be removed in the future) +This code is used in all HAPI servers and clients, and also is the HL7 maintained +FHIR Validator. In addition, this is the core code for the HL7 maintained IG publisher +and FHIR main build publisher. As such, this code is considered an authoritatively +correct implementation of the core FHIR specification that it implements. + ### CI/CD All integration and delivery done on Azure pipelines. Azure project can be viewed [here][Link-AzureProject]. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 28a4a8ca4..e69de29bb 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,2 +0,0 @@ -Code changes: -* remove erroneous logging to c:\temp diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml index b97c0db7f..dbbf10efa 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml @@ -89,7 +89,7 @@ org.projectlombok lombok - 1.18.16 + ${lombok_version} provided diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/datatypes30_40/ElementDefinition30_40.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/datatypes30_40/ElementDefinition30_40.java index a8186f3bf..0138bebc6 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/datatypes30_40/ElementDefinition30_40.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_40/datatypes30_40/ElementDefinition30_40.java @@ -613,10 +613,10 @@ public class ElementDefinition30_40 { Type t = ConversionContext30_40.INSTANCE.getVersionConvertor_30_40().convertType(src.getValueSet()); if (t instanceof org.hl7.fhir.r4.model.Reference) { tgt.setValueSet(((org.hl7.fhir.r4.model.Reference) t).getReference()); - tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new UriType("Reference")); + tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new org.hl7.fhir.r4.model.UrlType("Reference")); } else { tgt.setValueSet(t.primitiveValue()); - tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new UriType("uri")); + tgt.getValueSetElement().addExtension(VersionConvertor_30_40.EXT_SRC_TYPE, new org.hl7.fhir.r4.model.UrlType("uri")); } tgt.setValueSet(VersionConvertorConstants.refToVS(tgt.getValueSet())); } diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java index 638cb7db7..ea475c06f 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java @@ -625,10 +625,10 @@ public class ElementDefinition30_50 { DataType t = ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().convertType(src.getValueSet()); if (t instanceof org.hl7.fhir.r5.model.Reference) { tgt.setValueSet(((org.hl7.fhir.r5.model.Reference) t).getReference()); - tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new UriType("Reference")); + tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new org.hl7.fhir.r5.model.UrlType("Reference")); } else { tgt.setValueSet(t.primitiveValue()); - tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new UriType("uri")); + tgt.getValueSetElement().addExtension(VersionConvertor_30_50.EXT_SRC_TYPE, new org.hl7.fhir.r5.model.UrlType("uri")); } tgt.setValueSet(VersionConvertorConstants.refToVS(tgt.getValueSet())); } diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java index 88f1f1fba..63a799d32 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java @@ -54,7 +54,7 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader { if (VersionUtilities.isR5Ver(npm.fhirVersion())) { return new R5ToR5Loader(types, lkp.forNewPackage(npm)); } else if (VersionUtilities.isR4Ver(npm.fhirVersion())) { - return new R4ToR5Loader(types, lkp.forNewPackage(npm)); + return new R4ToR5Loader(types, lkp.forNewPackage(npm), npm.version()); } else if (VersionUtilities.isR3Ver(npm.fhirVersion())) { return new R3ToR5Loader(types, lkp.forNewPackage(npm)); } else if (VersionUtilities.isR2Ver(npm.fhirVersion())) { diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java index ce196c2fb..04b12e807 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/R4ToR5Loader.java @@ -36,12 +36,14 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.formats.JsonParser; import org.hl7.fhir.r4.formats.XmlParser; import org.hl7.fhir.r4.model.Resource; +import org.hl7.fhir.r5.conformance.StructureDefinitionHacker; import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader; import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.model.Bundle.BundleType; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.utilities.VersionUtilities; import java.io.IOException; import java.io.InputStream; @@ -52,9 +54,11 @@ import java.util.UUID; public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader { private final BaseAdvisor_40_50 advisor = new BaseAdvisor_40_50(); + private String version; - public R4ToR5Loader(String[] types, ILoaderKnowledgeProviderR5 lkp) { + public R4ToR5Loader(String[] types, ILoaderKnowledgeProviderR5 lkp, String version) { // might be 4B super(types, lkp); + this.version = version; } @Override @@ -123,6 +127,9 @@ public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader if (killPrimitives) { throw new FHIRException("Cannot kill primitives when using deferred loading"); } + if (r5 instanceof StructureDefinition && VersionUtilities.isR4BVer(version)) { + r5 = new StructureDefinitionHacker(version).fixSD((StructureDefinition) r5); + } if (patchUrls) { if (r5 instanceof StructureDefinition) { StructureDefinition sd = (StructureDefinition) r5; diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/DicomPackageBuilder.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/DicomPackageBuilder.java index 84b192755..c7c405d6d 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/DicomPackageBuilder.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/DicomPackageBuilder.java @@ -80,7 +80,7 @@ public class DicomPackageBuilder { vs.setId(vs.getId().substring(0, 64)); } if (ids.contains(vs.getId())) { - throw new Error("Duplicate Id once Ids cut off at 64 char: "+vs.getId()); + throw new Error("Duplicate Id (note Ids cut off at 64 char): "+vs.getId()); } ids.add(vs.getId()); gen.addFile(Category.RESOURCE, "ValueSet-"+vs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(vs)); @@ -113,7 +113,7 @@ public class DicomPackageBuilder { private JsonObject buildPackage() { JsonObject npm = new JsonObject(); npm.addProperty("tools-version", 3); - npm.addProperty("type", "fhir.ig"); + npm.addProperty("type", "Conformance"); npm.addProperty("license", "free"); npm.addProperty("author", "FHIR Project for DICOM"); npm.addProperty("name", "fhir.dicom"); diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java new file mode 100644 index 000000000..634e3107b --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/XVerPackegeFixer.java @@ -0,0 +1,387 @@ +package org.hl7.fhir.convertors.misc; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.naming.ldap.StartTlsRequest; +import javax.xml.parsers.ParserConfigurationException; + +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.json.JsonTrackingParser; +import org.xml.sax.SAXException; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +public class XVerPackegeFixer { + + private static final String R5_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r5.core\\package"; + private static final String R4_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r4.core\\package"; + private static final String R3_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r3.core\\package"; + private static final String R2B_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2b.core\\package"; + private static final String R2_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2.core\\package"; + private static int mod; + + private static Map map5 = new HashMap<>(); + private static Map map4 = new HashMap<>(); + private static Map map3 = new HashMap<>(); + private static Map map2 = new HashMap<>(); + private static Map map2b = new HashMap<>(); + + public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException { + mod = 0; + for (File f : new File(args[0]).listFiles()) { + if (f.getName().startsWith("xver-")) { + JsonObject j = JsonTrackingParser.parseJson(f); + fixUp(j, f.getName()); + JsonTrackingParser.write(j, f, true); + } + } + System.out.println("all done: "+mod+" modifiers"); + } + + private static void fixUp(JsonObject j, String name) throws FHIRFormatError, FileNotFoundException, IOException { + name = name.replace(".json", ""); + System.out.println("Process "+name); + String version = name.substring(name.lastIndexOf("-")+1); + int i = 0; + for (Entry e : j.entrySet()) { + if (i == 50) { + i = 0; + System.out.print("."); + } + i++; + String n = e.getKey(); + JsonObject o = ((JsonObject) e.getValue()); + boolean ok = (o.has("types") && o.getAsJsonArray("types").size() > 0) || (o.has("elements") && o.getAsJsonArray("elements").size() > 0); + if (!ok) { + List types = new ArrayList<>(); + List elements = new ArrayList<>(); + getElementInfo(version, n, types, elements); + if (elements.size() > 0) { + JsonArray arr = o.getAsJsonArray("elements"); + if (arr == null) { + arr = new JsonArray(); + o.add("elements", arr); + } + for (String s : types) { + arr.add(s); + } + } else if (types.size() > 0) { + JsonArray arr = o.getAsJsonArray("types"); + if (arr == null) { + arr = new JsonArray(); + o.add("types", arr); + } + for (String s : types) { + arr.add(s); + } + } + } + } + System.out.println("done"); + } + + private static boolean getElementInfo(String version, String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + if ("contained".equals(n.substring(n.indexOf(".")+1))) { + return false; + } + switch (version) { + case "4.6": return getElementInfoR5(n, types, elements); + case "4.0": return getElementInfoR4(n, types, elements); + case "3.0": return getElementInfoR3(n, types, elements); + case "1.4": return getElementInfoR2B(n, types, elements); + case "1.0": return getElementInfoR2(n, types, elements); + } + return false; + } + + private static Object tail(String value) { + return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value; + } + + private static boolean getElementInfoR5(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + String tn = n.substring(0, n.indexOf(".")); + org.hl7.fhir.r5.model.StructureDefinition sd = null; + if (map5.containsKey(tn)) { + sd = map5.get(tn); + } else { + sd = (org.hl7.fhir.r5.model.StructureDefinition) new org.hl7.fhir.r5.formats.JsonParser().parse(new FileInputStream(Utilities.path(R5_FOLDER, "StructureDefinition-"+tn+".json"))); + map5.put(tn, sd); + } + for (org.hl7.fhir.r5.model.ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals(n)) { + List children = listChildrenR5(sd.getSnapshot().getElement(), ed); + if (children.size() > 0) { + for (org.hl7.fhir.r5.model.ElementDefinition c : children) { + String en = c.getPath().substring(ed.getPath().length()+1); + elements.add(en); + } + } else { + for (org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent t : ed.getType()) { + if (t.hasTargetProfile()) { + StringBuilder b = new StringBuilder(); + b.append(t.getWorkingCode()); + b.append("("); + boolean first = true; + for (org.hl7.fhir.r5.model.CanonicalType u : t.getTargetProfile()) { + if (first) first = false; else b.append("|"); + b.append(tail(u.getValue())); + } + b.append(")"); + types.add(b.toString()); + } else { + types.add(t.getWorkingCode()); + } + } + } + } + } + return false; + } + + + private static List listChildrenR5(List list, org.hl7.fhir.r5.model.ElementDefinition ed) { + List res = new ArrayList<>(); + for (org.hl7.fhir.r5.model.ElementDefinition t : list) { + String p = t.getPath(); + if (p.startsWith(ed.getPath()+".")) { + p = p.substring(ed.getPath().length()+1); + if (!p.contains(".")) { + res.add(t); + } + } + } + return res; + } + + private static boolean getElementInfoR4(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + String tn = n.substring(0, n.indexOf(".")); + org.hl7.fhir.r4.model.StructureDefinition sd = null; + if (map4.containsKey(tn)) { + sd = map4.get(tn); + } else { + sd = (org.hl7.fhir.r4.model.StructureDefinition) new org.hl7.fhir.r4.formats.JsonParser().parse(new FileInputStream(Utilities.path(R4_FOLDER, "StructureDefinition-"+tn+".json"))); + map4.put(tn, sd); + } + for (org.hl7.fhir.r4.model.ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals(n)) { + List children = listChildrenR4(sd.getSnapshot().getElement(), ed); + if (children.size() > 0) { + for (org.hl7.fhir.r4.model.ElementDefinition c : children) { + String en = c.getPath().substring(ed.getPath().length()+1); + elements.add(en); + } + } else { + for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : ed.getType()) { + if (t.hasTargetProfile()) { + StringBuilder b = new StringBuilder(); + b.append(t.getWorkingCode()); + b.append("("); + boolean first = true; + for (org.hl7.fhir.r4.model.CanonicalType u : t.getTargetProfile()) { + if (first) first = false; else b.append("|"); + b.append(tail(u.getValue())); + } + b.append(")"); + types.add(b.toString()); + } else { + types.add(t.getWorkingCode()); + } + } + } + } + } + return false; + } + + + private static List listChildrenR4(List list, org.hl7.fhir.r4.model.ElementDefinition ed) { + List res = new ArrayList<>(); + for (org.hl7.fhir.r4.model.ElementDefinition t : list) { + String p = t.getPath(); + if (p.startsWith(ed.getPath()+".")) { + p = p.substring(ed.getPath().length()+1); + if (!p.contains(".")) { + res.add(t); + } + } + } + return res; + } + + + private static boolean getElementInfoR3(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + String tn = n.substring(0, n.indexOf(".")); + org.hl7.fhir.dstu3.model.StructureDefinition sd = null; + if (map3.containsKey(tn)) { + sd = map3.get(tn); + } else { + sd = (org.hl7.fhir.dstu3.model.StructureDefinition) new org.hl7.fhir.dstu3.formats.JsonParser().parse(new FileInputStream(Utilities.path(R3_FOLDER, "StructureDefinition-"+tn+".json"))); + map3.put(tn, sd); + } + for (org.hl7.fhir.dstu3.model.ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals(n)) { + List children = listChildrenR3(sd.getSnapshot().getElement(), ed); + if (children.size() > 0) { + for (org.hl7.fhir.dstu3.model.ElementDefinition c : children) { + String en = c.getPath().substring(ed.getPath().length()+1); + elements.add(en); + } + } else { + for (org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent t : ed.getType()) { + if (t.hasTargetProfile()) { + StringBuilder b = new StringBuilder(); + b.append(t.getCode()); + b.append("("); + b.append(tail(t.getTargetProfile())); + b.append(")"); + types.add(b.toString()); + } else { + types.add(t.getCode()); + } + } + } + } + } + return false; + } + + + private static List listChildrenR3(List list, org.hl7.fhir.dstu3.model.ElementDefinition ed) { + List res = new ArrayList<>(); + for (org.hl7.fhir.dstu3.model.ElementDefinition t : list) { + String p = t.getPath(); + if (p.startsWith(ed.getPath()+".")) { + p = p.substring(ed.getPath().length()+1); + if (!p.contains(".")) { + res.add(t); + } + } + } + return res; + } + + + private static boolean getElementInfoR2(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + String tn = n.substring(0, n.indexOf(".")); + org.hl7.fhir.dstu2.model.StructureDefinition sd = null; + if (map2.containsKey(tn)) { + sd = map2.get(tn); + } else { + sd = (org.hl7.fhir.dstu2.model.StructureDefinition) new org.hl7.fhir.dstu2.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2_FOLDER, "StructureDefinition-"+tn+".json"))); + map2.put(tn, sd); + } + for (org.hl7.fhir.dstu2.model.ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals(n)) { + List children = listChildrenR2(sd.getSnapshot().getElement(), ed); + if (children.size() > 0) { + for (org.hl7.fhir.dstu2.model.ElementDefinition c : children) { + String en = c.getPath().substring(ed.getPath().length()+1); + elements.add(en); + } + } else { + for (org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent t : ed.getType()) { + if (t.hasProfile()) { + StringBuilder b = new StringBuilder(); + b.append(t.getCode()); + b.append("("); + boolean first = true; + for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) { + if (first) first = false; else b.append("|"); + b.append(tail(u.getValue())); + } + b.append(")"); + types.add(b.toString()); + } else { + types.add(t.getCode()); + } + } + } + } + } + return false; + } + + + private static List listChildrenR2(List list, org.hl7.fhir.dstu2.model.ElementDefinition ed) { + List res = new ArrayList<>(); + for (org.hl7.fhir.dstu2.model.ElementDefinition t : list) { + String p = t.getPath(); + if (p.startsWith(ed.getPath()+".")) { + p = p.substring(ed.getPath().length()+1); + if (!p.contains(".")) { + res.add(t); + } + } + } + return res; + } + + + private static boolean getElementInfoR2B(String n, List types, List elements) throws FHIRFormatError, FileNotFoundException, IOException { + String tn = n.substring(0, n.indexOf(".")); + org.hl7.fhir.dstu2016may.model.StructureDefinition sd = null; + if (map2b.containsKey(tn)) { + sd = map2b.get(tn); + } else { + sd = (org.hl7.fhir.dstu2016may.model.StructureDefinition) new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2B_FOLDER, "StructureDefinition-"+tn+".json"))); + map2b.put(tn, sd); + } + for (org.hl7.fhir.dstu2016may.model.ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals(n)) { + List children = listChildrenR2B(sd.getSnapshot().getElement(), ed); + if (children.size() > 0) { + for (org.hl7.fhir.dstu2016may.model.ElementDefinition c : children) { + String en = c.getPath().substring(ed.getPath().length()+1); + elements.add(en); + } + } else { + for (org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent t : ed.getType()) { + if (t.hasProfile()) { + StringBuilder b = new StringBuilder(); + b.append(t.getCode()); + b.append("("); + boolean first = true; + for (org.hl7.fhir.dstu2016may.model.UriType u : t.getProfile()) { + if (first) first = false; else b.append("|"); + b.append(tail(u.getValue())); + } + b.append(")"); + types.add(b.toString()); + } else { + types.add(t.getCode()); + } + } + } + } + } + return false; + } + + + private static List listChildrenR2B(List list, org.hl7.fhir.dstu2016may.model.ElementDefinition ed) { + List res = new ArrayList<>(); + for (org.hl7.fhir.dstu2016may.model.ElementDefinition t : list) { + String p = t.getPath(); + if (p.startsWith(ed.getPath()+".")) { + p = p.substring(ed.getPath().length()+1); + if (!p.contains(".")) { + res.add(t); + } + } + } + return res; + } + + +} diff --git a/org.hl7.fhir.dstu2/pom.xml b/org.hl7.fhir.dstu2/pom.xml index d2abf14f9..fd1ab4123 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IWorkerContext.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IWorkerContext.java index cac9c6fba..6eab85c3d 100644 --- a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IWorkerContext.java +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IWorkerContext.java @@ -1,33 +1,33 @@ package org.hl7.fhir.dstu2.utils; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -46,6 +46,7 @@ import org.hl7.fhir.dstu2.model.ValueSet.ConceptDefinitionComponent; import org.hl7.fhir.dstu2.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu2.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu2.terminologies.ValueSetExpander.ValueSetExpansionOutcome; +import org.hl7.fhir.dstu2.utils.validation.IResourceValidator; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/SimpleWorkerContext.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/SimpleWorkerContext.java index f64a7dbbe..70e3a39e8 100644 --- a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/SimpleWorkerContext.java +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/SimpleWorkerContext.java @@ -1,33 +1,33 @@ package org.hl7.fhir.dstu2.utils; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -59,6 +59,7 @@ import org.hl7.fhir.dstu2.model.ValueSet; import org.hl7.fhir.dstu2.terminologies.ValueSetExpansionCache; import org.hl7.fhir.dstu2.utils.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient; +import org.hl7.fhir.dstu2.utils.validation.IResourceValidator; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.utilities.CSFileInputStream; diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IResourceValidator.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/IResourceValidator.java similarity index 83% rename from org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IResourceValidator.java rename to org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/IResourceValidator.java index c12ffd40c..dd6717a1f 100644 --- a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/IResourceValidator.java +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/IResourceValidator.java @@ -1,39 +1,42 @@ -package org.hl7.fhir.dstu2.utils; +package org.hl7.fhir.dstu2.utils.validation; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ import java.util.List; import org.hl7.fhir.dstu2.model.StructureDefinition; +import org.hl7.fhir.dstu2.utils.validation.constants.BestPracticeWarningLevel; +import org.hl7.fhir.dstu2.utils.validation.constants.CheckDisplayOption; +import org.hl7.fhir.dstu2.utils.validation.constants.IdStatus; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -42,29 +45,6 @@ import com.google.gson.JsonObject; public interface IResourceValidator { - /** - * whether the validator should enforce best practice guidelines - * as defined by various HL7 committees - * - * - * @author Grahame Grieve - * - */ - public enum BestPracticeWarningLevel { - Ignore, - Hint, - Warning, - Error - } - - public enum CheckDisplayOption { - Ignore, - Check, - CheckCaseAndSpace, - CheckCase, - CheckSpace - } - /** * how much to check displays for coded elements * @return @@ -77,10 +57,6 @@ public interface IResourceValidator { */ void setCheckDisplay(CheckDisplayOption checkDisplay); - enum IdStatus { - OPTIONAL, REQUIRED, PROHIBITED - } - /** * whether the resource must have an id or not (depends on context) * @@ -92,40 +68,27 @@ public interface IResourceValidator { BestPracticeWarningLevel getBasePracticeWarningLevel(); void setBestPracticeWarningLevel(BestPracticeWarningLevel value); - - + /** * Given a DOM element, return a list of errors in the resource - * - * @param errors - * @param elem * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, Element element) throws Exception; /** * Given a JSON Object, return a list of errors in the resource - * - * @param errors - * @param elem * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, JsonObject object) throws Exception; /** * Given a DOM element, return a list of errors in the resource - * - * @param errors - * @param elem * @- if the underlying infrastructure fails (not if the resource is invalid) */ List validate(Element element) throws Exception; /** * Given a DOM element, return a list of errors in the resource - * - * @param errors - * @param elem * @- if the underlying infrastructure fails (not if the resource is invalid) */ List validate(JsonObject object) throws Exception; @@ -133,10 +96,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile (by logical identifier) - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid) */ void validate(List errors, Element element, String profile) throws Exception; @@ -144,10 +103,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile (by logical identifier) - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid) */ List validate(Element element, String profile) throws Exception; @@ -155,10 +110,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile (by logical identifier) - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid) */ List validate(JsonObject object, StructureDefinition profile) throws Exception; @@ -166,10 +117,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile (by logical identifier) - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid) */ List validate(JsonObject object, String profile) throws Exception; @@ -177,10 +124,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, Element element, StructureDefinition profile) throws Exception; @@ -188,10 +131,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, JsonObject object, StructureDefinition profile) throws Exception; @@ -199,10 +138,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, JsonObject object, String profile) throws Exception; @@ -210,10 +145,6 @@ public interface IResourceValidator { /** * Given a DOM element, return a list of errors in the resource * with regard to the specified profile - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails (not if the resource is invalid) */ List validate(Element element, StructureDefinition profile) throws Exception; @@ -221,18 +152,12 @@ public interface IResourceValidator { /** * Given a DOM document, return a list of errors in the resource - * - * @param errors - * @param elem * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, Document document) throws Exception; /** * Given a DOM document, return a list of errors in the resource - * - * @param errors - * @param elem * @- if the underlying infrastructure fails (not if the resource is invalid) */ List validate(Document document) throws Exception; @@ -240,10 +165,6 @@ public interface IResourceValidator { /** * Given a DOM document, return a list of errors in the resource * with regard to the specified profile (by logical identifier) - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid) */ void validate(List errors, Document document, String profile) throws Exception; @@ -251,10 +172,6 @@ public interface IResourceValidator { /** * Given a DOM document, return a list of errors in the resource * with regard to the specified profile (by logical identifier) - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails, or the profile can't be found (not if the resource is invalid) */ List validate(Document document, String profile) throws Exception; @@ -262,10 +179,6 @@ public interface IResourceValidator { /** * Given a DOM document, return a list of errors in the resource * with regard to the specified profile - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails (not if the resource is invalid) */ void validate(List errors, Document document, StructureDefinition profile) throws Exception; @@ -273,10 +186,6 @@ public interface IResourceValidator { /** * Given a DOM document, return a list of errors in the resource * with regard to the specified profile - * - * @param errors - * @param element - * @param profile * @- if the underlying infrastructure fails (not if the resource is invalid) */ List validate(Document document, StructureDefinition profile) throws Exception; diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/BestPracticeWarningLevel.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/BestPracticeWarningLevel.java new file mode 100644 index 000000000..3ea64e188 --- /dev/null +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/BestPracticeWarningLevel.java @@ -0,0 +1,12 @@ +package org.hl7.fhir.dstu2.utils.validation.constants; + +/** + * whether the validator should enforce best practice guidelines + * as defined by various HL7 committees + */ +public enum BestPracticeWarningLevel { + Ignore, + Hint, + Warning, + Error +} diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/CheckDisplayOption.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/CheckDisplayOption.java new file mode 100644 index 000000000..ba2958efe --- /dev/null +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/CheckDisplayOption.java @@ -0,0 +1,9 @@ +package org.hl7.fhir.dstu2.utils.validation.constants; + +public enum CheckDisplayOption { + Ignore, + Check, + CheckCaseAndSpace, + CheckCase, + CheckSpace +} diff --git a/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/IdStatus.java b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/IdStatus.java new file mode 100644 index 000000000..22ac0e3de --- /dev/null +++ b/org.hl7.fhir.dstu2/src/main/java/org/hl7/fhir/dstu2/utils/validation/constants/IdStatus.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.dstu2.utils.validation.constants; + +public enum IdStatus { + OPTIONAL, + REQUIRED, + PROHIBITED +} diff --git a/org.hl7.fhir.dstu2016may/pom.xml b/org.hl7.fhir.dstu2016may/pom.xml index 615f6c0a0..0044f9b23 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/pom.xml b/org.hl7.fhir.dstu3/pom.xml index daadbc538..2f0859a2f 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/IWorkerContext.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/IWorkerContext.java index a0962e269..75d922a1d 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/IWorkerContext.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/IWorkerContext.java @@ -1,33 +1,33 @@ package org.hl7.fhir.dstu3.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -52,7 +52,7 @@ import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.dstu3.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.dstu3.utils.INarrativeGenerator; -import org.hl7.fhir.dstu3.utils.IResourceValidator; +import org.hl7.fhir.dstu3.utils.validation.IResourceValidator; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java index 35bd0485f..0dd0887fe 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/context/SimpleWorkerContext.java @@ -1,33 +1,33 @@ package org.hl7.fhir.dstu3.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -80,7 +80,7 @@ import org.hl7.fhir.dstu3.model.StructureMap.StructureMapStructureComponent; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.terminologies.ValueSetExpansionCache; import org.hl7.fhir.dstu3.utils.INarrativeGenerator; -import org.hl7.fhir.dstu3.utils.IResourceValidator; +import org.hl7.fhir.dstu3.utils.validation.IResourceValidator; import org.hl7.fhir.dstu3.utils.NarrativeGenerator; import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient; import org.hl7.fhir.exceptions.DefinitionException; diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java index 3508a9adb..870957cda 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilder.java @@ -206,14 +206,19 @@ public class FhirRequestBuilder { public ResourceRequest execute() throws IOException { formatHeaders(httpRequest, resourceFormat, headers); - Response response = getHttpClient().newCall(httpRequest.build()).execute(); + final Request request = httpRequest.build(); + log(request.method(), request.url().toString(), request.headers(), request.body() != null ? request.body().toString().getBytes() : null); + Response response = getHttpClient().newCall(request).execute(); T resource = unmarshalReference(response, resourceFormat); return new ResourceRequest(resource, response.code(), getLocationHeader(response.headers())); } public Bundle executeAsBatch() throws IOException { formatHeaders(httpRequest, resourceFormat, null); - Response response = getHttpClient().newCall(httpRequest.build()).execute(); + final Request request = httpRequest.build(); + log(request.method(), request.url().toString(), request.headers(), request.body() != null ? request.body().toString().getBytes() : null); + + Response response = getHttpClient().newCall(request).execute(); return unmarshalFeed(response, resourceFormat); } @@ -302,6 +307,26 @@ public class FhirRequestBuilder { } } + /** + * Logs the given {@link Request}, using the current {@link ToolingClientLogger}. If the current + * {@link FhirRequestBuilder#logger} is null, no action is taken. + * + * @param method HTTP request method + * @param url request URL + * @param requestHeaders {@link Headers} for request + * @param requestBody Byte array request + */ + protected void log(String method, String url, Headers requestHeaders, byte[] requestBody) { + if (logger != null) { + List headerList = new ArrayList<>(Collections.emptyList()); + Map> headerMap = requestHeaders.toMultimap(); + headerMap.keySet().forEach(key -> headerMap.get(key).forEach(value -> headerList.add(key + ":" + value))); + + logger.logRequest(method, url, headerList, requestBody); + } + + } + /** * Logs the given {@link Response}, using the current {@link ToolingClientLogger}. If the current * {@link FhirRequestBuilder#logger} is null, no action is taken. diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/RetryInterceptor.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/RetryInterceptor.java index 70c40f0f0..fd0a25202 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/RetryInterceptor.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/client/network/RetryInterceptor.java @@ -34,8 +34,8 @@ public class RetryInterceptor implements Interceptor { try { // If we are retrying a failed request that failed due to a bad response from the server, we must close it first if (response != null) { - System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code()) - + "> from url -> " + chain.request().url() + "."); +// System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code()) +// + "> from url -> " + chain.request().url() + "."); response.close(); } // System.out.println(chain.request().method() + " attempt <" + (retryCounter + 1) + "> to url -> " + chain.request().url()); diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/IResourceValidator.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IResourceValidator.java similarity index 83% rename from org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/IResourceValidator.java rename to org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IResourceValidator.java index 2701d3f60..529f433fe 100644 --- a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/IResourceValidator.java +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IResourceValidator.java @@ -1,50 +1,49 @@ -package org.hl7.fhir.dstu3.utils; +package org.hl7.fhir.dstu3.utils.validation; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ +import com.google.gson.JsonObject; +import org.hl7.fhir.dstu3.elementmodel.Manager.FhirFormat; +import org.hl7.fhir.dstu3.model.StructureDefinition; +import org.hl7.fhir.dstu3.utils.ValidationProfileSet; +import org.hl7.fhir.dstu3.utils.validation.constants.BestPracticeWarningLevel; +import org.hl7.fhir.dstu3.utils.validation.constants.CheckDisplayOption; +import org.hl7.fhir.dstu3.utils.validation.constants.IdStatus; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.utilities.validation.ValidationMessage; import java.io.IOException; import java.io.InputStream; import java.util.List; -import org.hl7.fhir.dstu3.elementmodel.Element; -import org.hl7.fhir.dstu3.elementmodel.Manager.FhirFormat; -import org.hl7.fhir.dstu3.model.StructureDefinition; -import org.hl7.fhir.exceptions.DefinitionException; -import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.utilities.validation.ValidationMessage; - -import com.google.gson.JsonObject; - /** * Interface to the instance validator. This takes a resource, in one of many forms, and * checks whether it is valid @@ -54,49 +53,6 @@ import com.google.gson.JsonObject; */ public interface IResourceValidator { - public enum ReferenceValidationPolicy { - IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID; - - public boolean checkExists() { - return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID; - } - - public boolean checkType() { - return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID; - } - - public boolean checkValid() { - return this == CHECK_VALID; - } - } - - public interface IValidatorResourceFetcher { - Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, IOException, FHIRException; - ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url); - boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException; - } - - public enum BestPracticeWarningLevel { - Ignore, - Hint, - Warning, - Error - } - - public enum CheckDisplayOption { - Ignore, - Check, - CheckCaseAndSpace, - CheckCase, - CheckSpace - } - - enum IdStatus { - OPTIONAL, REQUIRED, PROHIBITED - } - - - /** * how much to check displays for coded elements * @return @@ -123,22 +79,25 @@ public interface IResourceValidator { IValidatorResourceFetcher getFetcher(); IResourceValidator setFetcher(IValidatorResourceFetcher value); - + + IValidationPolicyAdvisor getPolicyAdvisor(); + IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor); + boolean isNoBindingMsgSuppressed(); IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed); - public boolean isNoInvariantChecks(); - public IResourceValidator setNoInvariantChecks(boolean value) ; + boolean isNoInvariantChecks(); + IResourceValidator setNoInvariantChecks(boolean value) ; - public boolean isNoTerminologyChecks(); - public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks); + boolean isNoTerminologyChecks(); + IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks); /** * Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning * @return */ - public boolean isErrorForUnknownProfiles(); - public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles); + boolean isErrorForUnknownProfiles(); + void setErrorForUnknownProfiles(boolean errorForUnknownProfiles); /** * Validate suite diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IValidationPolicyAdvisor.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IValidationPolicyAdvisor.java new file mode 100644 index 000000000..3f7536bc0 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IValidationPolicyAdvisor.java @@ -0,0 +1,21 @@ +package org.hl7.fhir.dstu3.utils.validation; + +import org.hl7.fhir.dstu3.elementmodel.Element; +import org.hl7.fhir.dstu3.utils.validation.constants.ReferenceValidationPolicy; + +public interface IValidationPolicyAdvisor { + + ReferenceValidationPolicy policyForReference(IResourceValidator validator, + Object appContext, + String path, + String url); + + ReferenceValidationPolicy policyForContained(IResourceValidator validator, + Object appContext, + String containerType, + String containerId, + Element.SpecialElement containingResourceType, + String path, + String url); + +} \ No newline at end of file diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IValidatorResourceFetcher.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IValidatorResourceFetcher.java new file mode 100644 index 000000000..d2fb0ade1 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/IValidatorResourceFetcher.java @@ -0,0 +1,13 @@ +package org.hl7.fhir.dstu3.utils.validation; + +import org.hl7.fhir.dstu3.elementmodel.Element; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; + +import java.io.IOException; + +public interface IValidatorResourceFetcher { + Element fetch(Object appContext, String url) throws IOException, FHIRException; + boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException; +} diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/BestPracticeWarningLevel.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/BestPracticeWarningLevel.java new file mode 100644 index 000000000..55bf800a3 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/BestPracticeWarningLevel.java @@ -0,0 +1,8 @@ +package org.hl7.fhir.dstu3.utils.validation.constants; + +public enum BestPracticeWarningLevel { + Ignore, + Hint, + Warning, + Error +} diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/CheckDisplayOption.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/CheckDisplayOption.java new file mode 100644 index 000000000..414371393 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/CheckDisplayOption.java @@ -0,0 +1,9 @@ +package org.hl7.fhir.dstu3.utils.validation.constants; + +public enum CheckDisplayOption { + Ignore, + Check, + CheckCaseAndSpace, + CheckCase, + CheckSpace +} diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/IdStatus.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/IdStatus.java new file mode 100644 index 000000000..d4bb24f70 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/IdStatus.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.dstu3.utils.validation.constants; + +public enum IdStatus { + OPTIONAL, + REQUIRED, + PROHIBITED +} diff --git a/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/ReferenceValidationPolicy.java b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/ReferenceValidationPolicy.java new file mode 100644 index 000000000..efe07ec42 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/main/java/org/hl7/fhir/dstu3/utils/validation/constants/ReferenceValidationPolicy.java @@ -0,0 +1,21 @@ +package org.hl7.fhir.dstu3.utils.validation.constants; + +public enum ReferenceValidationPolicy { + IGNORE, + CHECK_TYPE_IF_EXISTS, + CHECK_EXISTS, + CHECK_EXISTS_AND_TYPE, + CHECK_VALID; + + public boolean checkExists() { + return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID; + } + + public boolean checkType() { + return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID; + } + + public boolean checkValid() { + return this == CHECK_VALID; + } +} diff --git a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java new file mode 100644 index 000000000..448af2a78 --- /dev/null +++ b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/utils/client/network/FhirRequestBuilderTests.java @@ -0,0 +1,101 @@ +package org.hl7.fhir.dstu3.utils.client.network; + +import okhttp3.*; +import org.hl7.fhir.dstu3.formats.IParser; + +import org.hl7.fhir.utilities.ToolingClientLogger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.ArgumentMatchers; + +import org.mockito.junit.jupiter.MockitoExtension; +import java.io.IOException; + +@ExtendWith(MockitoExtension.class) +public class FhirRequestBuilderTests { + + private static final String DUMMY_URL = "https://some-url.com/"; + + Request mockRequest = new Request.Builder() + .url(DUMMY_URL) + .build(); + + final String RESPONSE_BODY_STRING = "{}"; + + Response response = new Response.Builder() + .request(mockRequest) + .protocol(Protocol.HTTP_2) + .code(200) // status code + .message("") + .body(ResponseBody.create(RESPONSE_BODY_STRING, + MediaType.get("application/json; charset=utf-8") + )) + .addHeader("Content-Type", "") + .build(); + + final Request.Builder requestBuilder = new Request.Builder() + .url(DUMMY_URL); + + final FhirRequestBuilder fhirRequestBuilder = Mockito.spy(new FhirRequestBuilder(requestBuilder)); + + @Mock + OkHttpClient client; + + @Mock + Call mockCall; + + @Mock + ToolingClientLogger logger; + + @BeforeEach + public void beforeEach() { + Mockito.doReturn(client).when(fhirRequestBuilder).getHttpClient(); + fhirRequestBuilder.withLogger(logger); + } + + @Nested + class RequestLoggingTests { + + @BeforeEach + public void beforeEach() throws IOException { + Mockito.doReturn(response).when(mockCall).execute(); + Mockito.doReturn(mockCall).when(client).newCall(ArgumentMatchers.any()); + + Mockito.doReturn(null).when(fhirRequestBuilder).unmarshalReference(ArgumentMatchers.any(), ArgumentMatchers.isNull()); + } + + @Test + public void testExecuteLogging() throws IOException { + fhirRequestBuilder.execute(); + Mockito.verify(logger).logRequest(ArgumentMatchers.eq("GET"), ArgumentMatchers.eq(DUMMY_URL), ArgumentMatchers.anyList(), ArgumentMatchers.isNull()); + } + + @Test + public void testExecuteBatchLogging() throws IOException { + fhirRequestBuilder.executeAsBatch(); + Mockito.verify(logger).logRequest(ArgumentMatchers.eq("GET"), ArgumentMatchers.eq(DUMMY_URL), ArgumentMatchers.anyList(), ArgumentMatchers.isNull()); + } + + } + + @Test + public void testUnmarshallReferenceLogging() { + IParser parser = Mockito.mock(IParser.class); + Mockito.doReturn(parser).when(fhirRequestBuilder).getParser(ArgumentMatchers.eq("json")); + + fhirRequestBuilder.unmarshalReference(response, "json"); + Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes())); + } + + @Test + public void testUnmarshallFeedLogging() { + fhirRequestBuilder.unmarshalFeed(response, "application/json"); + Mockito.verify(logger).logResponse(ArgumentMatchers.eq("200"), ArgumentMatchers.anyList(), AdditionalMatchers.aryEq(RESPONSE_BODY_STRING.getBytes())); + } + +} diff --git a/org.hl7.fhir.r4/pom.xml b/org.hl7.fhir.r4/pom.xml index d809dd10d..85cb06971 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java index f6c943a58..0e420ebf6 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/IWorkerContext.java @@ -1,39 +1,38 @@ package org.hl7.fhir.r4.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import org.fhir.ucum.UcumService; @@ -58,8 +57,7 @@ import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r4.terminologies.ValueSetExpander.TerminologyServiceErrorClass; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.utils.INarrativeGenerator; -import org.hl7.fhir.r4.utils.IResourceValidator; -import org.hl7.fhir.utilities.TerminologyServiceOptions; +import org.hl7.fhir.r4.utils.validation.IResourceValidator; import org.hl7.fhir.utilities.TranslationServices; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationOptions; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java index 72a0f050c..c2c5c332f 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/context/SimpleWorkerContext.java @@ -1,33 +1,33 @@ package org.hl7.fhir.r4.context; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -75,7 +75,7 @@ import org.hl7.fhir.r4.model.StructureMap.StructureMapModelMode; import org.hl7.fhir.r4.model.StructureMap.StructureMapStructureComponent; import org.hl7.fhir.r4.terminologies.TerminologyClient; import org.hl7.fhir.r4.utils.INarrativeGenerator; -import org.hl7.fhir.r4.utils.IResourceValidator; +import org.hl7.fhir.r4.utils.validation.IResourceValidator; import org.hl7.fhir.r4.utils.NarrativeGenerator; import org.hl7.fhir.utilities.CSFileInputStream; import org.hl7.fhir.utilities.Utilities; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Enumerations.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Enumerations.java index 25e2b377f..25bf7f2a5 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Enumerations.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Enumerations.java @@ -10035,7 +10035,8 @@ The primary difference between a medication statement and a medication administr /** * R4B - manually added */ - _4_1_0, + _4_1_0, + _4_3_0_CIBUILD, NULL; public static FHIRVersion fromCode(String codeString) throws FHIRException { if (codeString == null || "".equals(codeString)) @@ -10088,6 +10089,8 @@ The primary difference between a medication statement and a medication administr return _4_0_1; if ("4.1.0".equals(codeString)) return _4_1_0; + if ("4.3.0-CIBUILD".equals(codeString)) + return _4_3_0_CIBUILD; throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'"); } @Override @@ -10120,6 +10123,8 @@ The primary difference between a medication statement and a medication administr case _4_0_0: return "4.0.0"; case _4_0_1: return "4.0.1"; case _4_1_0: return "4.1.0"; + case _4_3_0_CIBUILD: return "4.3.0-CIBUILD"; + case NULL: return null; default: return "?"; } @@ -10150,6 +10155,7 @@ The primary difference between a medication statement and a medication administr case _4_0_0: return "http://hl7.org/fhir/FHIR-version"; case _4_0_1: return "http://hl7.org/fhir/FHIR-version"; case _4_1_0: return "http://hl7.org/fhir/FHIR-version"; + case _4_3_0_CIBUILD: return "http://hl7.org/fhir/FHIR-version"; case NULL: return null; default: return "?"; } @@ -10179,7 +10185,8 @@ The primary difference between a medication statement and a medication administr case _3_5_0: return "R4 Ballot #2."; case _4_0_0: return "FHIR Release 4 (Normative + STU)."; case _4_0_1: return "FHIR Release 4 Technical Correction #1."; - case _4_1_0: return "FHIR Release 4B"; + case _4_1_0: return "FHIR Release 4B Ballot #1"; + case _4_3_0_CIBUILD: return "FHIR Release 4B CI-Builld"; case NULL: return null; default: return "?"; } @@ -10210,6 +10217,7 @@ The primary difference between a medication statement and a medication administr case _4_0_0: return "4.0.0"; case _4_0_1: return "4.0.1"; case _4_1_0: return "4.1.0"; + case _4_3_0_CIBUILD: return "4.3.0-CIBUILD"; case NULL: return null; default: return "?"; } @@ -10275,6 +10283,8 @@ The primary difference between a medication statement and a medication administr return FHIRVersion._4_0_1; if ("4.1.0".equals(codeString)) return FHIRVersion._4_1_0; + if ("4.3.0-CIBUILD".equals(codeString)) + return FHIRVersion._4_3_0_CIBUILD; throw new IllegalArgumentException("Unknown FHIRVersion code '"+codeString+"'"); } public Enumeration fromType(Base code) throws FHIRException { @@ -10333,6 +10343,8 @@ The primary difference between a medication statement and a medication administr return new Enumeration(this, FHIRVersion._4_0_1); if ("4.1.0".equals(codeString)) return new Enumeration(this, FHIRVersion._4_1_0); + if ("4.3.0-CIBUILD".equals(codeString)) + return new Enumeration(this, FHIRVersion._4_3_0_CIBUILD); throw new FHIRException("Unknown FHIRVersion code '"+codeString+"'"); } public String toCode(FHIRVersion code) { @@ -10384,6 +10396,8 @@ The primary difference between a medication statement and a medication administr return "4.0.1"; if (code == FHIRVersion._4_1_0) return "4.1.0"; + if (code == FHIRVersion._4_3_0_CIBUILD) + return "4.3.0_CIBUILD"; return "?"; } public String toSystem(FHIRVersion code) { diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java index ae4524927..65286544b 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/StructureMapUtilities.java @@ -1,33 +1,33 @@ package org.hl7.fhir.r4.utils; -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ @@ -115,6 +115,7 @@ import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; +import org.hl7.fhir.r4.utils.validation.IResourceValidator; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TerminologyServiceOptions; import org.hl7.fhir.utilities.Utilities; diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java index 9775308b5..16f8aa443 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java @@ -2,8 +2,8 @@ package org.hl7.fhir.r4.utils.client.network; import okhttp3.*; import org.hl7.fhir.utilities.ToolingClientLogger; -import org.jetbrains.annotations.NotNull; +import javax.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -22,9 +22,8 @@ public class FhirLoggingInterceptor implements Interceptor { return this; } - @NotNull @Override - public Response intercept(@NotNull Interceptor.Chain chain) throws IOException { + public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException { // Log Request Request request = chain.request(); logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()), diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/RetryInterceptor.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/RetryInterceptor.java index dd0ec0def..b76026466 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/RetryInterceptor.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/RetryInterceptor.java @@ -34,8 +34,8 @@ public class RetryInterceptor implements Interceptor { try { // If we are retrying a failed request that failed due to a bad response from the server, we must close it first if (response != null) { - System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code()) - + "> from url -> " + chain.request().url() + "."); +// System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code()) +// + "> from url -> " + chain.request().url() + "."); response.close(); } // System.out.println(chain.request().method() + " attempt <" + (retryCounter + 1) + "> to url -> " + chain.request().url()); diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/IResourceValidator.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IResourceValidator.java similarity index 79% rename from org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/IResourceValidator.java rename to org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IResourceValidator.java index 864fe831f..e94333c3b 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/IResourceValidator.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IResourceValidator.java @@ -1,46 +1,45 @@ -package org.hl7.fhir.r4.utils; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ - +package org.hl7.fhir.r4.utils.validation; +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + */ import java.io.IOException; import java.io.InputStream; import java.util.List; -import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.r4.elementmodel.Element; import org.hl7.fhir.r4.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r4.model.StructureDefinition; +import org.hl7.fhir.r4.utils.ValidationProfileSet; +import org.hl7.fhir.r4.utils.validation.constants.BestPracticeWarningLevel; +import org.hl7.fhir.r4.utils.validation.constants.CheckDisplayOption; +import org.hl7.fhir.r4.utils.validation.constants.IdStatus; import org.hl7.fhir.utilities.validation.ValidationMessage; import com.google.gson.JsonObject; @@ -54,97 +53,51 @@ import com.google.gson.JsonObject; */ public interface IResourceValidator { - public enum ReferenceValidationPolicy { - IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID; - - public boolean checkExists() { - return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID; - } - - public boolean checkType() { - return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID; - } - - public boolean checkValid() { - return this == CHECK_VALID; - } - } - - public interface IValidatorResourceFetcher { - Element fetch(Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException; - ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url); - boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException; - } - - public enum BestPracticeWarningLevel { - Ignore, - Hint, - Warning, - Error - } - - public enum CheckDisplayOption { - Ignore, - Check, - CheckCaseAndSpace, - CheckCase, - CheckSpace - } - - enum IdStatus { - OPTIONAL, REQUIRED, PROHIBITED - } - - - /** * how much to check displays for coded elements - * @return */ CheckDisplayOption getCheckDisplay(); void setCheckDisplay(CheckDisplayOption checkDisplay); /** * whether the resource must have an id or not (depends on context) - * - * @return */ - IdStatus getResourceIdRule(); void setResourceIdRule(IdStatus resourceIdRule); /** * whether the validator should enforce best practice guidelines * as defined by various HL7 committees - * */ BestPracticeWarningLevel getBestPracticeWarningLevel(); IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value); IValidatorResourceFetcher getFetcher(); IResourceValidator setFetcher(IValidatorResourceFetcher value); + + IValidationPolicyAdvisor getPolicyAdvisor(); + IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor); boolean isNoBindingMsgSuppressed(); IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed); - public boolean isNoInvariantChecks(); - public IResourceValidator setNoInvariantChecks(boolean value) ; + boolean isNoInvariantChecks(); + IResourceValidator setNoInvariantChecks(boolean value) ; - public boolean isNoTerminologyChecks(); - public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks); + boolean isNoTerminologyChecks(); + IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks); - public boolean isNoExtensibleWarnings(); - public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings); + boolean isNoExtensibleWarnings(); + IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings); /** * Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning - * @return */ - public boolean isErrorForUnknownProfiles(); - public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles); + boolean isErrorForUnknownProfiles(); + void setErrorForUnknownProfiles(boolean errorForUnknownProfiles); - public String getValidationLanguage(); - public void setValidationLanguage(String value); + String getValidationLanguage(); + void setValidationLanguage(String value); /** * Validate suite @@ -194,5 +147,4 @@ public interface IResourceValidator { org.hl7.fhir.r4.elementmodel.Element validate(Object Context, List errors, JsonObject object, String profile) throws FHIRException; org.hl7.fhir.r4.elementmodel.Element validate(Object Context, List errors, JsonObject object, StructureDefinition profile) throws FHIRException; - } \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IValidationPolicyAdvisor.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IValidationPolicyAdvisor.java new file mode 100644 index 000000000..700c1802e --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IValidationPolicyAdvisor.java @@ -0,0 +1,21 @@ +package org.hl7.fhir.r4.utils.validation; + +import org.hl7.fhir.r4.elementmodel.Element; +import org.hl7.fhir.r4.utils.validation.constants.ReferenceValidationPolicy; + +public interface IValidationPolicyAdvisor { + + ReferenceValidationPolicy policyForReference(IResourceValidator validator, + Object appContext, + String path, + String url); + + ReferenceValidationPolicy policyForContained(IResourceValidator validator, + Object appContext, + String containerType, + String containerId, + Element.SpecialElement containingResourceType, + String path, + String url); + +} \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IValidatorResourceFetcher.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IValidatorResourceFetcher.java new file mode 100644 index 000000000..54e5cbc2d --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/IValidatorResourceFetcher.java @@ -0,0 +1,11 @@ +package org.hl7.fhir.r4.utils.validation; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4.elementmodel.Element; + +import java.io.IOException; + +public interface IValidatorResourceFetcher { + Element fetch(Object appContext, String url) throws FHIRException, IOException; + boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException; +} diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/BestPracticeWarningLevel.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/BestPracticeWarningLevel.java new file mode 100644 index 000000000..41755cf6d --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/BestPracticeWarningLevel.java @@ -0,0 +1,8 @@ +package org.hl7.fhir.r4.utils.validation.constants; + +public enum BestPracticeWarningLevel { + Ignore, + Hint, + Warning, + Error +} diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/CheckDisplayOption.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/CheckDisplayOption.java new file mode 100644 index 000000000..00f4608c5 --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/CheckDisplayOption.java @@ -0,0 +1,9 @@ +package org.hl7.fhir.r4.utils.validation.constants; + +public enum CheckDisplayOption { + Ignore, + Check, + CheckCaseAndSpace, + CheckCase, + CheckSpace +} diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/IdStatus.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/IdStatus.java new file mode 100644 index 000000000..a26479ba0 --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/IdStatus.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r4.utils.validation.constants; + +public enum IdStatus { + OPTIONAL, + REQUIRED, + PROHIBITED +} diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/ReferenceValidationPolicy.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/ReferenceValidationPolicy.java new file mode 100644 index 000000000..cffa1de70 --- /dev/null +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/validation/constants/ReferenceValidationPolicy.java @@ -0,0 +1,21 @@ +package org.hl7.fhir.r4.utils.validation.constants; + +public enum ReferenceValidationPolicy { + IGNORE, + CHECK_TYPE_IF_EXISTS, + CHECK_EXISTS, + CHECK_EXISTS_AND_TYPE, + CHECK_VALID; + + public boolean checkExists() { + return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID; + } + + public boolean checkType() { + return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID; + } + + public boolean checkValid() { + return this == CHECK_VALID; + } +} diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/ResourceRoundTripTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/ResourceRoundTripTests.java index d595577a1..fb69e3597 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/ResourceRoundTripTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/ResourceRoundTripTests.java @@ -21,7 +21,7 @@ import java.io.*; public class ResourceRoundTripTests { @BeforeAll - public void setUp() throws Exception { + public static void setUp() throws Exception { } @Test diff --git a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java index 747c66149..2b4a32a44 100644 --- a/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r4/src/test/java/org/hl7/fhir/r4/test/SnapShotGenerationTests.java @@ -22,7 +22,7 @@ import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.test.utils.TestingUtilities; import org.hl7.fhir.r4.utils.FHIRPathEngine; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r4.utils.IResourceValidator; +import org.hl7.fhir.r4.utils.validation.IResourceValidator; import org.hl7.fhir.r4.utils.NarrativeGenerator; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; diff --git a/org.hl7.fhir.r5/pom.xml b/org.hl7.fhir.r5/pom.xml index bfa4f55ee..96475097d 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.5.12-SNAPSHOT + 5.6.16-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/conformance/StructureDefinitionHacker.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/StructureDefinitionHacker.java new file mode 100644 index 000000000..26d8cfcb3 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/StructureDefinitionHacker.java @@ -0,0 +1,48 @@ +package org.hl7.fhir.r5.conformance; + +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.utilities.VersionUtilities; + +public class StructureDefinitionHacker { + + private String version; + + public StructureDefinitionHacker(String version) { + super(); + this.version = version; + } + + public Resource fixSD(StructureDefinition sd) { + if (VersionUtilities.isR4BVer(version) && sd.getUrl().equals("http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type")) { + // the definition of this one is wrong in R4B + return fixR4BFhirType(sd); + } + return sd; + } + + private Resource fixR4BFhirType(StructureDefinition sd) { + for (ElementDefinition ed : sd.getDifferential().getElement()) { + if (ed.getPath().equals("Extension.value[x]")) { + fixEDType(ed, "url", "uri"); + } + } + for (ElementDefinition ed : sd.getSnapshot().getElement()) { + if (ed.getPath().equals("Extension.value[x]")) { + fixEDType(ed, "url", "uri"); + } + } + return sd; + } + + private void fixEDType(ElementDefinition ed, String orig, String repl) { + for (TypeRefComponent t : ed.getType()) { + if (orig.equals(t.getCode())) { + t.setCode(repl); + } + } + } + +} 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 f441447a4..7e073a55a 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 @@ -52,6 +52,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; @@ -110,6 +111,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; @@ -129,6 +131,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; @@ -165,7 +196,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(); @@ -276,6 +307,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)) { @@ -338,12 +375,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(); @@ -498,6 +537,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte @Override public CodeSystem fetchCodeSystem(String system) { + if (system == null) { + return null; + } + if (system.contains("|")) { + String s = system.substring(0, system.indexOf("|")); + String v = system.substring(system.indexOf("|")+1); + return fetchCodeSystem(s, v); + } CodeSystem cs; synchronized (lock) { cs = codeSystems.get(system); @@ -511,6 +558,23 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte return cs; } + public CodeSystem fetchCodeSystem(String system, String version) { + if (version == null) { + return fetchCodeSystem(system); + } + CodeSystem cs; + synchronized (lock) { + cs = codeSystems.get(system, version); + } + if (cs == null && locator != null) { + locator.findResource(this, system); + synchronized (lock) { + cs = codeSystems.get(system); + } + } + return cs; + } + @Override public boolean supportsSystem(String system) throws TerminologyServiceException { synchronized (lock) { @@ -643,10 +707,18 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte if (expParameters == null) throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED)); Parameters p = expParameters.copy(); - return expandVS(vs, cacheOk, heirarchical, p); + return expandVS(vs, cacheOk, heirarchical, false, p); } - public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, Parameters p) { + @Override + public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, boolean incompleteOk) { + if (expParameters == null) + throw new Error(formatMessage(I18nConstants.NO_EXPANSION_PARAMETERS_PROVIDED)); + Parameters p = expParameters.copy(); + return expandVS(vs, cacheOk, heirarchical, incompleteOk, p); + } + + public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean heirarchical, boolean incompleteOk, Parameters p) { if (p == null) { throw new Error(formatMessage(I18nConstants.NO_PARAMETERS_PROVIDED_TO_EXPANDVS)); } @@ -673,6 +745,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } p.setParameter("includeDefinition", false); p.setParameter("excludeNested", !heirarchical); + if (incompleteOk) { + p.setParameter("incomplete-ok", true); + } List allErrors = new ArrayList<>(); @@ -853,6 +928,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(); } @@ -872,7 +953,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) { @@ -992,13 +1073,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); @@ -1205,8 +1288,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; } @@ -1216,7 +1303,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("|")); @@ -1264,13 +1350,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(); + } } } } @@ -1314,20 +1410,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; } @@ -1453,13 +1535,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(); } } } @@ -1533,7 +1612,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; } @@ -1544,7 +1623,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)); } @@ -1567,6 +1646,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 { @@ -1656,13 +1743,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/HTMLClientLogger.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java index 122dd2a3a..9d6803e15 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/HTMLClientLogger.java @@ -46,7 +46,7 @@ public class HTMLClientLogger extends BaseLogger implements ToolingClientLogger private boolean req = false; private PrintStream file; - + public HTMLClientLogger(String log) { if (log != null) { try { @@ -89,12 +89,13 @@ public class HTMLClientLogger extends BaseLogger implements ToolingClientLogger if (DEBUG) { System.out.println(" txlog resp: " +outcome+" "+present(body)); } - req = false; + if (file == null) return; if (!req) { System.out.println("Record Response without request"); } + req = false; file.println("
");
     file.println(outcome);
     for (String s : headers)  
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 6dd535d95..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
@@ -4,34 +4,34 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 
-/*
-  Copyright (c) 2011+, HL7, Inc.
-  All rights reserved.
-  
-  Redistribution and use in source and binary forms, with or without modification, 
-  are permitted provided that the following conditions are met:
-    
-   * Redistributions of source code must retain the above copyright notice, this 
-     list of conditions and the following disclaimer.
-   * Redistributions in binary form must reproduce the above copyright notice, 
-     this list of conditions and the following disclaimer in the documentation 
-     and/or other materials provided with the distribution.
-   * Neither the name of HL7 nor the names of its contributors may be used to 
-     endorse or promote products derived from this software without specific 
-     prior written permission.
-  
-  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
-  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
-  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
-  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
-  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
-  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
-  POSSIBILITY OF SUCH DAMAGE.
-  
- */
+/*
+  Copyright (c) 2011+, HL7, Inc.
+  All rights reserved.
+  
+  Redistribution and use in source and binary forms, with or without modification, 
+  are permitted provided that the following conditions are met:
+    
+   * Redistributions of source code must retain the above copyright notice, this 
+     list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above copyright notice, 
+     this list of conditions and the following disclaimer in the documentation 
+     and/or other materials provided with the distribution.
+   * Neither the name of HL7 nor the names of its contributors may be used to 
+     endorse or promote products derived from this software without specific 
+     prior written permission.
+  
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+  POSSIBILITY OF SUCH DAMAGE.
+  
+ */
 
 
 
@@ -44,8 +44,8 @@ import org.fhir.ucum.UcumService;
 import org.hl7.fhir.exceptions.DefinitionException;
 import org.hl7.fhir.exceptions.FHIRException;
 import org.hl7.fhir.exceptions.TerminologyServiceException;
-import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest;
 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,12 +64,14 @@ import org.hl7.fhir.r5.model.ValueSet;
 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.IResourceValidator;
+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;
@@ -168,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 {
@@ -191,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();
     
@@ -242,15 +248,14 @@ public interface IWorkerContext {
     IContextResourceLoader getNewLoader(NpmPackage npm) throws JsonSyntaxException, IOException;   
   }
 
-
   /**
    * Get the versions of the definitions loaded in context
    * @return
    */
-  public String getVersion();
-  
-  /**
-   * return the link to the base of the specification for the loaded version e.g. http://hl7.org/fhir/R4
+  public String getVersion();
+  
+  /**
+   * return the link to the base of the specification for the loaded version e.g. http://hl7.org/fhir/R4
    */
   public String getSpecUrl();
   
@@ -335,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)
@@ -476,6 +482,7 @@ public interface IWorkerContext {
    * @return
    */
   public CodeSystem fetchCodeSystem(String system);
+  public CodeSystem fetchCodeSystem(String system, String version);
 
   /**
    * True if the underlying terminology service provider will do 
@@ -508,6 +515,14 @@ public interface IWorkerContext {
    */
   public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical);
   
+  /**
+   * ValueSet Expansion - see $expand
+   *  
+   * @param source
+   * @return
+   */
+  public ValueSetExpansionOutcome expandVS(ValueSet source, boolean cacheOk, boolean heiarchical, boolean incompleteOk);
+  
   /**
    * ValueSet Expansion - see $expand, but resolves the binding first
    *  
@@ -733,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 codes, ValueSet vs);
   
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java
index 7e132e33b..13ee2c3a1 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/SimpleWorkerContext.java
@@ -54,9 +54,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError;
 import org.hl7.fhir.r5.conformance.ProfileUtilities;
 import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider;
 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;
-import org.hl7.fhir.r5.context.SimpleWorkerContext.PackageResourceLoader;
 import org.hl7.fhir.r5.formats.IParser;
 import org.hl7.fhir.r5.formats.JsonParser;
 import org.hl7.fhir.r5.formats.ParserType;
@@ -76,7 +74,7 @@ import org.hl7.fhir.r5.model.StructureMap;
 import org.hl7.fhir.r5.model.StructureMap.StructureMapModelMode;
 import org.hl7.fhir.r5.model.StructureMap.StructureMapStructureComponent;
 import org.hl7.fhir.r5.terminologies.TerminologyClient;
-import org.hl7.fhir.r5.utils.IResourceValidator;
+import org.hl7.fhir.r5.utils.validation.IResourceValidator;
 import org.hl7.fhir.r5.utils.XVerExtensionManager;
 import org.hl7.fhir.utilities.CSFileInputStream;
 import org.hl7.fhir.utilities.TextFile;
@@ -85,15 +83,12 @@ import org.hl7.fhir.utilities.Utilities;
 import org.hl7.fhir.utilities.VersionUtilities;
 import org.hl7.fhir.utilities.i18n.I18nConstants;
 import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
-import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
 import org.hl7.fhir.utilities.npm.NpmPackage;
 import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
 import org.hl7.fhir.utilities.validation.ValidationMessage;
 import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
 import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
 
-import com.google.gson.JsonObject;
-
 import ca.uhn.fhir.parser.DataFormatException;
 
 /*
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ParserBase.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ParserBase.java
index c3d2a3023..7a32ded0b 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ParserBase.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/ParserBase.java
@@ -118,11 +118,13 @@ public abstract class ParserBase {
 
 	//FIXME: i18n should be done here
 	public void logError(int line, int col, String path, IssueType type, String message, IssueSeverity level) throws FHIRFormatError {
-	  if (policy == ValidationPolicy.EVERYTHING) {
-	    ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level);
-	    errors.add(msg);
-	  } else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK))
-	    throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col));
+	  if (errors != null) {
+	    if (policy == ValidationPolicy.EVERYTHING) {
+	      ValidationMessage msg = new ValidationMessage(Source.InstanceValidator, type, line, col, path, message, level);
+	      errors.add(msg);
+	    } else if (level == IssueSeverity.FATAL || (level == IssueSeverity.ERROR && policy == ValidationPolicy.QUICK))
+	      throw new FHIRFormatError(message+String.format(" at line %d col %d", line, col));
+	  }
 	}
 	
 	
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java
index dfdaf3b51..a7c89f4b0 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/SHCParser.java
@@ -237,6 +237,7 @@ public class SHCParser extends ParserBase {
 
   private static final int BUFFER_SIZE = 1024;
   public static final String CURRENT_PACKAGE = "hl7.fhir.uv.shc-vaccination#0.6.2";
+  private static final int MAX_ALLOWED_SHC_LENGTH = 1195;
   
   // todo: deal with chunking
   public static String decodeQRCode(String src) {
@@ -253,10 +254,14 @@ public class SHCParser extends ParserBase {
     return b.toString();
   }
 
-  public static JWT decodeJWT(String jwt) throws IOException, DataFormatException {
+  public JWT decodeJWT(String jwt) throws IOException, DataFormatException {
     if (jwt.startsWith("shc:/")) {
       jwt = decodeQRCode(jwt);
     }
+    if (jwt.length() > MAX_ALLOWED_SHC_LENGTH) {
+      logError(-1, -1, "jwt", IssueType.TOOLONG, "JWT Payload limit length is "+MAX_ALLOWED_SHC_LENGTH+" bytes for a single image - this has "+jwt.length()+" bytes", IssueSeverity.ERROR);
+    }
+
     String[] parts = splitToken(jwt);
     byte[] headerJson;
     byte[] payloadJson;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CanonicalResource.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CanonicalResource.java
index 207dc68f8..16c5bbd07 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CanonicalResource.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/CanonicalResource.java
@@ -553,7 +553,11 @@ public abstract class CanonicalResource extends DomainResource {
   public boolean supportsCopyright() {
     return true;
   }
-  
+
+  public String getVersionedUrl() {
+    return hasVersion() ? getUrl()+"|"+getVersion() : getUrl();
+  }
+
 // end addition
 
 }
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Enumerations.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Enumerations.java
index bb1894715..3e8b64c62 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Enumerations.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Enumerations.java
@@ -6716,6 +6716,10 @@ The MedicationUsage resource was previously called MedicationStatement.
          * R5 Preview #1.
          */
         _4_2_0, 
+        /**
+         * R4B
+         */
+        _4_3_0_CIBUILD, 
         /**
          * R5 Preview #2.
          */
@@ -6733,8 +6737,6 @@ The MedicationUsage resource was previously called MedicationStatement.
          */
         NULL;      
       
-      public static final FHIRVersion R4B = FHIRVersion._4_1_0; 
-
         public static FHIRVersion fromCode(String codeString) throws FHIRException {
             if (codeString == null || "".equals(codeString))
                 return null;
@@ -6788,6 +6790,8 @@ The MedicationUsage resource was previously called MedicationStatement.
           return _4_1_0;
         if ("4.2.0".equals(codeString))
           return _4_2_0;
+        if ("4.3.0-CIBUILD".equals(codeString))
+          return _4_3_0_CIBUILD;
         if ("4.4.0".equals(codeString))
           return _4_4_0;
         if ("4.5.0".equals(codeString))
@@ -6823,6 +6827,7 @@ The MedicationUsage resource was previously called MedicationStatement.
             case _4_0_1: return "4.0.1";
             case _4_1_0: return "4.1.0";
             case _4_2_0: return "4.2.0";
+            case _4_3_0_CIBUILD: return "4.3.0-CIBUILD";
             case _4_4_0: return "4.4.0";
             case _4_5_0: return "4.5.0";
             case _4_6_0: return "4.6.0";
@@ -6857,6 +6862,7 @@ The MedicationUsage resource was previously called MedicationStatement.
             case _4_0_1: return "http://hl7.org/fhir/FHIR-version";
             case _4_1_0: return "http://hl7.org/fhir/FHIR-version";
             case _4_2_0: return "http://hl7.org/fhir/FHIR-version";
+            case _4_3_0_CIBUILD: return "http://hl7.org/fhir/FHIR-version";
             case _4_4_0: return "http://hl7.org/fhir/FHIR-version";
             case _4_5_0: return "http://hl7.org/fhir/FHIR-version";
             case _4_6_0: return "http://hl7.org/fhir/FHIR-version";
@@ -6891,6 +6897,7 @@ The MedicationUsage resource was previously called MedicationStatement.
             case _4_0_1: return "FHIR Release 4 (Normative + STU) with 1 technical errata.";
             case _4_1_0: return "Interim Version.";
             case _4_2_0: return "R5 Preview #1.";
+            case _4_3_0_CIBUILD: return "R4B CIBuild";
             case _4_4_0: return "R5 Preview #2.";
             case _4_5_0: return "R5 Preview #3.";
             case _4_6_0: return "R5 Draft Ballot.";
@@ -6925,6 +6932,7 @@ The MedicationUsage resource was previously called MedicationStatement.
             case _4_0_1: return "4.0.1";
             case _4_1_0: return "4.1.0";
             case _4_2_0: return "4.2.0";
+            case _4_3_0_CIBUILD: return "4.3.0-CIBUILD";
             case _4_4_0: return "4.4.0";
             case _4_5_0: return "4.5.0";
             case _4_6_0: return "4.6.0";
@@ -6988,6 +6996,8 @@ public String toCode(int len) {
         return true;
       if ("4.2.0".equals(codeString))
         return true;
+      if ("4.3.0-CIBUILD".equals(codeString))
+        return true;
       return false;
       }
 
@@ -6998,7 +7008,7 @@ public String toCode(int len) {
         
         
         public boolean isR4B() {
-          return toCode().startsWith("4.1");
+          return toCode().startsWith("4.1") || toCode().startsWith("4.3");
         }
         
 // end addition
@@ -7059,6 +7069,8 @@ public String toCode(int len) {
           return FHIRVersion._4_1_0;
         if ("4.2.0".equals(codeString))
           return FHIRVersion._4_2_0;
+        if ("4.3.0-CIBUILD".equals(codeString))
+          return FHIRVersion._4_3_0_CIBUILD;
         if ("4.4.0".equals(codeString))
           return FHIRVersion._4_4_0;
         if ("4.5.0".equals(codeString))
@@ -7125,6 +7137,8 @@ public String toCode(int len) {
           return new Enumeration(this, FHIRVersion._4_1_0);
         if ("4.2.0".equals(codeString))
           return new Enumeration(this, FHIRVersion._4_2_0);
+        if ("4.3.0-CIBUILD".equals(codeString))
+          return new Enumeration(this, FHIRVersion._4_3_0_CIBUILD);
         if ("4.4.0".equals(codeString))
           return new Enumeration(this, FHIRVersion._4_4_0);
         if ("4.5.0".equals(codeString))
@@ -7184,6 +7198,8 @@ public String toCode(int len) {
         return "4.1.0";
       if (code == FHIRVersion._4_2_0)
         return "4.2.0";
+      if (code == FHIRVersion._4_3_0_CIBUILD)
+        return "4.3.0-CIBUILD";
       if (code == FHIRVersion._4_4_0)
         return "4.4.0";
       if (code == FHIRVersion._4_5_0)
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java
index 3f0d8321e..a197b14f8 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CapabilityStatementRenderer.java
@@ -14,6 +14,7 @@ import org.hl7.fhir.r5.model.CapabilityStatement.ResourceInteractionComponent;
 import org.hl7.fhir.r5.model.CapabilityStatement.SystemInteractionComponent;
 import org.hl7.fhir.r5.model.CapabilityStatement.SystemRestfulInteraction;
 import org.hl7.fhir.r5.model.CapabilityStatement.TypeRestfulInteraction;
+import org.hl7.fhir.r5.model.CanonicalType;
 import org.hl7.fhir.r5.renderers.utils.RenderingContext;
 import org.hl7.fhir.r5.renderers.utils.BaseWrappers.ResourceWrapper;
 import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
@@ -79,12 +80,41 @@ public class CapabilityStatementRenderer extends ResourceRenderer {
       if (hasHistory)
         tr.th().b().attribute("title", "GET changes for all resources of the type (history interaction on type)").tx("History");
 
+      XhtmlNode profCell = null;
+      boolean hasProf = false;
+      boolean hasSupProf = false;
       for (CapabilityStatementRestResourceComponent r : rest.getResource()) {
         tr = t.tr();
         tr.td().addText(r.getType());
-        if (r.hasProfile()) {
-          tr.td().ah(r.getProfile()).addText(r.getProfile());
+
+        //Show profiles
+        profCell = tr.td();
+        hasProf = r.hasProfile();
+        hasSupProf = r.hasSupportedProfile();
+        if ((!hasProf) && (!hasSupProf)) {
+          profCell.nbsp();
         }
+        else if (hasProf) {
+          profCell.ah(r.getProfile()).addText(r.getProfile());
+          if (hasSupProf) {
+            profCell.br();
+            profCell.addText("Additional supported profiles:");
+            for (CanonicalType sp: r.getSupportedProfile()) { 
+              profCell.br();
+              profCell.nbsp().nbsp();
+              profCell.ah(sp.getValue()).addText(sp.getValue());   
+            }
+          }
+        }
+        else {    //Case of only supported profiles
+          profCell.addText("Supported profiles:");
+          for (CanonicalType sp: r.getSupportedProfile()) { 
+            profCell.br();
+            profCell.nbsp().nbsp();
+            profCell.ah(sp.getValue()).addText(sp.getValue());   
+          }
+        }
+        //Show capabilities
         tr.td().addText(showOp(r, TypeRestfulInteraction.READ));
         if (hasVRead)
           tr.td().addText(showOp(r, TypeRestfulInteraction.VREAD));
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java
index f9618e79f..464423299 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java
@@ -94,8 +94,10 @@ public class CodeSystemRenderer extends TerminologyRenderer {
   private void generateProperties(XhtmlNode x, CodeSystem cs) {
     if (cs.hasProperty()) {
       boolean hasRendered = false;
+      boolean hasURI = false;
       for (PropertyComponent p : cs.getProperty()) {
         hasRendered = hasRendered || !p.getCode().equals(ToolingExtensions.getPresentation(p, p.getCodeElement()));
+        hasURI = hasURI || p.hasUri();
       }
       
       x.para().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Properties", getContext().getLang()));
@@ -105,18 +107,22 @@ public class CodeSystemRenderer extends TerminologyRenderer {
         tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Name", getContext().getLang()));        
       }
       tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Code", getContext().getLang()));
-      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "URL", getContext().getLang()));
-      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang()));
+      if (hasURI) {
+        tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "URL", getContext().getLang()));
+      }
       tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Type", getContext().getLang()));
+      tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Description", getContext().getLang()));
       for (PropertyComponent p : cs.getProperty()) {
         tr = tbl.tr();
         if (hasRendered) {
           tr.td().tx(ToolingExtensions.getPresentation(p, p.getCodeElement()));          
         }
         tr.td().tx(p.getCode());
-        tr.td().tx(p.getUri());
-        tr.td().tx(p.getDescription());
+        if (hasURI) {
+          tr.td().tx(p.getUri());
+        }
         tr.td().tx(p.hasType() ? p.getType().toCode() : "");
+        tr.td().tx(p.getDescription());
       }
     }
   }
@@ -126,7 +132,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
     if (cs.getContent() == CodeSystemContentMode.COMPLETE)
       p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines the following codes", cs.getUrl())+":");
     else if (cs.getContent() == CodeSystemContentMode.EXAMPLE)
-      p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, of which the following are some examples", cs.getUrl())+":");
+      p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines some example codes", cs.getUrl())+":");
     else if (cs.getContent() == CodeSystemContentMode.FRAGMENT )
       p.tx(getContext().getWorker().translator().translateAndFormat("xhtml-gen-cs", getContext().getLang(), "This code system %s defines many codes, of which the following are a subset", cs.getUrl())+":");
     else if (cs.getContent() == CodeSystemContentMode.NOTPRESENT ) {
@@ -141,6 +147,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
     boolean hierarchy = false;
     boolean version = false;
     boolean ignoreStatus = false;
+    boolean isSupplement = cs.getContent() == CodeSystemContentMode.SUPPLEMENT;
     List properties = new ArrayList<>();
     for (PropertyComponent cp : cs.getProperty()) {
       if (showPropertyInTable(cp)) {
@@ -170,7 +177,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
     List langs = new ArrayList<>();
     addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, definitions, commentS, version, deprecated, properties, null, false), maps);
     for (ConceptDefinitionComponent c : csNav.getConcepts(null)) {
-      hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs) || hasExtensions;
+      hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, definitions, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs, isSupplement) || hasExtensions;
     }
     if (langs.size() > 0) {
       Collections.sort(langs);
@@ -223,10 +230,10 @@ public class CodeSystemRenderer extends TerminologyRenderer {
         return true;
       }
       String uri = cp.getUri();
-      String code = null;
       if (Utilities.noString(uri)){
-        return false;
+        return true; // do we always want to render properties in this case? Not sure...
       }
+      String code = null;
       if (uri.contains("#")) {
         code = uri.substring(uri.indexOf("#")+1);
         uri = uri.substring(0, uri.indexOf("#"));
@@ -290,7 +297,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
 
 
 
-  private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List maps, String system, CodeSystem cs, List properties, CodeSystemNavigator csNav, List langs) throws FHIRFormatError, DefinitionException, IOException {
+  private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, boolean hasDefinitions, boolean comment, boolean version, boolean deprecated, List maps, String system, CodeSystem cs, List properties, CodeSystemNavigator csNav, List langs, boolean isSupplement) throws FHIRFormatError, DefinitionException, IOException {
     boolean hasExtensions = false;
     XhtmlNode tr = t.tr();
     XhtmlNode td = tr.td();
@@ -300,7 +307,12 @@ public class CodeSystemRenderer extends TerminologyRenderer {
       String s = Utilities.padLeft("", '\u00A0', level*2);
       td.addText(s);
     }
-    td.attribute("style", "white-space:nowrap").addText(c.getCode());
+    String link = isSupplement ? getLinkForCode(cs.getSupplements(), null, c.getCode()) : null;
+    if (link != null) {
+      td.ah(link).attribute("style", "white-space:nowrap").addText(c.getCode());
+    } else {
+      td.attribute("style", "white-space:nowrap").addText(c.getCode());
+    }      
     XhtmlNode a;
     if (c.hasCodeElement()) {
       td.an(cs.getId()+"-" + Utilities.nmtokenize(c.getCode()));
@@ -450,7 +462,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
     }
     List ocl = csNav.getOtherChildren(c);
     for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) {
-      hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs) || hasExtensions;
+      hasExtensions = addDefineRowToTable(t, cc, level+1, hasHierarchy, hasDisplay, hasDefinitions, comment, version, deprecated, maps, system, cs, properties, csNav, langs, isSupplement) || hasExtensions;
     }
     for (ConceptDefinitionComponent cc : ocl) {
       tr = t.tr();
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java
index e2ea0fb46..4daa03408 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java
@@ -2,7 +2,18 @@ package org.hl7.fhir.r5.renderers;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.util.Currency;
 import java.util.List;
+import java.util.TimeZone;
 
 import org.hl7.fhir.exceptions.DefinitionException;
 import org.hl7.fhir.exceptions.FHIRException;
@@ -29,11 +40,14 @@ import org.hl7.fhir.r5.model.ContactPoint.ContactPointSystem;
 import org.hl7.fhir.r5.model.DataType;
 import org.hl7.fhir.r5.model.DateTimeType;
 import org.hl7.fhir.r5.model.Enumeration;
+import org.hl7.fhir.r5.model.Expression;
 import org.hl7.fhir.r5.model.Extension;
 import org.hl7.fhir.r5.model.HumanName;
 import org.hl7.fhir.r5.model.HumanName.NameUse;
+import org.hl7.fhir.r5.model.IdType;
 import org.hl7.fhir.r5.model.Identifier;
-import org.hl7.fhir.r5.model.InstantType;
+import org.hl7.fhir.r5.model.MarkdownType;
+import org.hl7.fhir.r5.model.Money;
 import org.hl7.fhir.r5.model.Period;
 import org.hl7.fhir.r5.model.PrimitiveType;
 import org.hl7.fhir.r5.model.Quantity;
@@ -63,6 +77,9 @@ import org.hl7.fhir.utilities.validation.ValidationOptions;
 import org.hl7.fhir.utilities.xhtml.NodeType;
 import org.hl7.fhir.utilities.xhtml.XhtmlNode;
 import org.hl7.fhir.utilities.xhtml.XhtmlParser;
+
+import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
+
 import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator;
 import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Piece;
 
@@ -372,6 +389,8 @@ public class DataRenderer extends Renderer {
       return displayTiming((Timing) type);
     } else if (type instanceof SampledData) {
       return displaySampledData((SampledData) type);
+    } else if (type.isDateTime()) {
+      return displayDateTime((BaseDateTimeType) type);
     } else if (type.isPrimitive()) {
       return type.primitiveValue();
     } else {
@@ -379,11 +398,58 @@ public class DataRenderer extends Renderer {
     }
   }
 
+  private String displayDateTime(BaseDateTimeType type) {
+    if (!type.hasPrimitiveValue()) {
+      return "";
+    }
+    
+    // relevant inputs in rendering context:
+    // timeZone, dateTimeFormat, locale, mode
+    //   timezone - application specified timezone to use. 
+    //        null = default to the time of the date/time itself
+    //   dateTimeFormat - application specified format for date times
+    //        null = default to ... depends on mode
+    //   mode - if rendering mode is technical, format defaults to XML format
+    //   locale - otherwise, format defaults to SHORT for the Locale (which defaults to default Locale)  
+    if (isOnlyDate(type.getPrecision())) {
+      DateTimeFormatter fmt = context.getDateFormat();
+      if (fmt == null) {
+        if (context.isTechnicalMode()) {
+          fmt = DateTimeFormatter.ISO_DATE;
+        } else {
+          fmt = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(context.getLocale());
+        }
+      }
+
+      LocalDate date = LocalDate.of(type.getYear(), type.getMonth()+1, type.getDay());
+      return fmt.format(date);
+    }
+
+    DateTimeFormatter fmt = context.getDateTimeFormat();
+    if (fmt == null) {
+      if (context.isTechnicalMode()) {
+        fmt = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+      } else {
+        fmt = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(context.getLocale());
+      }
+    }
+    ZonedDateTime zdt = ZonedDateTime.parse(type.primitiveValue());
+    ZoneId zone = context.getTimeZoneId();
+    if (zone != null) {
+      zdt = zdt.withZoneSameInstant(zone);
+    }
+    return fmt.format(zdt);
+  }   
+  
+  private boolean isOnlyDate(TemporalPrecisionEnum temporalPrecisionEnum) {
+    return temporalPrecisionEnum == TemporalPrecisionEnum.YEAR || temporalPrecisionEnum == TemporalPrecisionEnum.MONTH || temporalPrecisionEnum == TemporalPrecisionEnum.DAY;
+  }
+
   public String display(BaseWrapper type) {
     return "to do";   
   }
 
-  public void render(XhtmlNode x, BaseWrapper type)  {
+  public void render(XhtmlNode x, BaseWrapper type) throws FHIRFormatError, DefinitionException, IOException  {
     Base base = null;
     try {
       base = type.getBase();
@@ -398,7 +464,7 @@ public class DataRenderer extends Renderer {
     }
   }
   
-  public void renderBase(XhtmlNode x, Base b) {
+  public void renderBase(XhtmlNode x, Base b) throws FHIRFormatError, DefinitionException, IOException {
     if (b instanceof DataType) {
       render(x, (DataType) b);
     } else {
@@ -406,9 +472,9 @@ public class DataRenderer extends Renderer {
     }
   }
   
-  public void render(XhtmlNode x, DataType type) {
-    if (type instanceof DateTimeType) {
-      renderDateTime(x, (DateTimeType) type);
+  public void render(XhtmlNode x, DataType type) throws FHIRFormatError, DefinitionException, IOException {
+    if (type instanceof BaseDateTimeType) {
+      x.tx(displayDateTime((BaseDateTimeType) type));
     } else if (type instanceof UriType) {
       renderUri(x, (UriType) type);
     } else if (type instanceof Annotation) {
@@ -423,6 +489,10 @@ public class DataRenderer extends Renderer {
       renderHumanName(x, (HumanName) type);
     } else if (type instanceof Address) {
       renderAddress(x, (Address) type);
+    } else if (type instanceof Expression) {
+      renderExpression(x, (Expression) type);
+    } else if (type instanceof Money) {
+      renderMoney(x, (Money) type);
     } else if (type instanceof ContactPoint) {
       renderContactPoint(x, (ContactPoint) type);
     } else if (type instanceof Quantity) {
@@ -437,10 +507,8 @@ public class DataRenderer extends Renderer {
       renderSampledData(x, (SampledData) type);
     } else if (type instanceof Reference) {
       renderReference(x, (Reference) type);
-    } else if (type instanceof InstantType) {
-      x.tx(((InstantType) type).toHumanDisplay());
-    } else if (type instanceof BaseDateTimeType) {
-      x.tx(((BaseDateTimeType) type).toHumanDisplay());
+    } else if (type instanceof MarkdownType) {
+      addMarkdown(x, ((MarkdownType) type).asStringValue());
     } else if (type.isPrimitive()) {
       x.tx(type.primitiveValue());
     } else {
@@ -460,22 +528,24 @@ public class DataRenderer extends Renderer {
 
   public void renderDateTime(XhtmlNode x, Base e) {
     if (e.hasPrimitiveValue()) {
-      x.addText(((DateTimeType) e).toHumanDisplay());
+      x.addText(displayDateTime((DateTimeType) e));
     }
   }
 
   public void renderDateTime(XhtmlNode x, String s) {
     if (s != null) {
       DateTimeType dt = new DateTimeType(s);
-      x.addText(dt.toHumanDisplay());
+      x.addText(displayDateTime(dt));
     }
   }
 
   protected void renderUri(XhtmlNode x, UriType uri) {
     if (uri.getValue().startsWith("mailto:")) {
       x.ah(uri.getValue()).addText(uri.getValue().substring(7));
-    } else {
+    } else if (Utilities.isAbsoluteUrlLinkable(uri.getValue()) && !(uri instanceof IdType)) {
       x.ah(uri.getValue()).addText(uri.getValue());
+    } else {
+      x.addText(uri.getValue());
     }
   }
   
@@ -641,6 +711,38 @@ public class DataRenderer extends Renderer {
     }
   }
   
+  protected String getLinkForCode(String system, String version, String code) {
+    if ("http://snomed.info/sct".equals(system)) {
+      if (!Utilities.noString(code)) {
+        return "http://snomed.info/id/"+code;        
+      } else {
+        return "https://browser.ihtsdotools.org/";
+      }
+    } else if ("http://loinc.org".equals(system)) {
+      if (!Utilities.noString(code)) {
+        return "https://loinc.org/"+code;
+      } else {
+        return "https://loinc.org/";
+      }
+    } else if ("http://www.nlm.nih.gov/research/umls/rxnorm".equals(system)) {
+      if (!Utilities.noString(code)) {
+        return "https://mor.nlm.nih.gov/RxNav/search?searchBy=RXCUI&searchTerm="+code;        
+      } else {
+        return "https://www.nlm.nih.gov/research/umls/rxnorm/index.html";
+      }
+    } else {
+      CodeSystem cs = context.getWorker().fetchCodeSystem(system, version);
+      if (cs != null && cs.hasUserData("path")) {
+        if (!Utilities.noString(code)) {
+          return cs.getUserString("path")+"#"+Utilities.nmtokenize(code);
+        } else {
+          return cs.getUserString("path");
+        }
+      }
+    }  
+    return null;
+  }
+  
   protected void renderCodingWithDetails(XhtmlNode x, Coding c) {
     String s = "";
     if (c.hasDisplayElement())
@@ -650,22 +752,13 @@ public class DataRenderer extends Renderer {
 
 
     String sn = describeSystem(c.getSystem());
-    if ("http://snomed.info/sct".equals(c.getSystem())) {
-      if (c.hasCode()) {
-        x.ah("http://snomed.info/id/"+c.getCode()).tx(sn);        
-      } else {
-        x.ah("https://browser.ihtsdotools.org/").tx(sn);
-      }
-    } else if ("http://loinc.org".equals(c.getSystem())) {
-      x.ah("https://loinc.org/").tx(sn);            
+    String link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode());
+    if (link != null) {
+      x.ah(link).tx(sn);
     } else {
-      CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem());
-      if (cs != null && cs.hasUserData("path")) {
-        x.ah(cs.getUserString("path")).tx(sn);
-      } else {
-        x.tx(sn);
-      }
+      x.tx(sn);
     }
+    
     x.tx(" ");
     x.tx(c.getCode());
     if (!Utilities.noString(s)) {
@@ -903,6 +996,57 @@ public class DataRenderer extends Renderer {
     return s.toString();
   }
 
+  protected String getLocalizedBigDecimalValue(BigDecimal input, Currency c) {
+    NumberFormat numberFormat = NumberFormat.getNumberInstance(context.getLocale());
+    numberFormat.setGroupingUsed(true);
+    numberFormat.setMaximumFractionDigits(c.getDefaultFractionDigits());
+    numberFormat.setMinimumFractionDigits(c.getDefaultFractionDigits());
+    return numberFormat.format(input);
+}
+  
+  protected void renderMoney(XhtmlNode x, Money money) {
+    Currency c = Currency.getInstance(money.getCurrency());
+    if (c != null) {
+      XhtmlNode s = x.span(null, c.getDisplayName());
+      s.tx(c.getSymbol(context.getLocale()));
+      s.tx(getLocalizedBigDecimalValue(money.getValue(), c));
+      x.tx(" ("+c.getCurrencyCode()+")");
+    } else {
+      x.tx(money.getCurrency());
+      x.tx(money.getValue().toPlainString());
+    }
+  }
+  
+  protected void renderExpression(XhtmlNode x, Expression expr) {
+  // there's two parts: what the expression is, and how it's described. 
+    // we start with what it is, and then how it's desceibed 
+    if (expr.hasExpression()) {
+      XhtmlNode c = x;
+      if (expr.hasReference()) {
+        c = x.ah(expr.getReference());        
+      }
+      if (expr.hasLanguage()) {
+        c = c.span(null, expr.getLanguage());
+      }
+      c.code().tx(expr.getExpression());
+    } else if (expr.hasReference()) {
+      x.ah(expr.getReference()).tx("source");
+    }
+    if (expr.hasName() || expr.hasDescription()) {
+      x.tx("(");
+      if (expr.hasName()) {
+        x.b().tx(expr.getName());
+      }
+      if (expr.hasDescription()) {
+        x.tx("\"");
+        x.tx(expr.getDescription());
+        x.tx("\"");
+      }
+      x.tx(")");
+    }
+  }
+  
+  
   protected void renderContactPoint(XhtmlNode x, ContactPoint contact) {
     if (contact != null) {
       if (!contact.hasSystem()) {
@@ -1046,19 +1190,19 @@ public class DataRenderer extends Renderer {
       x.tx(" "+q.getLow().getUnit());
   }
 
-  public static String displayPeriod(Period p) {
-    String s = !p.hasStart() ? "(?)" : p.getStartElement().toHumanDisplay();
+  public String displayPeriod(Period p) {
+    String s = !p.hasStart() ? "(?)" : displayDateTime(p.getStartElement());
     s = s + " --> ";
-    return s + (!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
+    return s + (!p.hasEnd() ? "(ongoing)" : displayDateTime(p.getEndElement()));
   }
 
   public void renderPeriod(XhtmlNode x, Period p) {
-    x.addText(!p.hasStart() ? "??" : p.getStartElement().toHumanDisplay());
+    x.addText(!p.hasStart() ? "??" : displayDateTime(p.getStartElement()));
     x.tx(" --> ");
-    x.addText(!p.hasEnd() ? "(ongoing)" : p.getEndElement().toHumanDisplay());
+    x.addText(!p.hasEnd() ? "(ongoing)" : displayDateTime(p.getEndElement()));
   }
   
-  public void renderDataRequirement(XhtmlNode x, DataRequirement dr) {
+  public void renderDataRequirement(XhtmlNode x, DataRequirement dr) throws FHIRFormatError, DefinitionException, IOException {
     XhtmlNode tbl = x.table("grid");
     XhtmlNode tr = tbl.tr();    
     XhtmlNode td = tr.td().colspan("2");
@@ -1163,7 +1307,7 @@ public class DataRenderer extends Renderer {
       CommaSeparatedStringBuilder c = new CommaSeparatedStringBuilder();
       for (DateTimeType p : s.getEvent()) {
         if (p.hasValue()) {
-          c.append(p.toHumanDisplay());
+          c.append(displayDateTime(p));
         } else if (!renderExpression(c, p)) {
           c.append("??");
         }        
@@ -1174,7 +1318,7 @@ public class DataRenderer extends Renderer {
     if (s.hasRepeat()) {
       TimingRepeatComponent rep = s.getRepeat();
       if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasStart())
-        b.append("Starting "+rep.getBoundsPeriod().getStartElement().toHumanDisplay());
+        b.append("Starting "+displayDateTime(rep.getBoundsPeriod().getStartElement()));
       if (rep.hasCount())
         b.append("Count "+Integer.toString(rep.getCount())+" times");
       if (rep.hasDuration())
@@ -1206,7 +1350,7 @@ public class DataRenderer extends Renderer {
         b.append("Do "+st);
       }
       if (rep.hasBoundsPeriod() && rep.getBoundsPeriod().hasEnd())
-        b.append("Until "+rep.getBoundsPeriod().getEndElement().toHumanDisplay());
+        b.append("Until "+displayDateTime(rep.getBoundsPeriod().getEndElement()));
     }
     return b.toString();
   }
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/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java
index 2788fe78d..bcade79dc 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java
@@ -33,12 +33,14 @@ import org.hl7.fhir.r5.model.DomainResource;
 import org.hl7.fhir.r5.model.Dosage;
 import org.hl7.fhir.r5.model.ElementDefinition;
 import org.hl7.fhir.r5.model.Enumeration;
+import org.hl7.fhir.r5.model.Expression;
 import org.hl7.fhir.r5.model.Extension;
 import org.hl7.fhir.r5.model.HumanName;
 import org.hl7.fhir.r5.model.IdType;
 import org.hl7.fhir.r5.model.Identifier;
 import org.hl7.fhir.r5.model.InstantType;
 import org.hl7.fhir.r5.model.Meta;
+import org.hl7.fhir.r5.model.Money;
 import org.hl7.fhir.r5.model.Narrative;
 import org.hl7.fhir.r5.model.Narrative.NarrativeStatus;
 import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
@@ -118,6 +120,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
         generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), context.isTechnicalMode(), 0);
       }
     } catch (Exception e) {
+      System.out.println("Error Generating Narrative for "+r.fhirType()+"/"+r.getId()+": "+e.getMessage());
       e.printStackTrace();
       x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage());
     }
@@ -291,7 +294,6 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
     if (ew == null)
       return;
 
-
     Base e = ew.getBase();
 
     if (e instanceof StringType)
@@ -308,9 +310,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
       renderDateTime(x, e);
     } else if (e instanceof Base64BinaryType)
       x.addText(new Base64().encodeAsString(((Base64BinaryType) e).getValue()));
-    else if (e instanceof org.hl7.fhir.r5.model.DateType)
-      x.addText(((org.hl7.fhir.r5.model.DateType) e).toHumanDisplay());
-    else if (e instanceof Enumeration) {
+    else if (e instanceof org.hl7.fhir.r5.model.DateType) {
+      org.hl7.fhir.r5.model.DateType dt = ((org.hl7.fhir.r5.model.DateType) e);
+      if (((org.hl7.fhir.r5.model.DateType) e).hasValue()) {
+        x.addText(((org.hl7.fhir.r5.model.DateType) e).toHumanDisplay());
+      }
+    } else if (e instanceof Enumeration) {
       Object ev = ((Enumeration) e).getValue();
       x.addText(ev == null ? "" : ev.toString()); // todo: look up a display name if there is one
     } else if (e instanceof BooleanType) {
@@ -347,6 +352,10 @@ public class ProfileDrivenRenderer extends ResourceRenderer {
       renderAddress(x, (Address) e);
     } else if (e instanceof ContactPoint) {
       renderContactPoint(x, (ContactPoint) e);
+    } else if (e instanceof Expression) {
+      renderExpression(x, (Expression) e);
+    } else if (e instanceof Money) {
+      renderMoney(x, (Money) e);
     } else if (e instanceof ContactDetail) {
       ContactDetail cd = (ContactDetail) e;
       if (cd.hasName()) {
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 f9e34d1f7..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());                    
@@ -792,7 +792,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
   private void listOptions(Questionnaire q, QuestionnaireItemComponent i, XhtmlNode select) {
     if (i.hasAnswerValueSet()) {
       ValueSet vs = null;
-      if (Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
+      if (!Utilities.noString(i.getAnswerValueSet()) && i.getAnswerValueSet().startsWith("#")) {
         vs = (ValueSet) q.getContained(i.getAnswerValueSet().substring(1));
         if (vs != null && !vs.hasUrl()) {
           vs = vs.copy();
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 820625889..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) {
@@ -473,10 +483,10 @@ public abstract class ResourceRenderer extends DataRenderer {
   }
 
   private String getPrimitiveValue(BaseWrapper b, String name) throws UnsupportedEncodingException, FHIRException, IOException {
-    return b != null && b.getChildByName(name).hasValues() ? b.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
+    return b != null && b.has(name) && b.getChildByName(name).hasValues() ? b.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
   }
 
   private String getPrimitiveValue(ResourceWrapper r, String name) throws UnsupportedEncodingException, FHIRException, IOException {
-    return r.getChildByName(name).hasValues() ? r.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
+    return r.has(name) && r.getChildByName(name).hasValues() ? r.getChildByName(name).getValues().get(0).getBase().primitiveValue() : null;
   }
 }
\ No newline at end of file
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java
index 507b67db8..e88e85122 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java
@@ -176,23 +176,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
       else
         x.para().tx("This value set contains "+count.toString()+" concepts");
     }
-    if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_FRAGMENT)) {
-      XhtmlNode div = x.div().style("border: maroon 1px solid; background-color: #FFCCCC; padding: 8px");
-      List exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_FRAGMENT);
-      if (exl.size() > 1) {
-        div.para().addText("Warning: this expansion is generated from fragments of the following code systems, and may be missing codes, or include codes that are not valid:");
-        XhtmlNode ul = div.ul();
-        for (Extension ex : exl) {
-          addCSRef(ul.li(), ex.getValue().primitiveValue());
-        }
-      } else {
-        XhtmlNode p = div.para();
-        p.addText("Warning: this expansion is generated from a fragment of the code system ");
-        addCSRef(p, exl.get(0).getValue().primitiveValue());
-        p.addText(" and may be missing codes, or include codes that are not valid");
-      }
-    }
     
+    generateContentModeNotices(x, vs.getExpansion());
     generateVersionNotice(x, vs.getExpansion());
 
     CodeSystem allCS = null;
@@ -273,6 +258,46 @@ public class ValueSetRenderer extends TerminologyRenderer {
     return hasExtensions;
   }
 
+  private void generateContentModeNotices(XhtmlNode x, ValueSetExpansionComponent expansion) {
+    generateContentModeNotice(x, expansion, "example", "Expansion based on example code system"); 
+    generateContentModeNotice(x, expansion, "fragment", "Expansion based on code system fragment"); 
+  }
+  
+  private void generateContentModeNotice(XhtmlNode x, ValueSetExpansionComponent expansion, String mode, String text) {
+    Multimap versions = HashMultimap.create();
+    for (ValueSetExpansionParameterComponent p : expansion.getParameter()) {
+      if (p.getName().equals(mode)) {
+        String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|");
+        if (parts.length == 2)
+          versions.put(parts[0], parts[1]);
+      }
+    }
+    if (versions.size() > 0) {
+      XhtmlNode div = null;
+      XhtmlNode ul = null;
+      boolean first = true;
+      for (String s : versions.keySet()) {
+        if (versions.size() == 1 && versions.get(s).size() == 1) {
+          for (String v : versions.get(s)) { // though there'll only be one
+            XhtmlNode p = x.para().style("border: black 1px dotted; background-color: #ffcccc; padding: 8px; margin-bottom: 8px");
+            p.tx(text+" ");
+            expRef(p, s, v);
+          }
+        } else {
+          for (String v : versions.get(s)) {
+            if (first) {
+              div = x.div().style("border: black 1px dotted; background-color: #EEEEEE; padding: 8px; margin-bottom: 8px");
+              div.para().tx(text+"s: ");
+              ul = div.ul();
+              first = false;
+            }
+            expRef(ul.li(), s, v);
+          }
+        }
+      }
+    }
+  }
+
   private boolean checkDoSystem(ValueSet vs, ValueSet src) {
     if (src != null)
       vs = src;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java
index 3fe565098..f7800c7e6 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/DOMWrappers.java
@@ -49,7 +49,7 @@ public class DOMWrappers {
 
     @Override
     public Base getBase() throws UnsupportedEncodingException, IOException, FHIRException {
-      if (type == null || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
+      if (Utilities.noString(type) || type.equals("Resource") || type.equals("BackboneElement") || type.equals("Element"))
         return null;
 
       String xml;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java
index c18561207..f30babd61 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/RenderingContext.java
@@ -1,8 +1,14 @@
 package org.hl7.fhir.r5.renderers.utils;
 
 import java.io.IOException;
+import java.text.DateFormat;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
 
 import org.hl7.fhir.exceptions.FHIRException;
 import org.hl7.fhir.exceptions.FHIRFormatError;
@@ -117,6 +123,11 @@ public class RenderingContext {
   private boolean addGeneratedNarrativeHeader = true;
 
   private FhirPublication targetVersion;
+  private Locale locale;
+  private ZoneId timeZoneId;
+  private DateTimeFormatter dateTimeFormat;
+  private DateTimeFormatter dateFormat;
+  
   /**
    * 
    * @param context - access to all related resources that might be needed
@@ -136,6 +147,8 @@ public class RenderingContext {
     if (terminologyServiceOptions != null) {
       this.terminologyServiceOptions = terminologyServiceOptions;
     }
+ // default to US locale - discussion here: https://github.com/hapifhir/org.hl7.fhir.core/issues/666
+    this.locale = new Locale.Builder().setLanguageTag("en-US").build(); 
     profileUtilities = new ProfileUtilities(worker, null, null);
   }
 
@@ -427,4 +440,79 @@ public class RenderingContext {
     return mode == ResourceRendererMode.TECHNICAL;
   }
 
+  public boolean hasLocale() {
+    return locale != null;
+  }
+  
+  public Locale getLocale() {
+    if (locale == null) {
+      return Locale.getDefault();
+    } else { 
+      return locale;
+    }
+  }
+
+  public void setLocale(Locale locale) {
+    this.locale = locale;
+  }
+
+
+  /**
+   * if the timezone is null, the rendering will default to the source timezone
+   * in the resource
+   * 
+   * Note that if you're working server side, the FHIR project recommends the use
+   * of the Date header so that clients know what timezone the server defaults to,
+   * 
+   * There is no standard way for the server to know what the client timezone is. 
+   * In the case where the client timezone is unknown, the timezone should be null
+   *
+   * @return the specified timezone to render in
+   */
+  public ZoneId getTimeZoneId() {
+    return timeZoneId;
+  }
+
+  public void setTimeZoneId(ZoneId timeZoneId) {
+    this.timeZoneId = timeZoneId;
+  }
+
+
+  /**
+   * In the absence of a specified format, the renderers will default to 
+   * the FormatStyle.MEDIUM for the current locale.
+   * 
+   * @return the format to use
+   */
+  public DateTimeFormatter getDateTimeFormat() {
+    return this.dateTimeFormat;
+  }
+
+  public void setDateTimeFormat(DateTimeFormatter dateTimeFormat) {
+    this.dateTimeFormat = dateTimeFormat;
+  }
+
+  /**
+   * In the absence of a specified format, the renderers will default to 
+   * the FormatStyle.MEDIUM for the current locale.
+   * 
+   * @return the format to use
+   */
+  public DateTimeFormatter getDateFormat() {
+    return this.dateFormat;
+  }
+
+  public void setDateFormat(DateTimeFormatter dateFormat) {
+    this.dateFormat = dateFormat;
+  }
+
+  public ResourceRendererMode getMode() {
+    return mode;
+  }
+
+  public void setMode(ResourceRendererMode mode) {
+    this.mode = mode;
+  }
+  
+  
 }
\ No newline at end of file
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/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java
index 03d7fbdf8..b64be0281 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java
@@ -33,6 +33,8 @@ package org.hl7.fhir.r5.terminologies;
 
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -51,6 +53,7 @@ import org.hl7.fhir.r5.model.CodeType;
 import org.hl7.fhir.r5.model.DataType;
 import org.hl7.fhir.r5.model.DateTimeType;
 import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
+import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter;
 import org.hl7.fhir.r5.model.Identifier;
 import org.hl7.fhir.r5.model.Meta;
 import org.hl7.fhir.r5.model.UriType;
@@ -60,6 +63,15 @@ import org.hl7.fhir.utilities.Utilities;
 
 public class CodeSystemUtilities {
 
+  public static class ConceptDefinitionComponentSorter implements Comparator {
+
+    @Override
+    public int compare(ConceptDefinitionComponent o1, ConceptDefinitionComponent o2) {
+      return o1.getCode().compareTo(o2.getCode());
+    }
+
+  }
+
   public static final String USER_DATA_CROSS_LINK = "cs.utils.cross.link";
 
   public static class CodeSystemNavigator {
@@ -128,7 +140,7 @@ public class CodeSystemUtilities {
     private List getSubsumedBy(ConceptDefinitionComponent cd) {
       List codes = new ArrayList<>();
       for (ConceptPropertyComponent cp : cd.getProperty()) {
-        if (cp.getCode().equals("subsumedBy")) {
+        if ("subsumedBy".equals(cp.getCode())) {
           codes.add(cp.getValue().primitiveValue());
         }
       }
@@ -149,7 +161,7 @@ public class CodeSystemUtilities {
 
   public static boolean isNotSelectable(CodeSystem cs, ConceptDefinitionComponent def) {
     for (ConceptPropertyComponent p : def.getProperty()) {
-      if (p.getCode().equals("notSelectable") && p.hasValue() && p.getValue() instanceof BooleanType) 
+      if ("notSelectable".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 
         return ((BooleanType) p.getValue()).getValue();
     }
     return false;
@@ -212,14 +224,14 @@ public class CodeSystemUtilities {
     try {
       for (ConceptPropertyComponent p : def.getProperty()) {
         if (!ignoreStatus) {
-          if (p.getCode().equals("status") && p.hasValue() && p.hasValueCodeType() && p.getValueCodeType().getCode().equals("deprecated"))
+          if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "deprecated".equals(p.getValueCodeType().getCode()))
             return true;
         }
         // this, though status should also be set
-        if (p.getCode().equals("deprecationDate") && p.hasValue() && p.getValue() instanceof DateTimeType) 
+        if ("deprecationDate".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof DateTimeType) 
           return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance()));
         // legacy  
-        if (p.getCode().equals("deprecated") && p.hasValue() && p.getValue() instanceof BooleanType) 
+        if ("deprecated".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 
           return ((BooleanType) p.getValue()).getValue();
       }
       return false;
@@ -236,7 +248,7 @@ public class CodeSystemUtilities {
   
   public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException {
     for (ConceptPropertyComponent p : def.getProperty()) {
-      if (p.getCode().equals("status") && p.hasValueStringType()) 
+      if ("status".equals(p.getCode()) && p.hasValueStringType()) 
         return "inactive".equals(p.getValueStringType());
     }
     return false;
@@ -273,10 +285,14 @@ public class CodeSystemUtilities {
   }
 
   public static CodeSystem makeShareable(CodeSystem cs) {
+    if (!cs.hasExperimental()) {
+      cs.setExperimental(false);
+    }
+
     if (!cs.hasMeta())
       cs.setMeta(new Meta());
     for (UriType t : cs.getMeta().getProfile()) 
-      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"))
+      if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue()))
         return cs;
     cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"));
     return cs;
@@ -286,7 +302,7 @@ public class CodeSystemUtilities {
     if (!cs.hasMeta())
       cs.setMeta(new Meta());
     for (UriType t : cs.getMeta().getProfile()) 
-      if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"))
+      if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue()))
         return false;
     cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem"));
     return true;
@@ -350,8 +366,12 @@ public class CodeSystemUtilities {
     }
     if (fmm != null) {
       String sfmm = ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_FMM_LEVEL);
-      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) 
+      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) { 
         ToolingExtensions.setIntegerExtension(cs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
+      }
+      if (Integer.parseInt(fmm) <= 1) {
+        cs.setExperimental(true);
+      }
     }
   }
 
@@ -490,5 +510,27 @@ public class CodeSystemUtilities {
     }
     
   }
+
+  public static boolean hasHierarchy(CodeSystem cs) {
+    for (ConceptDefinitionComponent c : cs.getConcept()) {
+      if (c.hasConcept()) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static void sortAllCodes(CodeSystem cs) {
+    sortAllCodes(cs.getConcept());
+  }
+
+  private static void sortAllCodes(List list) {
+    Collections.sort(list, new ConceptDefinitionComponentSorter());
+    for (ConceptDefinitionComponent cd : list) {
+      if (cd.hasConcept()) {
+        sortAllCodes(cd.getConcept());
+      }
+    }    
+  }
   
 }
\ No newline at end of file
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyCacheManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyCacheManager.java
index ad9f2e3ae..874542ea3 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyCacheManager.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyCacheManager.java
@@ -17,6 +17,7 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Base64;
+import java.util.Date;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipOutputStream;
@@ -75,7 +76,8 @@ public class TerminologyCacheManager {
     }
 
     IniFile ini = new IniFile(Utilities.path(cacheFolder, "cache.ini"));
-    ini.setStringProperty("version", "version", version, null);
+    ini.setStringProperty("cache", "version", version, null);
+    ini.setDateProperty("cache", "last-use", new Date(), null);
     ini.save();
   }
 
@@ -116,7 +118,7 @@ public class TerminologyCacheManager {
 
   private String getCacheVersion() throws IOException {
     IniFile ini = new IniFile(Utilities.path(cacheFolder, "cache.ini"));
-    return ini.getStringProperty("version", "version");
+    return ini.getStringProperty("cache", "version");
   }
 
   public String getFolder() {
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 6a906bb49..706e70e7a 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,10 +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);
-      }
+      CodeSystem cs = resolveCodeSystem(system);
       if (cs == null) {
         warningMessage = "Unable to resolve system "+system;
         if (!inExpansion) {
@@ -498,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;
         }
@@ -604,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();
@@ -709,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/terminologies/ValueSetExpanderSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java
index 382c10419..bd45f5cac 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java
@@ -634,6 +634,9 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
       if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
         addFragmentWarning(exp, cs);
       }
+      if (cs.getContent() == CodeSystemContentMode.EXAMPLE) {
+        addExampleWarning(exp, cs);
+      }      
     }
 
     if (!inc.getConcept().isEmpty()) {
@@ -645,6 +648,8 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
         if (def == null) {
           if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
             addFragmentWarning(exp, cs);
+          } else if (cs.getContent() == CodeSystemContentMode.EXAMPLE) {
+              addExampleWarning(exp, cs);
           } else {
             if (checkCodesWhenExpanding) {
               throw failTSE("Unable to find code '" + c.getCode() + "' in code system " + cs.getUrl());
@@ -733,14 +738,25 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
   }
 
   private void addFragmentWarning(ValueSetExpansionComponent exp, CodeSystem cs) {
-    for (Extension ex : cs.getExtensionsByUrl(ToolingExtensions.EXT_EXP_FRAGMENT)) {
-      if (ex.getValue().primitiveValue().equals(cs.getUrl())) {
+    String url = cs.getVersionedUrl();
+    for (ValueSetExpansionParameterComponent p : exp.getParameter()) {
+      if ("fragment".equals(p.getName()) && p.hasValueUriType() && url.equals(p.getValue().primitiveValue())) { 
         return;
-      }
+      }     
     }
-    exp.addExtension(new Extension(ToolingExtensions.EXT_EXP_FRAGMENT).setValue(new UriType(cs.getUrl())));
+    exp.addParameter().setName("fragment").setValue(new UriType(url));
   }
 
+  private void addExampleWarning(ValueSetExpansionComponent exp, CodeSystem cs) {
+    String url = cs.getVersionedUrl();
+    for (ValueSetExpansionParameterComponent p : exp.getParameter()) {
+      if ("example".equals(p.getName()) && p.hasValueUriType() && url.equals(p.getValue().primitiveValue())) { 
+        return;
+      }     
+    }
+    exp.addParameter().setName("example").setValue(new UriType(url));
+  }
+  
   private List convertDesignations(List list) {
     List res = new ArrayList();
     for (ConceptReferenceDesignationComponent t : list) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java
index ae0f24f9e..af153bea1 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java
@@ -48,6 +48,9 @@ import org.hl7.fhir.utilities.Utilities;
 public class ValueSetUtilities {
 
   public static ValueSet makeShareable(ValueSet vs) {
+    if (!vs.hasExperimental()) {
+      vs.setExperimental(false);
+    }
     if (!vs.hasMeta())
       vs.setMeta(new Meta());
     for (UriType t : vs.getMeta().getProfile()) 
@@ -129,8 +132,12 @@ public class ValueSetUtilities {
     }
     if (fmm != null) {
       String sfmm = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_FMM_LEVEL);
-      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) 
+      if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm))  {
         ToolingExtensions.setIntegerExtension(vs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm));
+      }
+      if (Integer.parseInt(fmm) <= 1) {
+        vs.setExperimental(true);
+      }
     }
     if (vs.hasUserData("cs"))
       CodeSystemUtilities.markStatus((CodeSystem) vs.getUserData("cs"), wg, status, pckage, fmm, normativeVersion);
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java
index 7c0d6cf53..af854cd8a 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/TestingUtilities.java
@@ -151,13 +151,14 @@ public class TestingUtilities extends BaseTestingUtilities {
     String result = compareXml(f1, f2);
     if (result != null && SHOW_DIFF) {
       String diff = Utilities.path(System.getenv("ProgramFiles"), "WinMerge", "WinMergeU.exe");
-      List command = new ArrayList();
-      command.add("\"" + diff + "\" \"" + f1 + "\" \"" + f2 + "\"");
-
-      ProcessBuilder builder = new ProcessBuilder(command);
-      builder.directory(new CSFile("c:\\temp"));
-      builder.start();
+      if (new File(diff).exists()) {
+        List command = new ArrayList();
+        command.add("\"" + diff + "\" \"" + f1 + "\" \"" + f2 + "\"");
 
+        ProcessBuilder builder = new ProcessBuilder(command);
+        builder.directory(new CSFile("c:\\temp"));
+        builder.start();
+      }
     }
     return result;
   }
@@ -171,7 +172,7 @@ public class TestingUtilities extends BaseTestingUtilities {
   }
 
   private static String compareElements(String path, Element e1, Element e2) {
-    if (!e1.getNamespaceURI().equals(e2.getNamespaceURI()))
+    if (!namespacesMatch(e1.getNamespaceURI(), e2.getNamespaceURI()))
       return "Namespaces differ at " + path + ": " + e1.getNamespaceURI() + "/" + e2.getNamespaceURI();
     if (!e1.getLocalName().equals(e2.getLocalName()))
       return "Names differ at " + path + ": " + e1.getLocalName() + "/" + e2.getLocalName();
@@ -209,6 +210,10 @@ public class TestingUtilities extends BaseTestingUtilities {
     return null;
   }
 
+  private static boolean namespacesMatch(String ns1, String ns2) {
+    return ns1 == null ? ns2 == null : ns1.equals(ns2);
+  }
+
   private static Object normalise(String text) {
     String result = text.trim().replace('\r', ' ').replace('\n', ' ').replace('\t', ' ');
     while (result.contains("  "))
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/IGHelper.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IGHelper.java
index e1a1dcbb7..6e22a43ac 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IGHelper.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IGHelper.java
@@ -40,6 +40,7 @@ public class IGHelper {
   public static final String EXT_MAPPING_CSV = ToolingExtensions.EXT_IGP_MAPPING_CSV;
   public static final String EXT_BUNDLE = ToolingExtensions.EXT_IGP_BUNDLE;
   public static final String EXT_RESOURCE_INFO = ToolingExtensions.EXT_IGP_RESOURCE_INFO;
+  public static final String EXT_CONTAINED_RESOURCE_INFO = ToolingExtensions.EXT_IGP_CONTAINED_RESOURCE_INFO;
   public static final String EXT_PRIVATE_BASE = ToolingExtensions.EXT_PRIVATE_BASE;
 
   public static String readStringParameter(ImplementationGuideDefinitionComponent ig, String name) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java
index 7744b4f8a..aa8438628 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java
@@ -276,7 +276,7 @@ public class NPMPackageGenerator {
       return "hl7.fhir.r3.core";
     if (v.startsWith("4.0"))
       return "hl7.fhir.r4.core";
-    if (v.startsWith("4.1"))
+    if (v.startsWith("4.1") || v.startsWith("4.3"))
       return "hl7.fhir.r4b.core";
     return null;
   }
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java
index d67049ee7..4248ba585 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/QuestionnaireBuilder.java
@@ -230,6 +230,7 @@ public class QuestionnaireBuilder {
       questionnaire.setPublisher(profile.getPublisher());
       Questionnaire.QuestionnaireItemComponent item = new Questionnaire.QuestionnaireItemComponent();
       questionnaire.addItem(item);
+      item.setLinkId("meta");
       item.getCode().addAll(profile.getKeyword());
       questionnaire.setId(nextId("qs"));
     }
@@ -241,6 +242,7 @@ public class QuestionnaireBuilder {
       response.setStatus(QuestionnaireResponseStatus.INPROGRESS);
       QuestionnaireResponse.QuestionnaireResponseItemComponent item = new QuestionnaireResponse.QuestionnaireResponseItemComponent();
       response.addItem(item);
+      item.setLinkId("meta");
       item.setUserData("object", resource);
     }
 
@@ -260,9 +262,10 @@ public class QuestionnaireBuilder {
 	  	display.setType(QuestionnaireItemType.DISPLAY);
 	  	display.setText(element.getComment());
 	  	group.addItem(display);
+	  	display.setLinkId(element.getId()+"-display");
 	  }
 	  group.setType(QuestionnaireItemType.GROUP);
-	  ToolingExtensions.addFlyOver(group, element.getDefinition());
+	  ToolingExtensions.addFlyOver(group, element.getDefinition(), element.getId()+"-flyover");
     group.setRequired(element.getMin() > 0);
     if (element.getMin() > 0)
     	ToolingExtensions.addMin(group, element.getMin());
@@ -284,10 +287,11 @@ public class QuestionnaireBuilder {
         nparents.addAll(parents);
         nparents.add(child);
         QuestionnaireItemComponent childGroup = group.addItem();
+        childGroup.setLinkId(child.getId()+"-grp");
         childGroup.setType(QuestionnaireItemType.GROUP);
 
         List nResponse = new ArrayList();
-        processExisting(child.getPath(), answerGroups, nResponse);
+        processExisting(child.getPath(), answerGroups, childGroup, nResponse);
         // if the element has a type, we add a question. else we add a group on the basis that
         // it will have children of its own
         if (child.getType().isEmpty() || isAbstractType(child.getType())) 
@@ -334,13 +338,14 @@ public class QuestionnaireBuilder {
     return path.substring(path.lastIndexOf('.')+1);
   }
 
-  private void processExisting(String path, List answerGroups, List nResponse) throws FHIRException {
+  private void processExisting(String path, List answerGroups, QuestionnaireItemComponent item, List nResponse) throws FHIRException {
     // processing existing data
     for (QuestionnaireResponse.QuestionnaireResponseItemComponent ag : answerGroups) {
       List children = ((Element) ag.getUserData("object")).listChildrenByName(tail(path));
       for (Base child : children) {
         if (child != null) {
           QuestionnaireResponse.QuestionnaireResponseItemComponent ans = ag.addItem();
+          ag.setLinkId(item.getLinkId());
           ans.setUserData("object", child);
           nResponse.add(ans);
         }
@@ -366,9 +371,9 @@ public class QuestionnaireBuilder {
       }
 
       if (!Utilities.noString(element.getComment())) 
-        ToolingExtensions.addFlyOver(group, element.getDefinition()+" "+element.getComment());
+        ToolingExtensions.addFlyOver(group, element.getDefinition()+" "+element.getComment(), group.getLinkId()+"-flyover");
       else
-        ToolingExtensions.addFlyOver(group, element.getDefinition());
+        ToolingExtensions.addFlyOver(group, element.getDefinition(), group.getLinkId()+"-flyover");
 
       if (element.getType().size() > 1 || element.getType().get(0).getWorkingCode().equals("*")) {
         List types = expandTypeList(element.getType());
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 9a9c753b4..da07639c6 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
@@ -69,7 +69,9 @@ import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.fhir.ucum.Utilities;
 import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r5.model.Base;
 import org.hl7.fhir.r5.model.BooleanType;
+import org.hl7.fhir.r5.model.CanonicalResource;
 import org.hl7.fhir.r5.model.CanonicalType;
 import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
 import org.hl7.fhir.r5.model.CodeType;
@@ -89,6 +91,7 @@ import org.hl7.fhir.r5.model.IntegerType;
 import org.hl7.fhir.r5.model.MarkdownType;
 import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
 import org.hl7.fhir.r5.model.PrimitiveType;
+import org.hl7.fhir.r5.model.Property;
 import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent;
 import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemType;
 import org.hl7.fhir.r5.model.StringType;
@@ -166,6 +169,7 @@ public class ToolingExtensions {
   public static final String EXT_IGP_MAPPING_CSV = "http://hl7.org/fhir/StructureDefinition/igpublisher-mapping-csv";
   public static final String EXT_IGP_BUNDLE = "http://hl7.org/fhir/StructureDefinition/igpublisher-bundle";
   public static final String EXT_IGP_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/resource-information";
+  public static final String EXT_IGP_CONTAINED_RESOURCE_INFO = "http://hl7.org/fhir/tools/StructureDefinition/contained-resource-information";
   public static final String EXT_IGP_LOADVERSION = "http://hl7.org/fhir/StructureDefinition/igpublisher-loadversion";
   public static final String EXT_MAX_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet";
   public static final String EXT_MIN_VALUESET = "http://hl7.org/fhir/StructureDefinition/elementdefinition-minValueSet";
@@ -182,7 +186,6 @@ public class ToolingExtensions {
   public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
   public static final String EXT_RENDERED_VALUE = "http://hl7.org/fhir/StructureDefinition/rendered-value";
   public static final String EXT_OLD_CONCEPTMAP_EQUIVALENCE = "http://hl7.org/fhir/1.0/StructureDefinition/extension-ConceptMap.element.target.equivalence";
-  public static final String EXT_EXP_FRAGMENT = "http://hl7.org/fhir/tools/StructureDefinition/expansion-codesystem-fragment";
   public static final String EXT_EXP_TOOCOSTLY = "http://hl7.org/fhir/StructureDefinition/valueset-toocostly";
   public static final String EXT_MUST_SUPPORT = "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support";
   public static final String EXT_TRANSLATABLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable";
@@ -192,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
 
@@ -462,11 +468,12 @@ public class ToolingExtensions {
 //    return findBooleanExtension(c, EXT_DEPRECATED);    
 //  }
 
-  public static void addFlyOver(QuestionnaireItemComponent item, String text){
+  public static void addFlyOver(QuestionnaireItemComponent item, String text, String linkId){
     if (!StringUtils.isBlank(text)) {
     	QuestionnaireItemComponent display = item.addItem();
     	display.setType(QuestionnaireItemType.DISPLAY);
     	display.setText(text);
+    	display.setLinkId(linkId);
     	display.getExtension().add(Factory.newExtension(EXT_CONTROL, Factory.newCodeableConcept("flyover", "http://hl7.org/fhir/questionnaire-item-control", "Fly-over"), true));
     }
   }
@@ -900,5 +907,25 @@ public class ToolingExtensions {
       dr.getExtension().add(Factory.newExtension(url, new UriType(value), true));   
   }
 
+  public static boolean usesExtension(String url, Base base) {
+    if ("Extension".equals(base.fhirType())) {
+      Property p = base.getNamedProperty("url");
+      for (Base b : p.getValues()) {
+        if (url.equals(b.primitiveValue())) {
+          return true;
+        }
+      }
+    }
+    
+    for (Property p : base.children() ) {
+      for (Base v : p.getValues()) {
+        if (usesExtension(url, v)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   
 }
\ No newline at end of file
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java
index 7c210a247..22c7f7706 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/TypesUtilities.java
@@ -35,6 +35,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.hl7.fhir.utilities.Utilities;
+import org.hl7.fhir.utilities.VersionUtilities;
+import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo;
 
 
 public class TypesUtilities {
@@ -111,7 +113,7 @@ public class TypesUtilities {
     res.add(new WildcardInformation("id", TypeClassification.PRIMITIVE));
     res.add(new WildcardInformation("instant", TypeClassification.PRIMITIVE));
     res.add(new WildcardInformation("integer", TypeClassification.PRIMITIVE));
-    if (!version.startsWith("4.1")) {
+    if (!VersionUtilities.isR4BVer(version)) {
       res.add(new WildcardInformation("integer64", TypeClassification.PRIMITIVE));
     }
     res.add(new WildcardInformation("markdown", TypeClassification.PRIMITIVE));
@@ -130,6 +132,7 @@ public class TypesUtilities {
     res.add(new WildcardInformation("Annotation", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Attachment", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("CodeableConcept", TypeClassification.DATATYPE));
+    res.add(new WildcardInformation("CodeableReference", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Coding", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("ContactPoint", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Count", TypeClassification.DATATYPE));
@@ -142,6 +145,7 @@ public class TypesUtilities {
     res.add(new WildcardInformation("Quantity", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Range", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Ratio", TypeClassification.DATATYPE));
+    res.add(new WildcardInformation("RatioRange", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Reference", " - a reference to another resource", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("SampledData", TypeClassification.DATATYPE));
     res.add(new WildcardInformation("Signature", TypeClassification.DATATYPE));
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java
index 6ddc0c527..ddf283329 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/XVerExtensionManager.java
@@ -40,7 +40,15 @@ public class XVerExtensionManager {
     this.context = context;
   }
 
+  public boolean isR5(String url) {
+    String v = url.substring(20, 23);
+    return "5.0".equals(v);    
+  }
+  
   public XVerExtensionStatus status(String url) throws FHIRException {
+    if (url.length() < 24) {
+      return XVerExtensionStatus.Invalid;
+    }
     String v = url.substring(20, 23);
     if ("5.0".equals(v)) {
       v = "4.6"; // for now
@@ -125,6 +133,11 @@ public class XVerExtensionManager {
     } else {
       throw new FHIRException("Internal error - attempt to define extension for "+url+" when it is invalid");
     }
+    if (path.has("modifier") && path.get("modifier").getAsBoolean()) {
+      ElementDefinition baseDef = new ElementDefinition("Extension");
+      sd.getDifferential().getElement().add(0, baseDef);
+      baseDef.setIsModifier(true);
+    }
     return sd;
   }
 
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java
index 02c18b66f..b719907d9 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/Client.java
@@ -4,7 +4,6 @@ import okhttp3.Headers;
 import okhttp3.MediaType;
 import okhttp3.Request;
 import okhttp3.RequestBody;
-import okhttp3.logging.HttpLoggingInterceptor;
 import org.hl7.fhir.r5.model.Bundle;
 import org.hl7.fhir.r5.model.Resource;
 import org.hl7.fhir.r5.utils.client.EFhirClientException;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java
index d012b0acd..b68d58430 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java
@@ -2,8 +2,8 @@ package org.hl7.fhir.r5.utils.client.network;
 
 import okhttp3.*;
 import org.hl7.fhir.utilities.ToolingClientLogger;
-import org.jetbrains.annotations.NotNull;
 
+import javax.annotation.Nonnull;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -22,9 +22,8 @@ public class FhirLoggingInterceptor implements Interceptor {
     return this;
   }
 
-  @NotNull
   @Override
-  public Response intercept(@NotNull Interceptor.Chain chain) throws IOException {
+  public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException {
     // Log Request
     Request request = chain.request();
     logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()),
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/RetryInterceptor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/RetryInterceptor.java
index 604478366..d6f4e2de1 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/RetryInterceptor.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/RetryInterceptor.java
@@ -35,8 +35,8 @@ public class RetryInterceptor implements Interceptor {
       try {
         // If we are retrying a failed request that failed due to a bad response from the server, we must close it first
         if (response != null) {
-          System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
-            + "> from url -> " + chain.request().url() + ".");
+//          System.out.println("Previous " + chain.request().method() + " attempt returned HTTP<" + (response.code())
+//            + "> from url -> " + chain.request().url() + ".");
           response.close();
         }
         // System.out.println(chain.request().method() + " attempt <" + (retryCounter + 1) + "> to url -> " + chain.request().url());
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FFHIRPathHostServices.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FFHIRPathHostServices.java
index 3748e2235..c5024dc22 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FFHIRPathHostServices.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/structuremap/FFHIRPathHostServices.java
@@ -9,7 +9,7 @@ import org.hl7.fhir.r5.model.Resource;
 import org.hl7.fhir.r5.model.TypeDetails;
 import org.hl7.fhir.r5.model.ValueSet;
 import org.hl7.fhir.r5.utils.FHIRPathEngine;
-import org.hl7.fhir.r5.utils.IResourceValidator;
+import org.hl7.fhir.r5.utils.validation.IResourceValidator;
 import org.hl7.fhir.utilities.validation.ValidationMessage;
 
 import java.util.ArrayList;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/BundleValidationRule.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/BundleValidationRule.java
new file mode 100644
index 000000000..370f67c4c
--- /dev/null
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/BundleValidationRule.java
@@ -0,0 +1,29 @@
+package org.hl7.fhir.r5.utils.validation;
+
+public class BundleValidationRule {
+  private String rule;
+  private String profile;
+  private boolean checked;
+
+  public BundleValidationRule(String rule, String profile) {
+    super();
+    this.rule = rule;
+    this.profile = profile;
+  }
+
+  public String getRule() {
+    return rule;
+  }
+
+  public String getProfile() {
+    return profile;
+  }
+
+  public boolean isChecked() {
+    return checked;
+  }
+
+  public void setChecked(boolean checked) {
+    this.checked = checked;
+  }
+}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java
similarity index 60%
rename from org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java
rename to org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java
index e1c24bf02..2ce6aca70 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/IResourceValidator.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IResourceValidator.java
@@ -1,4 +1,4 @@
-package org.hl7.fhir.r5.utils;
+package org.hl7.fhir.r5.utils.validation;
 
 /*
   Copyright (c) 2011+, HL7, Inc.
@@ -30,26 +30,18 @@ package org.hl7.fhir.r5.utils;
  */
 
 
+import com.google.gson.JsonObject;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
+import org.hl7.fhir.r5.model.StructureDefinition;
+import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
+import org.hl7.fhir.r5.utils.validation.constants.CheckDisplayOption;
+import org.hl7.fhir.r5.utils.validation.constants.IdStatus;
+import org.hl7.fhir.utilities.validation.ValidationMessage;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URISyntaxException;
 import java.util.List;
-import java.util.Locale;
-
-import org.hl7.fhir.exceptions.DefinitionException;
-import org.hl7.fhir.exceptions.FHIRException;
-import org.hl7.fhir.exceptions.FHIRFormatError;
-import org.hl7.fhir.r5.elementmodel.Element;
-import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
-import org.hl7.fhir.r5.model.CanonicalResource;
-import org.hl7.fhir.r5.model.StructureDefinition;
-import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule;
-import org.hl7.fhir.utilities.Utilities;
-import org.hl7.fhir.utilities.validation.ValidationMessage;
-
-import com.google.gson.JsonObject;
 
 /**
  * Interface to the instance validator. This takes a resource, in one of many forms, and 
@@ -60,124 +52,21 @@ import com.google.gson.JsonObject;
    */
 public interface IResourceValidator {
 
-  public class BundleValidationRule {
-    private String rule;
-    private String profile;
-    private boolean checked;
-    
-    public BundleValidationRule(String rule, String profile) {
-      super();
-      this.rule = rule;
-      this.profile = profile;
-    }
-    public String getRule() {
-      return rule;
-    }
-    public String getProfile() {
-      return profile;
-    }
-    public boolean isChecked() {
-      return checked;
-    }
-    public void setChecked(boolean checked) {
-      this.checked = checked;
-    }    
-  }
-
-  public enum ReferenceValidationPolicy {
-    IGNORE, CHECK_TYPE_IF_EXISTS, CHECK_EXISTS, CHECK_EXISTS_AND_TYPE, CHECK_VALID;
-    
-    public boolean checkExists() {
-      return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID || this == CHECK_TYPE_IF_EXISTS;
-    }
-    
-    public boolean checkType() {
-      return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID;
-    }
-    
-    public boolean checkValid() {
-      return this == CHECK_VALID;
-    }
-  }
-  
-  public interface IValidationProfileUsageTracker {
-    void recordProfileUsage(StructureDefinition profile, Object appContext, Element element);
-  }
-  
-
-  public interface IValidatorResourceFetcher {
-
-    Element fetch(IResourceValidator validator, Object appContext, String url) throws FHIRFormatError, DefinitionException, FHIRException, IOException;
-    ReferenceValidationPolicy validationPolicy(IResourceValidator validator, Object appContext, String path, String url);
-    boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException;
-
-    byte[] fetchRaw(IResourceValidator validator, String url) throws MalformedURLException, IOException; // for attachment checking
-
-    IValidatorResourceFetcher setLocale(Locale locale);
-    
-    
-    /**
-     * this is used when the validator encounters a reference to a structure definition, value set or code system at some random URL reference 
-     * while validating. 
-     *
-     *  Added in v5.2.2. return null to leave functionality as it was before then.
-     *  
-     * @param primitiveValue
-     * @return an R5 version of the resource
-     * @throws URISyntaxException 
-     */
-    CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException;
-    
-    /**
-     * Whether to try calling fetchCanonicalResource for this reference (not whether it will succeed - just throw an exception from fetchCanonicalResource if it doesn't resolve. This is a policy thing.
-     * 
-     *  Added in v5.2.2. return false to leave functionality as it was before then.
-     *  
-     * @param url
-     * @return
-     */
-    boolean fetchesCanonicalResource(IResourceValidator validator, String url);
-  }
-  
-  public enum BestPracticeWarningLevel {
-    Ignore,
-    Hint,
-    Warning,
-    Error
-  }
-
-  public enum CheckDisplayOption {
-    Ignore,
-    Check,
-    CheckCaseAndSpace,
-    CheckCase,
-    CheckSpace
-  }
-
-  enum IdStatus {
-    OPTIONAL, REQUIRED, PROHIBITED
-  }
-  
   /**
    * how much to check displays for coded elements 
-   * @return
    */
   CheckDisplayOption getCheckDisplay();
   void setCheckDisplay(CheckDisplayOption checkDisplay);
 
   /**
    * whether the resource must have an id or not (depends on context)
-   * 
-   * @return
    */
-
 	IdStatus getResourceIdRule();
 	void setResourceIdRule(IdStatus resourceIdRule);
   
   /**
    * whether the validator should enforce best practice guidelines
    * as defined by various HL7 committees 
-   *  
    */
   BestPracticeWarningLevel getBestPracticeWarningLevel();
   IResourceValidator setBestPracticeWarningLevel(BestPracticeWarningLevel value);
@@ -185,48 +74,46 @@ public interface IResourceValidator {
   IValidatorResourceFetcher getFetcher();
   IResourceValidator setFetcher(IValidatorResourceFetcher value);
 
+  IValidationPolicyAdvisor getPolicyAdvisor();
+  IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor);
+
   IValidationProfileUsageTracker getTracker();
   IResourceValidator setTracker(IValidationProfileUsageTracker value);
 
   boolean isNoBindingMsgSuppressed();
   IResourceValidator setNoBindingMsgSuppressed(boolean noBindingMsgSuppressed);
   
-  public boolean isNoInvariantChecks();
-  public IResourceValidator setNoInvariantChecks(boolean value) ;
+  boolean isNoInvariantChecks();
+  IResourceValidator setNoInvariantChecks(boolean value) ;
 
-  public boolean isWantInvariantInMessage();
-  public IResourceValidator setWantInvariantInMessage(boolean wantInvariantInMessage); 
+  boolean isWantInvariantInMessage();
+  IResourceValidator setWantInvariantInMessage(boolean wantInvariantInMessage);
 
-  public boolean isNoTerminologyChecks();
-  public IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
+  boolean isNoTerminologyChecks();
+  IResourceValidator setNoTerminologyChecks(boolean noTerminologyChecks);
 
-  public boolean isNoExtensibleWarnings();
-  public IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings);
+  boolean isNoExtensibleWarnings();
+  IResourceValidator setNoExtensibleWarnings(boolean noExtensibleWarnings);
   
-  public boolean isNoUnicodeBiDiControlChars();
-  public void setNoUnicodeBiDiControlChars(boolean noUnicodeBiDiControlChars);
+  boolean isNoUnicodeBiDiControlChars();
+  void setNoUnicodeBiDiControlChars(boolean noUnicodeBiDiControlChars);
   
   /**
    * Whether being unable to resolve a profile in found in Resource.meta.profile or ElementDefinition.type.profile or targetProfile is an error or just a warning
-   * @return
    */
-  public boolean isErrorForUnknownProfiles();
-  public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
+  boolean isErrorForUnknownProfiles();
+  void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
 
-  public boolean isShowMessagesFromReferences();
-  public void setShowMessagesFromReferences(boolean value);
+  boolean isShowMessagesFromReferences();
+  void setShowMessagesFromReferences(boolean value);
 
   /** 
    * this is used internally in the publishing stack to ensure that everything is water tight, but 
    * this check is not necessary or appropriate at run time when the validator is hosted in HAPI
    * @return
    */
-  public boolean isWantCheckSnapshotUnchanged();
-  public void setWantCheckSnapshotUnchanged(boolean wantCheckSnapshotUnchanged);
-  
-  //FIXME: don't need that, gets never used?
-//  public String getValidationLanguage();
-//  public void setValidationLanguage(String value);
+  boolean isWantCheckSnapshotUnchanged();
+  void setWantCheckSnapshotUnchanged(boolean wantCheckSnapshotUnchanged);
   
   /**
    * It's common to see references such as Patient/234234 - these usually mean a reference to a Patient resource. 
@@ -235,27 +122,28 @@ public interface IResourceValidator {
    * 
    * @return
    */
-  public boolean isAssumeValidRestReferences();
-  public void setAssumeValidRestReferences(boolean value);
+  boolean isAssumeValidRestReferences();
+  void setAssumeValidRestReferences(boolean value);
   
   /** 
    * if this is true, the validator will accept extensions and references to example.org and acme.com as 
    * valid, on the basis that they are understood to be references to content that could exist in priniple but can't in practice
    */
-  public boolean isAllowExamples();
-  public void setAllowExamples(boolean value) ;
+  boolean isAllowExamples();
+  void setAllowExamples(boolean value) ;
  
-  public boolean isNoCheckAggregation();
-  public void setNoCheckAggregation(boolean value);
+  boolean isNoCheckAggregation();
+  void setNoCheckAggregation(boolean value);
+
   /**
    * CrumbTrail - whether the validator creates hints to 
    * @return
    */
-  public boolean isCrumbTrails();
-  public void setCrumbTrails(boolean crumbTrails);
+  boolean isCrumbTrails();
+  void setCrumbTrails(boolean crumbTrails);
 
-  public boolean isValidateValueSetCodesOnTxServer();
-  public void setValidateValueSetCodesOnTxServer(boolean value);
+  boolean isValidateValueSetCodesOnTxServer();
+  void setValidateValueSetCodesOnTxServer(boolean value);
 
   /** 
    * Bundle validation rules allow for requesting particular entries in a bundle get validated against particular profiles
@@ -264,7 +152,7 @@ public interface IResourceValidator {
    *  
    * @return
    */
-  public List getBundleValidationRules();
+  List getBundleValidationRules();
   
   /**
    * Validate suite
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java
new file mode 100644
index 000000000..ac686f683
--- /dev/null
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationPolicyAdvisor.java
@@ -0,0 +1,82 @@
+package org.hl7.fhir.r5.utils.validation;
+
+import java.util.List;
+
+import org.hl7.fhir.r5.elementmodel.Element;
+import org.hl7.fhir.r5.model.ElementDefinition;
+import org.hl7.fhir.r5.model.StructureDefinition;
+import org.hl7.fhir.r5.model.ValueSet;
+import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy;
+import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
+import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy;
+import org.hl7.fhir.r5.utils.validation.constants.BindingKind;
+
+public interface IValidationPolicyAdvisor {
+
+  /**
+   *
+   * @param validator
+   * @param appContext What was originally provided from the app for it's context
+   * @param path Path that led us to this resource.
+   * @param url Url of the profile the container resource is being validated against.
+   * @return {@link ReferenceValidationPolicy}
+   */
+  ReferenceValidationPolicy policyForReference(IResourceValidator validator,
+                                               Object appContext,
+                                               String path,
+                                               String url);
+
+  /**
+   * //TODO pass through the actual containing Element as opposed to the type, id
+   * @param validator
+   * @param appContext What was originally provided from the app for it's context
+   * @param containerType Type of the resources that contains the resource being validated
+   * @param containerId Id of the resources that contains the resource being validated
+   * @param containingResourceType Type of the resource that will be validated (BUNDLE_ENTRY, BUNDLE_OUTCOME, CONTAINED_RESOURCE, PARAMETER)
+   * @param path Path that led us to this resource.
+   * @param url Url of the profile the container resource is being validated against.
+   * @return {@link ReferenceValidationPolicy}
+   */
+  ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator,
+                                                        Object appContext,
+                                                        String containerType,
+                                                        String containerId,
+                                                        Element.SpecialElement containingResourceType,
+                                                        String path,
+                                                        String url);
+
+  /**
+   * Called before validating a concept in an instance against the terminology sub-system
+   * 
+   * There's two reasons to use this policy advisor feature:
+   *   - save time by not calling the terminology server for validation that don't bring value to the context calling the validation
+   *   - suppressing known issues from being listed as a problem
+   *   
+   * Note that the terminology subsystem has two parts: a mini-terminology server running inside the 
+   * validator, and then calling out to an external terminology service (usually tx.fhir.org, though you
+   * run your own local copy of this - see https://confluence.hl7.org/display/FHIR/Running+your+own+copy+of+tx.fhir.org).
+   * You can't tell which subsystem will handle the terminology validation directly from the content provided here which
+   * subsystem will be called - you'll haev to investigate based on your set up. (matters, since it makes a huge performance 
+   * difference, though it also depends on caching, and the impact of caching is also not known at this point)
+   *   
+   * @param validator
+   * @param appContext What was originally provided from the app for it's context
+   * @param stackPath The current path for the stack. Note that the because of cross-references and FHIRPath conformsTo() statements, the stack can wind through the content unpredictably. 
+   * @param definition the definition being validated against (might be useful: ElementDefinition.base.path, ElementDefinition.type, ElementDefinition.binding
+   * @param structure The structure definition that contains the element definition being validated against (may be from the base spec, may be from a profile)
+   * @param kind The part of the binding being validated
+   * @param valueSet The value set for the binding part that's being validated 
+   * @param systems A list of canonical URls (including versions if known) of the systems in the instance that's being validated. Note that if a plain code is being validated, then there'll be no known system when this is called (systems will be empty, not null) 
+   * @return {@link CodedContentValidationPolicy}
+   */
+  CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator,
+                                                        Object appContext,
+                                                        String stackPath,
+                                                        ElementDefinition definition,
+                                                        StructureDefinition structure,
+                                                        BindingKind kind,
+                                                        ValueSet valueSet,
+                                                        List systems);
+
+  
+}
\ No newline at end of file
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationProfileUsageTracker.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationProfileUsageTracker.java
new file mode 100644
index 000000000..b0ef7bfd7
--- /dev/null
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidationProfileUsageTracker.java
@@ -0,0 +1,8 @@
+package org.hl7.fhir.r5.utils.validation;
+
+import org.hl7.fhir.r5.elementmodel.Element;
+import org.hl7.fhir.r5.model.StructureDefinition;
+
+public interface IValidationProfileUsageTracker {
+  void recordProfileUsage(StructureDefinition profile, Object appContext, Element element);
+}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidatorResourceFetcher.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidatorResourceFetcher.java
new file mode 100644
index 000000000..26378c3c8
--- /dev/null
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/IValidatorResourceFetcher.java
@@ -0,0 +1,41 @@
+package org.hl7.fhir.r5.utils.validation;
+
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r5.elementmodel.Element;
+import org.hl7.fhir.r5.model.CanonicalResource;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Locale;
+
+public interface IValidatorResourceFetcher {
+
+  Element fetch(IResourceValidator validator, Object appContext, String url) throws FHIRException, IOException;
+
+  boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException;
+
+  byte[] fetchRaw(IResourceValidator validator, String url) throws IOException; // for attachment checking
+
+  IValidatorResourceFetcher setLocale(Locale locale);
+
+  /**
+   * this is used when the validator encounters a reference to a structure definition, value set or code system at some random URL reference
+   * while validating.
+   * 

+ * Added in v5.2.2. return null to leave functionality as it was before then. + * + * @return an R5 version of the resource + * @throws URISyntaxException + */ + CanonicalResource fetchCanonicalResource(IResourceValidator validator, String url) throws URISyntaxException; + + /** + * Whether to try calling fetchCanonicalResource for this reference (not whether it will succeed - just throw an exception from fetchCanonicalResource if it doesn't resolve. This is a policy thing. + *

+ * Added in v5.2.2. return false to leave functionality as it was before then. + * + * @param url + * @return + */ + boolean fetchesCanonicalResource(IResourceValidator validator, String url); +} 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 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 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.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/BestPracticeWarningLevel.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/BestPracticeWarningLevel.java new file mode 100644 index 000000000..441fadea8 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/BestPracticeWarningLevel.java @@ -0,0 +1,8 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum BestPracticeWarningLevel { + Ignore, + Hint, + Warning, + Error +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/BindingKind.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/BindingKind.java new file mode 100644 index 000000000..51a929df0 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/BindingKind.java @@ -0,0 +1,14 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum BindingKind { + /** + * The primary binding e.g. ElementDefinition.binding.valueSet + */ + PRIMARY, + + /** + * The max value set + */ + MAX_VS; + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/CheckDisplayOption.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/CheckDisplayOption.java new file mode 100644 index 000000000..9a080366a --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/CheckDisplayOption.java @@ -0,0 +1,9 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum CheckDisplayOption { + Ignore, + Check, + CheckCaseAndSpace, + CheckCase, + CheckSpace +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/CodedContentValidationPolicy.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/CodedContentValidationPolicy.java new file mode 100644 index 000000000..4e3df1b7e --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/CodedContentValidationPolicy.java @@ -0,0 +1,21 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum CodedContentValidationPolicy { + /** + * don't validate the code + */ + IGNORE, + + /** + * validate the code against the underlying code systems + */ + CODE, + + /** + * validate the code against the value set too. + * Note that this isn't much faster than just validating the code since + * the expensive part is hitting the terminology server (if necessary) + * and that has to be done for the code part too + */ + VALUESET // +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/ContainedReferenceValidationPolicy.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/ContainedReferenceValidationPolicy.java new file mode 100644 index 000000000..8361ebdde --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/ContainedReferenceValidationPolicy.java @@ -0,0 +1,19 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum ContainedReferenceValidationPolicy { + IGNORE, + CHECK_TYPE, + CHECK_VALID; + + public boolean ignore() { + return this == IGNORE; + } + + public boolean checkType() { + return this == CHECK_TYPE || this == CHECK_VALID; + } + + public boolean checkValid() { + return this == CHECK_VALID; + } +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java new file mode 100644 index 000000000..84004111d --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/IdStatus.java @@ -0,0 +1,7 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum IdStatus { + OPTIONAL, + REQUIRED, + PROHIBITED +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/ReferenceValidationPolicy.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/ReferenceValidationPolicy.java new file mode 100644 index 000000000..616fb10eb --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/validation/constants/ReferenceValidationPolicy.java @@ -0,0 +1,25 @@ +package org.hl7.fhir.r5.utils.validation.constants; + +public enum ReferenceValidationPolicy { + IGNORE, + CHECK_TYPE_IF_EXISTS, + CHECK_EXISTS, + CHECK_EXISTS_AND_TYPE, + CHECK_VALID; + + public boolean ignore() { + return this == IGNORE; + } + + public boolean checkExists() { + return this == CHECK_EXISTS_AND_TYPE || this == CHECK_EXISTS || this == CHECK_VALID || this == CHECK_TYPE_IF_EXISTS; + } + + public boolean checkType() { + return this == CHECK_TYPE_IF_EXISTS || this == CHECK_EXISTS_AND_TYPE || this == CHECK_VALID; + } + + public boolean checkValid() { + return this == CHECK_VALID; + } +} diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java index bd1171480..7fe3a1645 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/FHIRPathTests.java @@ -15,6 +15,7 @@ import org.apache.commons.lang3.NotImplementedException; import org.fhir.ucum.UcumException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.BooleanType; @@ -187,7 +188,11 @@ public class FHIRPathTests { } else { res = resources.get(input); if (res == null) { - res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input)); + if (input.endsWith(".json")) { + res = new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", input)); + } else { + res = new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", input)); + } resources.put(input, res); } fp.check(res, res.getResourceType().toString(), res.getResourceType().toString(), node); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java index 3547785e7..b02ef7786 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java @@ -1,8 +1,13 @@ package org.hl7.fhir.r5.test; +import java.io.BufferedReader; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -56,7 +61,6 @@ public class NarrativeGenerationTests { public Base parseType(String xml, String type) throws FHIRFormatError, IOException, FHIRException { return new org.hl7.fhir.r5.formats.XmlParser().parseType(xml, type); } - } public static final String WINDOWS = "WINDOWS"; @@ -77,12 +81,14 @@ public class NarrativeGenerationTests { private String id; private boolean header; private boolean meta; + private boolean technical; public TestDetails(Element test) { super(); id = test.getAttribute("id"); header = "true".equals(test.getAttribute("header")); meta = "true".equals(test.getAttribute("meta")); + technical = "technical".equals(test.getAttribute("mode")); } public String getId() { @@ -133,6 +139,15 @@ public class NarrativeGenerationTests { rc.setDefinitionsTarget("test.html"); rc.setTerminologyServiceOptions(TerminologyServiceOptions.defaults()); rc.setParser(new TestTypeParser()); + + // getting timezones correct (well, at least consistent, so tests pass on any computer) + rc.setLocale(new java.util.Locale("en", "AU")); + rc.setTimeZoneId(ZoneId.of("Australia/Sydney")); + rc.setDateTimeFormat(null); + rc.setDateFormat(null); + rc.setMode(test.technical ? ResourceRendererMode.TECHNICAL : ResourceRendererMode.END_USER); + + Resource source; if (TestingUtilities.findTestResource("r5", "narrative", test.getId() + ".json")) { source = (Resource) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".json")); @@ -143,9 +158,12 @@ public class NarrativeGenerationTests { XhtmlNode x = RendererFactory.factory(source, rc).build(source); String target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html")); String output = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER; - TextFile.stringToFile(target, TestingUtilities.tempFile("narrative", test.getId() + ".target.html")); - TextFile.stringToFile(output, TestingUtilities.tempFile("narrative", test.getId() + ".output.html")); - Assertions.assertTrue(output.equals(target), "Output does not match expected"); + String tfn = TestingUtilities.tempFile("narrative", test.getId() + ".target.html"); + String ofn = TestingUtilities.tempFile("narrative", test.getId() + ".output.html"); + TextFile.stringToFile(target, tfn); + TextFile.stringToFile(output, ofn); + String msg = TestingUtilities.checkXMLIsSame(ofn, tfn); + Assertions.assertTrue(msg == null, "Output does not match expected: "+msg); if (test.isMeta()) { org.hl7.fhir.r5.elementmodel.Element e = Manager.parseSingle(context, TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".xml"), FhirFormat.XML); @@ -153,8 +171,10 @@ public class NarrativeGenerationTests { target = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html")); output = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER; - TextFile.stringToFile(output, TestingUtilities.tempFile("narrative", test.getId() + "-meta.output.html")); - Assertions.assertTrue(output.equals(target), "Output does not match expected (meta)"); + ofn = TestingUtilities.tempFile("narrative", test.getId() + "-meta.output.html"); + TextFile.stringToFile(output, ofn); + msg = TestingUtilities.checkXMLIsSame(ofn, tfn); + Assertions.assertTrue(msg == null, "Meta output does not match expected: "+msg); } } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGeneratorTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGeneratorTests.java index cdc8c5c6b..e48ff819e 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGeneratorTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGeneratorTests.java @@ -4,22 +4,36 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Locale; +import java.util.TimeZone; +import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.r5.formats.XmlParser; +import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.renderers.DataRenderer; import org.hl7.fhir.r5.renderers.RendererFactory; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.EOperationOutcome; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.xhtml.NodeType; +import org.hl7.fhir.utilities.xhtml.XhtmlComposer; +import org.hl7.fhir.utilities.xhtml.XhtmlNode; +import org.junit.Assert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.xmlpull.v1.XmlPullParserException; public class NarrativeGeneratorTests { - private static RenderingContext rc; @BeforeAll @@ -40,4 +54,61 @@ public class NarrativeGeneratorTests { new XmlParser().compose(s, r, true); s.close(); } + + + private void checkDateTimeRendering(String src, String lang, String country, ZoneId tz, String fmt, ResourceRendererMode mode, + String... expected) throws FHIRFormatError, DefinitionException, IOException { + rc.setLocale(new java.util.Locale(lang, country)); + rc.setTimeZoneId(tz); + if (fmt == null) { + rc.setDateTimeFormat(null); + rc.setDateFormat(null); + } else { + // really, it would be better to test patterns based on FormatStyle here, since + // that's what will be used in the real world, but + rc.setDateTimeFormat(DateTimeFormatter.ofPattern(fmt)); + rc.setDateFormat(DateTimeFormatter.ofPattern(fmt)); + } + rc.setMode(mode); + + DateTimeType dt = new DateTimeType(src); + String actual = new DataRenderer(rc).display(dt); + + Assert.assertTrue("Actual = "+actual+", expected one of "+Utilities.toString(expected), Utilities.existsInList(actual, expected)); + XhtmlNode node = new XhtmlNode(NodeType.Element, "p"); + new DataRenderer(rc).render(node, dt); + actual = new XhtmlComposer(true, false).compose(node); + Assert.assertTrue(actual.startsWith("

")); + Assert.assertTrue(actual.endsWith("

")); + Assert.assertTrue("Actual = "+actual+", expected one of "+Utilities.toString(expected), Utilities.existsInList(actual.substring(0, actual.length()-4).substring(3), expected)); +} + + @Test + public void testDateTimeRendering1() throws FHIRFormatError, DefinitionException, IOException { + checkDateTimeRendering("2021-11-19T14:13:12Z", "en", "AU", ZoneId.of("UTC"), null, ResourceRendererMode.TECHNICAL, "2021-11-19T14:13:12Z"); + } + + + @Test + public void testDateTimeRendering2() throws FHIRFormatError, DefinitionException, IOException { + checkDateTimeRendering("2021-11-19T14:13:12Z", "en", "AU", ZoneId.of("Australia/Sydney"), null, ResourceRendererMode.TECHNICAL, "2021-11-20T01:13:12+11:00"); + } + + @Test + public void testDateTimeRendering3() throws FHIRFormatError, DefinitionException, IOException { + checkDateTimeRendering("2021-11-19T14:13:12Z", "en", "AU", ZoneId.of("UTC"), "yyyy/MM/dd hh:mm:ss", ResourceRendererMode.TECHNICAL, "2021/11/19 02:13:12"); + } + + @Test // varies between versions, so multiple possible expected + public void testDateTimeRendering4() throws FHIRFormatError, DefinitionException, IOException { + checkDateTimeRendering("2021-11-19T14:13:12Z", "en", "AU", ZoneId.of("UTC"), null, ResourceRendererMode.END_USER, "19/11/21, 2:13 pm", "19/11/21 2:13 PM"); + } + + + @Test + public void testDateTimeRendering5() throws FHIRFormatError, DefinitionException, IOException { + checkDateTimeRendering("2021-11-19", "en", "AU", ZoneId.of("UTC"), null, ResourceRendererMode.END_USER, "19/11/21"); + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java index 9e1f1c7cf..fc2f92699 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/ParsingTests.java @@ -1,61 +1,27 @@ package org.hl7.fhir.r5.test; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.NotImplementedException; -import org.hl7.fhir.exceptions.DefinitionException; -import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; -import org.hl7.fhir.exceptions.PathEngineException; -import org.hl7.fhir.r5.conformance.ProfileUtilities; -import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; -import org.hl7.fhir.r5.context.IWorkerContext.IContextResourceLoader; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; -import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.Bundle; -import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; -import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.model.Resource; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; -import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; -import org.hl7.fhir.r5.renderers.RendererFactory; -import org.hl7.fhir.r5.renderers.utils.RenderingContext; -import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; -import org.hl7.fhir.r5.model.TypeDetails; -import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.test.utils.TestingUtilities; -import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.TextFile; -import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.ToolsVersion; -import org.hl7.fhir.utilities.validation.ValidationMessage; -import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; -import org.hl7.fhir.utilities.xml.XMLUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.xml.sax.SAXException; public class ParsingTests { diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java index 066edc1bd..8c06c149a 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java @@ -39,7 +39,7 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r5.utils.IResourceValidator; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.XVerExtensionManager; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.npm.CommonPackages; diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/FHIRToolingClientTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/FHIRToolingClientTest.java index 966392cf4..cc352cbc1 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/FHIRToolingClientTest.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/client/FHIRToolingClientTest.java @@ -6,7 +6,6 @@ import okhttp3.internal.http2.Header; import org.hl7.fhir.r5.model.*; import org.hl7.fhir.r5.utils.client.network.Client; import org.hl7.fhir.r5.utils.client.network.ResourceRequest; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -96,7 +95,6 @@ class FHIRToolingClientTest { return bundle; } - @NotNull private Patient generatePatient() { // Create a patient object Patient patient = new Patient(); @@ -115,7 +113,6 @@ class FHIRToolingClientTest { return patient; } - @NotNull private Observation generateObservation() { // Create an observation object Observation observation = new Observation(); diff --git a/org.hl7.fhir.report/pom.xml b/org.hl7.fhir.report/pom.xml index abd95f513..d99dafee2 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.utilities/pom.xml b/org.hl7.fhir.utilities/pom.xml index 1f5deda34..84afa17e3 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.5.12-SNAPSHOT + 5.6.16-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/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index 80b48d90d..30a220e02 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -1510,5 +1510,9 @@ public class Utilities { return -1; } + public static String toString(String[] expected) { + return "['"+String.join("' | '", expected)+"']"; + } + } \ No newline at end of file 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..cb463856b 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 @@ -127,11 +127,14 @@ public class VersionUtilities { } public static boolean isSupportedVersion(String version) { - return Utilities.existsInList(version, "1.0.2", "1.4.0", "3.0.2", "4.0.1", "4.1.0", CURRENT_FULL_VERSION); + if (version.contains("-")) { + version = version.substring(0, version.indexOf("-")); + } + return Utilities.existsInList(version, "1.0.2", "1.4.0", "3.0.2", "4.0.1", "4.1.0", "4.3.0",CURRENT_FULL_VERSION); } public static String listSupportedVersions() { - return "1.0.2, 1.4.0, 3.0.2, 4.0.1, 4.1.0, "+CURRENT_FULL_VERSION; + return "1.0.2, 1.4.0, 3.0.2, 4.0.1, 4.1.0, 4.3.0, "+CURRENT_FULL_VERSION; } public static boolean isR5Ver(String ver) { @@ -139,7 +142,7 @@ public class VersionUtilities { } public static boolean isR4BVer(String ver) { - return ver != null && ver.startsWith("4.1"); + return ver != null && (ver.startsWith("4.1") || ver.startsWith("4.3")); } public static boolean isR4Ver(String ver) { @@ -397,7 +400,6 @@ public class VersionUtilities { } if (isR4Ver(version)) { - res.add("CodeSystem"); res.add("ActivityDefinition"); res.add("CapabilityStatement"); @@ -429,6 +431,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 +513,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 d0c752264..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 { @@ -348,6 +349,7 @@ public class I18nConstants { public static final String REFERENCE_REF_RESOURCETYPE = "Reference_REF_ResourceType"; public static final String REFERENCE_REF_SUSPICIOUS = "REFERENCE_REF_SUSPICIOUS"; public static final String REFERENCE_REF_WRONGTARGET = "Reference_REF_WrongTarget"; + public static final String REFERENCE_REF_WRONGTARGET_LOAD = "REFERENCE_REF_WRONGTARGET_LOAD"; public static final String REFERENCE_TO__CANNOT_BE_RESOLVED = "reference_to__cannot_be_resolved"; public static final String REFERENCE__REFERS_TO_A__NOT_A_VALUESET = "Reference__refers_to_a__not_a_ValueSet"; public static final String RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED = "Resource_resolution_services_not_provided"; @@ -438,7 +440,7 @@ public class I18nConstants { public static final String TERMINOLOGY_TX_SYSTEM_VALUESET = "Terminology_TX_System_ValueSet"; public static final String TERMINOLOGY_TX_SYSTEM_VALUESET2 = "Terminology_TX_System_ValueSet2"; public static final String TERMINOLOGY_TX_VALUESET_NOTFOUND = "Terminology_TX_ValueSet_NotFound"; - public static final String TERMINOLOGY_TX_VALUESET_NOTFOUND2 = "Terminology_TX_ValueSet_NotFound2"; + public static final String TERMINOLOGY_TX_VALUESET_NOTFOUND_CS = "Terminology_TX_ValueSet_NotFound_CS"; public static final String TERMINOLOGY_TX_NOSVC_BOUND_REQ = "TERMINOLOGY_TX_NOSVC_BOUND_REQ"; public static final String TERMINOLOGY_TX_NOSVC_BOUND_EXT = "TERMINOLOGY_TX_NOSVC_BOUND_EXT"; public static final String TEXT_SHOULD_NOT_BE_PRESENT = "Text_should_not_be_present"; @@ -482,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"; @@ -510,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"; @@ -652,6 +657,42 @@ public class I18nConstants { public static final String BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME = "BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME"; public static final String UNICODE_BIDI_CONTROLS_CHARS_DISALLOWED = "UNICODE_BIDI_CONTROLS_CHARS_DISALLOWED"; public static final String UNICODE_BIDI_CONTROLS_CHARS_MATCH = "UNICODE_BIDI_CONTROLS_CHARS_MATCH"; + public static final String CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHALL = "CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHALL"; + public static final String CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHOULD = "CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHOULD"; + public static final String CODESYSTEM_CS_NONHL7_MISSING_ELEMENT = "CODESYSTEM_CS_NONHL7_MISSING_ELEMENT"; + 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/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java index 6fba41e07..b37965b2c 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/json/JsonTrackingParser.java @@ -664,6 +664,12 @@ public class JsonTrackingParser { TextFile.stringToFile(jcnt, file); } + public static void write(JsonObject json, File file, boolean pretty) throws IOException { + Gson gson = pretty ? new GsonBuilder().setPrettyPrinting().create() : new GsonBuilder().create(); + String jcnt = gson.toJson(json); + TextFile.stringToFile(jcnt, file); + } + public static void write(JsonObject json, String fileName) throws IOException { Gson gson = new GsonBuilder().setPrettyPrinting().create(); String jcnt = gson.toJson(json); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java index c41de02f5..00cb450c8 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/BasePackageCacheManager.java @@ -77,7 +77,7 @@ public abstract class BasePackageCacheManager implements IPackageCacheManager { String url = packageClient.url(id, version); return new InputStreamWithSrc(stream, url, version); } catch (IOException e) { - ourLog.info("Failed to resolve package {}#{} from server: {}", id, version, nextPackageServer); + ourLog.info("Failed to resolve package {}#{} from server: {} ({})", id, version, nextPackageServer, e.getMessage()); } } } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java index b65e206e7..284cabed8 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java @@ -3,7 +3,7 @@ package org.hl7.fhir.utilities.npm; public class CommonPackages { public static final String ID_XVER = "hl7.fhir.xver-extensions"; - public static final String VER_XVER = "0.0.7"; + public static final String VER_XVER = "0.0.8"; public static final String ID_PUBPACK = "hl7.fhir.pubpack"; public static final String VER_PUBPACK = "0.0.9"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java index 3eaecbfb6..3d4f7796a 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/FilesystemPackageCacheManager.java @@ -83,8 +83,6 @@ import java.util.Map.Entry; */ public class FilesystemPackageCacheManager extends BasePackageCacheManager implements IPackageCacheManager { - public static final String PRIMARY_SERVER = "http://packages.fhir.org"; - public static final String SECONDARY_SERVER = "https://packages2.fhir.org/packages"; // private static final String SECONDARY_SERVER = "http://local.fhir.org:960/packages"; public static final String PACKAGE_REGEX = "^[a-zA-Z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+$"; public static final String PACKAGE_VERSION_REGEX = "^[A-Za-z][A-Za-z0-9\\_\\-]*(\\.[A-Za-z0-9\\_\\-]+)+\\#[A-Za-z0-9\\-\\_\\$]+(\\.[A-Za-z0-9\\-\\_\\$]+)*$"; @@ -102,8 +100,8 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple * Constructor */ public FilesystemPackageCacheManager(boolean userMode, int toolsVersion) throws IOException { - addPackageServer(PRIMARY_SERVER); - addPackageServer(SECONDARY_SERVER); + addPackageServer(PackageClient.PRIMARY_SERVER); + addPackageServer(PackageClient.SECONDARY_SERVER); if (userMode) cacheFolder = Utilities.path(System.getProperty("user.home"), ".fhir", "packages"); @@ -226,7 +224,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple public String getLatestVersion(String id) throws IOException { for (String nextPackageServer : getPackageServers()) { // special case: - if (!(CommonPackages.ID_PUBPACK.equals(id) && PRIMARY_SERVER.equals(nextPackageServer))) { + if (!(CommonPackages.ID_PUBPACK.equals(id) && PackageClient.PRIMARY_SERVER.equals(nextPackageServer))) { CachingPackageClient pc = new CachingPackageClient(nextPackageServer); try { return pc.getLatestVersion(id); @@ -540,7 +538,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple InputStream stream = fetchFromUrlSpecific(Utilities.pathURL("http://build.fhir.org", id + ".tgz"), false); return new InputStreamWithSrc(stream, Utilities.pathURL("http://build.fhir.org", id + ".tgz"), "current"); } else { - throw new FHIRException("The package '" + id + "' has no entry on the current build server"); + throw new FHIRException("The package '" + id + "' has no entry on the current build server ("+ciList.toString()+")"); } } @@ -653,6 +651,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple http.trustAllhosts(); HTTPResult res = http.get("https://build.fhir.org/ig/qas.json?nocache=" + System.currentTimeMillis()); res.checkThrowException(); + buildInfo = (JsonArray) new com.google.gson.JsonParser().parse(TextFile.bytesToString(res.getContent())); List builds = new ArrayList<>(); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java index cf97aae8f..677cb6ca9 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/PackageClient.java @@ -25,6 +25,9 @@ import java.util.Set; public class PackageClient { + public static final String PRIMARY_SERVER = "http://packages.fhir.org"; + public static final String SECONDARY_SERVER = "https://packages2.fhir.org/packages"; + private String address; private String cacheFolder; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java index 58131279a..51e5abf8f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/XhtmlNode.java @@ -755,7 +755,8 @@ public class XhtmlNode implements IBaseXhtml { public XhtmlNode nbsp() { - return addText(NBSP); + addText(NBSP); + return this; } diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 017cacba5..bf567fc41 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -120,6 +120,7 @@ Reference_REF_NoType = Unable to determine type of target resource Reference_REF_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0} Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1} Reference_REF_WrongTarget = The type ''{0}'' is not a valid Target for this element (must be one of {1}) +REFERENCE_REF_WRONGTARGET_LOAD = The type ''{2}'' is not a valid Target for the element {0} (must be {1}) Resource_RES_ID_Malformed = Invalid Resource id Resource_RES_ID_Missing = Resource requires an id, but none is present Resource_RES_ID_Prohibited = Resource has an id, but none is allowed @@ -132,10 +133,10 @@ Terminology_TX_Binding_NoSource = Binding for path {0} has no source, so can''t Terminology_TX_Binding_NoSource2 = Binding has no source, so can''t be checked Terminology_TX_Code_NotValid = Code {0} is not a valid code in code system {1} Terminology_TX_Code_Unknown = Unknown Code ({0}#{1}) -Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} ({1}) +Terminology_TX_Code_ValueSet = No code provided, and a code is required from the value set {0} Terminology_TX_Code_ValueSet_MISSING = No code provided, and a code is required from the value set Terminology_TX_Code_ValueSetMax = No code provided, and a code must be provided from the value set {0} (max value set {1}) -Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} ({1}) +Terminology_TX_Code_ValueSet_Ext = No code provided, and a code should be provided from the value set {0} Terminology_TX_Coding_Count = Expected {0} but found {1} coding elements Terminology_TX_Confirm_1_CC = Could not confirm that the codings provided are in the value set {0} and a coding from this value set is required (class = {1}) Terminology_TX_Confirm_2_CC = Could not confirm that the codings provided are in the value set {0} and a coding should come from this value set unless it has no suitable code (the validator cannot judge what is suitable) (class = {1}) @@ -149,24 +150,24 @@ Terminology_TX_Error_CodeableConcept = Error {0} validating CodeableConcept Terminology_TX_Error_CodeableConcept_Max = Error {0} validating CodeableConcept using maxValueSet Terminology_TX_Error_Coding1 = Error {0} validating Coding Terminology_TX_Error_Coding2 = Error {0} validating Coding: {1} -Terminology_TX_NoValid_1_CC = None of the codings provided are in the value set {0} ({1}), and a coding from this value set is required) (codes = {2}) -Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0} ({1}), and a code from this value set is required) (code = {2}#{3}) -Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0} ({1}{2}) +Terminology_TX_NoValid_1_CC = None of the codings provided are in the value set {0}, and a coding from this value set is required) (codes = {1}) +Terminology_TX_NoValid_10 = The code provided is not in the maximum value set {0}, and a code from this value set is required) (code = {1}#{2}) +Terminology_TX_NoValid_11 = The code provided is not in the maximum value set {0}, and a code from this value set is required) (code = {1}#{2}), error = {3}) Terminology_TX_NoValid_12 = The Coding provided ({2}) is not in the value set {0}, and a code is required from this value set. {1} Terminology_TX_NoValid_13 = The Coding provided ({2}) is not in the value set {0}, and a code should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable). {1} Terminology_TX_NoValid_14 = The Coding provided ({2}) is not in the value set {0}, and a code is recommended to come from this value set. {1} Terminology_TX_NoValid_15 = The value provided (''{0}'') could not be validated in the absence of a terminology server -Terminology_TX_NoValid_16 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code is required from this value set){3} -Terminology_TX_NoValid_17 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) {3} -Terminology_TX_NoValid_18 = The value provided (''{0}'') is not in the value set {1} ({2}), and a code is recommended to come from this value set){3} -Terminology_TX_NoValid_2_CC = None of the codings provided are in the value set {0} ({1}), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = {2}) -Terminology_TX_NoValid_3_CC = None of the codings provided are in the value set {0} ({1}), and a coding is recommended to come from this value set) (codes = {2}) +Terminology_TX_NoValid_16 = The value provided (''{0}'') is not in the value set {1}, and a code is required from this value set){2} +Terminology_TX_NoValid_17 = The value provided (''{0}'') is not in the value set {1}, and a code should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) {2} +Terminology_TX_NoValid_18 = The value provided (''{0}'') is not in the value set {1}, and a code is recommended to come from this value set){2} +Terminology_TX_NoValid_2_CC = None of the codings provided are in the value set {0}, and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = {1}) +Terminology_TX_NoValid_3_CC = None of the codings provided are in the value set {0}, and a coding is recommended to come from this value set) (codes = {1}) Terminology_TX_NoValid_4 = The Coding provided ({2}) is not in the value set {0}, and a code is required from this value set {1} Terminology_TX_NoValid_5 = The Coding provided ({2}) is not in the value set {0}, and a code should come from this value set unless it has no suitable code (the validator cannot judge what is suitable) {1} Terminology_TX_NoValid_6 = The Coding provided ({2}) is not in the value set {0}, and a code is recommended to come from this value set {1} -Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0} ({1}), (error = {2}) -Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0} ({1}), and a code from this value set is required) (codes = {2}) -Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0} ({1}), (error = {2}) +Terminology_TX_NoValid_7 = None of the codes provided could be validated against the maximum value set {0}, (error = {2}) +Terminology_TX_NoValid_8 = None of the codes provided are in the maximum value set {0}, and a code from this value set is required) (codes = {1}) +Terminology_TX_NoValid_9 = The code provided could not be validated against the maximum value set {0}, (error = {1}) Terminology_TX_System_Invalid = Invalid System URI: {0} Terminology_TX_System_NotKnown = Code System URI ''{0}'' is unknown so the code cannot be validated Terminology_TX_System_Relative = Coding.system must be an absolute reference, not a local reference @@ -174,7 +175,7 @@ Terminology_TX_System_Unknown = Unknown Code System ''{0}'' Terminology_TX_System_ValueSet = Invalid System URI: {0} - cannot use a value set URI as a system Terminology_TX_System_ValueSet2 = The Coding references a value set, not a code system (''{0}'') Terminology_TX_ValueSet_NotFound = ValueSet {0} not found by validator -Terminology_TX_ValueSet_NotFound2 = ValueSet {0} not found by validator +Terminology_TX_ValueSet_NotFound_CS = Found a reference to a CodeSystem ({0}) where a ValueSet belongs Type_Specific_Checks_DT_Base64_Valid = The value ''{0}'' is not a valid Base64 value Type_Specific_Checks_DT_Boolean_Value = boolean values must be ''true'' or ''false'' Type_Specific_Checks_DT_Code_WS = The code ''{0}'' is not valid (whitespace rules) @@ -185,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}) @@ -570,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} @@ -642,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 @@ -662,4 +666,39 @@ INV_FAILED = Rule {0} Failed PATTERN_CHECK_STRING = The pattern [{0}] defined in the profile {1} not found. Issues: {2} TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE = Example URLs are not allowed in this context ({0}) UNICODE_BIDI_CONTROLS_CHARS_DISALLOWED = The Unicode sequence has bi-di control characters which are not allowed in this context: {1} -UNICODE_BIDI_CONTROLS_CHARS_MATCH = The Unicode sequence has unterminated bi-di control characters (see CVE-2021-42574): {1} +UNICODE_BIDI_CONTROLS_CHARS_MATCH = The Unicode sequence has unterminated bi-di control characters (see CVE-2021-42574): {1} +CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHALL = HL7 Defined CodeSystems SHALL have a stated value for the {0} element so that users know the status and meaning of the code system clearly +CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHOULD = HL7 Defined CodeSystems SHOULD have a stated value for the {0} element so that users know the status and meaning of the code system clearly +CODESYSTEM_CS_NONHL7_MISSING_ELEMENT = CodeSystems SHOULD have a stated value for the {0} element so that users know the status and meaning of the code system clearly +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.utilities/src/main/resources/Messages_de.properties b/org.hl7.fhir.utilities/src/main/resources/Messages_de.properties index ec09a482c..16c0cc1fe 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages_de.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages_de.properties @@ -143,24 +143,24 @@ Terminology_TX_Error_CodeableConcept=Fehler {0} bei der Validierung des Codeable Terminology_TX_Error_CodeableConcept_Max=Fehler {0} bei der Validierung des CodeableConcepts mit maxValueSet Terminology_TX_Error_Coding1=Fehler {0} bei der Validierung des Coding Terminology_TX_Error_Coding2=Fehler {0} bei der Validierung des Coding: {1} -Terminology_TX_NoValid_1_CC=Keiner der bereitgestellten Codes ist im ValueSet {0} ({1}, und ein Code aus diesem ValueSet ist erforderlich) (Codes = {2}) -Terminology_TX_NoValid_10=Der bereitgestellte Code ist nicht im maximum ValueSet {0} ({1}, und ein Code aus diesem ValueSet ist erforderlich) (Code = {2}#{3}) -Terminology_TX_NoValid_11=Der bereitgestellte Code ist nicht im maximum value set {0} ({1}{2} +Terminology_TX_NoValid_1_CC=Keiner der bereitgestellten Codes ist im ValueSet {0}, und ein Code aus diesem ValueSet ist erforderlich) (Codes = {1}) +Terminology_TX_NoValid_10=Der bereitgestellte Code ist nicht im maximum ValueSet {0}, und ein Code aus diesem ValueSet ist erforderlich) (Code = {1}#{2}) +Terminology_TX_NoValid_11=Der bereitgestellte Code ist nicht im maximum value set {0}, und ein Code aus diesem ValueSet ist erforderlich) (Code = {1}#{2}, Fehler = {3})) Terminology_TX_NoValid_12=Die angegebene Codierung ist nicht im ValueSet {0} enthalten, und es wird ein Code aus diesem ValueSet ben\u00f6tigt. {1} Terminology_TX_NoValid_13=Die bereitgestellte Codierung ist nicht im ValueSet {0} enthalten, und ein Code sollte aus diesem ValueSet stammen, es sei denn, er hat enth\u00e4lt geeigneten Code. {1} Terminology_TX_NoValid_14=Die angegebene Codierung ist nicht im ValueSet {0} enthalten, und es wird empfohlen, einen Code aus diesem ValueSet zu verwenden. {1} Terminology_TX_NoValid_15=Der angegebene Wert ("{0}") konnte in Ermangelung eines Terminologieservers nicht validiert werden. -Terminology_TX_NoValid_16=Der angegebene Wert ("{0}") ist nicht im ValueSet {1} ({2}, und ein Code aus diesem Valueset ist erforderlich){3} -Terminology_TX_NoValid_17=Der angegebene Wert ("{0}") ist nicht im Valueset {1} ({2}, und ein Code sollte aus diesem Valueset stammen, es sei denn, er hat enth\u00e4lt geeigneten Code){3} -Terminology_TX_NoValid_18=Der angegebene Wert ("{0}") ist nicht im Valueset {1} ({2}, und es wird empfohlen, einen Code aus diesem Valueset zu verwenden){3} -Terminology_TX_NoValid_2_CC=Keiner der angegebenen Codes ist im Valueset {0} ({1}, und ein Code sollte aus diesem Valueset stammen, es sei denn, er enth\u00e4lt keinen geeigneten Code) (Codes = {2}) -Terminology_TX_NoValid_3_CC=Keiner der angegebenen Codes ist im Valueset {0} ({1}, und es wird empfohlen, einen Code aus dieserm Valueset zu verwenden) (Codes = {2}) +Terminology_TX_NoValid_16=Der angegebene Wert ("{0}") ist nicht im ValueSet {1}, und ein Code aus diesem Valueset ist erforderlich){2} +Terminology_TX_NoValid_17=Der angegebene Wert ("{0}") ist nicht im Valueset {1}, und ein Code sollte aus diesem Valueset stammen, es sei denn, er hat enth\u00e4lt geeigneten Code){2} +Terminology_TX_NoValid_18=Der angegebene Wert ("{0}") ist nicht im Valueset {1}, und es wird empfohlen, einen Code aus diesem Valueset zu verwenden){2} +Terminology_TX_NoValid_2_CC=Keiner der angegebenen Codes ist im Valueset {0}, und ein Code sollte aus diesem Valueset stammen, es sei denn, er enth\u00e4lt keinen geeigneten Code) (Codes = {1}) +Terminology_TX_NoValid_3_CC=Keiner der angegebenen Codes ist im Valueset {0}, und es wird empfohlen, einen Code aus dieserm Valueset zu verwenden) (Codes = {1}) Terminology_TX_NoValid_4=Die bereitgestellte Codierung ist nicht im Valueset {0}, und es wird ein Code aus diesem Valueset ben\u00f6tigt{1} Terminology_TX_NoValid_5=Die angegebene Codierung ist nicht im Valueset {0}, und ein Code sollte aus diesem Valueset stammen, es sei denn, er enth\u00e4lt keinen geeigneten Code{1} Terminology_TX_NoValid_6=Die bereitgestellte Codierung ist nicht im Valueset {0} enthalten, und es wird empfohlen, einen Code aus diesem Valueset zu verwenden{1} -Terminology_TX_NoValid_7=Keiner der bereitgestellten Codes konnte gegen das maximum value set {0} ({1}) validiert werden, (Fehler = {2}) -Terminology_TX_NoValid_8=Keiner der bereitgestellten Codes befindet sich im maximum value sete {0} ({1}, und ein Code aus diesem Valueset ist erforderlich) (Codes = {2}) -Terminology_TX_NoValid_9=Der bereitgestellte Code konnte nicht gegen das the maximum value set {0} ({1}) validiert werden, (Fehler = {2}) +Terminology_TX_NoValid_7=Keiner der bereitgestellten Codes konnte gegen das maximum value set {0} validiert werden, (Fehler = {1}) +Terminology_TX_NoValid_8=Keiner der bereitgestellten Codes befindet sich im maximum value sete {0}, und ein Code aus diesem Valueset ist erforderlich) (Codes = {1}) +Terminology_TX_NoValid_9=Der bereitgestellte Code konnte nicht gegen das the maximum value set {0} validiert werden, (Fehler = {1}) Terminology_TX_System_Invalid=Ung\u00fcltige System URI: {0} Terminology_TX_System_NotKnown=Code System URI "{0}" ist unbekannt, so dass der Code nicht validiert werden kann Terminology_TX_System_Relative=Coding.system muss eine absolute Referenz sein, nicht eine lokale Referenz diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties b/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties index 301e92ded..e720a5f74 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages_nl.properties @@ -130,10 +130,10 @@ Terminology_TX_Binding_NoSource = Binding voor pad {0} heeft geen bron, dus kan Terminology_TX_Binding_NoSource2 = Binding heeft geen bron, dus kan niet worden gevalideerd Terminology_TX_Code_NotValid = Code {0} is geen geldige code in het codesysteem {1} Terminology_TX_Code_Unknown = Onbekende code ({0}#{1}) -Terminology_TX_Code_ValueSet = Geen code aanwezig en een code is verplicht uit waardelijst {0} ({1}) is verplicht +Terminology_TX_Code_ValueSet = Geen code aanwezig en een code is verplicht uit waardelijst {0} is verplicht Terminology_TX_Code_ValueSet_MISSING = Geen code aanwezig en een code is verplicht uit de waardelijst Terminology_TX_Code_ValueSetMax = Geen code aanwezig en een code is verplicht uit de waardelijst {0} (max waardelijst {1}) -Terminology_TX_Code_ValueSet_Ext = Geen code aanwezig en een code is verplicht uit waardelijst {0} ({1}) +Terminology_TX_Code_ValueSet_Ext = Geen code aanwezig en een code is verplicht uit waardelijst {0} Terminology_TX_Coding_Count = Verwacht {0}, maar gevonden {1} coding elementen Terminology_TX_Confirm_1_CC = Kan niet bevestigen dat de gevonden codings bestaan in de waardelijst {0} en een coding uit deze waardelijst is verplicht (class = {1}) Terminology_TX_Confirm_2_CC = Kan niet bevestigen dat de gevonden codings bestaan in de waardelijst {0} en een coding uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) (class = {1}) @@ -147,24 +147,24 @@ Terminology_TX_Error_CodeableConcept = Fout {0} bij valideren CodeableConcept Terminology_TX_Error_CodeableConcept_Max = Fout {0} bij valideren CodeableConcept met maxValueSet Terminology_TX_Error_Coding1 = Fout {0} bij valideren Coding Terminology_TX_Error_Coding2 = Fout {0} bij valideren Coding: {1} -Terminology_TX_NoValid_1_CC = Geen van de gevonden codings bestaan in waardelijst {0} ({1}) en een coding uit deze waardelijst is verplicht (codes = {2}) -Terminology_TX_NoValid_10 = De gevonden code bestaat niet in de maximum waardelijst {0} ({1}) en een code uit deze waardelijst is verplicht (code = {2}#{3}) -Terminology_TX_NoValid_11 = De gevonden code bestaat niet in de maximum waardelijst {0} ({1}{2}) +Terminology_TX_NoValid_1_CC = Geen van de gevonden codings bestaan in waardelijst {0} en een coding uit deze waardelijst is verplicht (codes = {1}) +Terminology_TX_NoValid_10 = De gevonden code bestaat niet in de maximum waardelijst {0} en een code uit deze waardelijst is verplicht (code = {1}#{2}) +Terminology_TX_NoValid_11 = De gevonden code bestaat niet in de maximum waardelijst {0} en een code uit deze waardelijst is verplicht (code = {1}#{2}, fout = {3}) Terminology_TX_NoValid_12 = De gevonden Coding ({2}) bestaat niet in de waardelijst {0} en een code uit deze waardelijst is verplicht. {1} Terminology_TX_NoValid_13 = De gevonden Coding ({2}) bestaat niet in de waardelijst {0} en een code uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) ({1}) Terminology_TX_NoValid_14 = De gevonden Coding ({2}) bestaat niet in de waardelijst {0} en een code uit deze waardelijst is aanbevolen. {1} Terminology_TX_NoValid_15 = De gevonden waarde (''{0}'') kan niet worden gevalideerd zonder een terminologieserver -Terminology_TX_NoValid_16 = De gevonden waarde (''{0}'') bestaat niet in de waardelijst {1} ({2}) en een code uit deze waardelijst is verplicht {3} -Terminology_TX_NoValid_17 = De gevonden waarde (''{0}'') bestaat niet in de waardelijst {1} ({2}) en een code uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) {3} -Terminology_TX_NoValid_18 = De gevonden waarde (''{0}'') bestaat niet in de waardelijst {1} ({2}) en een code uit deze waardelijst is aanbevolen. {3} -Terminology_TX_NoValid_2_CC = Geen van de gevonden codings bestaat in de waardelijst {0} ({1}) en een coding uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) (codes = {2}) -Terminology_TX_NoValid_3_CC = Geen van de gevonden codings bestaat in de waardelijst {0} ({1}) en een coding uit deze waardelijst is aanbevolen (codes = {2}) +Terminology_TX_NoValid_16 = De gevonden waarde (''{0}'') bestaat niet in de waardelijst {1} en een code uit deze waardelijst is verplicht {2} +Terminology_TX_NoValid_17 = De gevonden waarde (''{0}'') bestaat niet in de waardelijst {1} en een code uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) {2} +Terminology_TX_NoValid_18 = De gevonden waarde (''{0}'') bestaat niet in de waardelijst {1} en een code uit deze waardelijst is aanbevolen. {2} +Terminology_TX_NoValid_2_CC = Geen van de gevonden codings bestaat in de waardelijst {0} en een coding uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) (codes = {1}) +Terminology_TX_NoValid_3_CC = Geen van de gevonden codings bestaat in de waardelijst {0} en een coding uit deze waardelijst is aanbevolen (codes = {1}) Terminology_TX_NoValid_4 = De gevonden Coding ({2}) bestaat niet in de waardelijst {0} en een code uit deze waardelijst is verplicht {1} Terminology_TX_NoValid_5 = De gevonden Coding ({2}) bestaat niet in de waardelijst {0} en een code uit deze waardelijst is verplicht tenzij deze geen relevante code bevat (de validator kan niet bepalen wat relevant is) {1} Terminology_TX_NoValid_6 = De gevonden Coding ({2}) bestaat niet in de waardelijst {0} en een code uit deze waardelijst is aanbevolen {1} -Terminology_TX_NoValid_7 = Geen van de gevonden codes kon worden gevalideerd tegen de maximum waardelijst {0} ({1}), (fout = {2}) -Terminology_TX_NoValid_8 = Geen van de gevonden codes bestaan in de maximum waardelijst {0} ({1}) en een code uit deze waardelijst is verplicht (codes = {2}) -Terminology_TX_NoValid_9 = De gevonden code kon niet worden gevalideerd tegen de maximum waardelijst {0} ({1}), (fout = {2}) +Terminology_TX_NoValid_7 = Geen van de gevonden codes kon worden gevalideerd tegen de maximum waardelijst {0}, (fout = {2}) +Terminology_TX_NoValid_8 = Geen van de gevonden codes bestaan in de maximum waardelijst {0} en een code uit deze waardelijst is verplicht (codes = {1}) +Terminology_TX_NoValid_9 = De gevonden code kon niet worden gevalideerd tegen de maximum waardelijst {0}, (fout = {1}) Terminology_TX_System_Invalid = Ongeldige system URI: {0} Terminology_TX_System_NotKnown = CodeSystem URI ''{0}'' is onbekend dus de code kan niet worden gevalideerd Terminology_TX_System_Relative = Coding.system moet een absolute referentie zijn, geen lokale referentie diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java index f31a349b4..f941577f6 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; +import java.nio.file.Paths; +import java.text.MessageFormat; import java.util.Random; import static org.junit.jupiter.api.Assertions.*; @@ -20,17 +22,20 @@ class UtilitiesTest { public static final String TEST_TXT = "test.txt"; + private static final String getNormalizedJavaHomeDir() { + return System.getenv("JAVA_HOME") == null ? null : Paths.get(System.getenv("JAVA_HOME")).normalize().toString(); + } + public static final String LINUX_TEMP_DIR = "/tmp/"; public static final String LINUX_USER_DIR = System.getProperty("user.home") + "/"; - public static final String LINUX_JAVA_HOME = System.getenv("JAVA_HOME") + "/"; + public static final String LINUX_JAVA_HOME = getNormalizedJavaHomeDir() + "/"; public static final String WIN_TEMP_DIR = "c:\\temp\\"; public static final String WIN_USER_DIR = System.getProperty("user.home") + "\\"; - public static final String WIN_JAVA_HOME = System.getenv("JAVA_HOME") + "\\"; + public static final String WIN_JAVA_HOME = getNormalizedJavaHomeDir() + "\\"; public static final String OSX_USER_DIR = System.getProperty("user.home") + "/"; - public static final String OSX_JAVA_HOME = System.getenv("JAVA_HOME"); - + public static final String OSX_JAVA_HOME = getNormalizedJavaHomeDir() + "/"; @Test @DisplayName("Test Utilities.path maps temp directory correctly") public void testTempDirPath() throws IOException { @@ -125,27 +130,34 @@ class UtilitiesTest { public static final int BIG_NEG = Utilities.ONE_MB * -1; + + private static final String buildMeasureLimitMessage(int size, String contains) { + return MessageFormat.format("\"{0}\" should contain \"{1}\"", size, contains); + } + + //TODO we've witnessed at least one intermittent failure of this test. It could be refactored to run several times to + // trigger edge cases more often now that it provides better feedback. @Test @DisplayName("Test size bounds on file size utility.") void describeSizeTest() { Assertions.assertAll("GB Measure Limits", - () -> assertTrue(Utilities.describeSize(GB_MEASURE_JUST_OVER).contains(Utilities.GB)), - () -> assertTrue(Utilities.describeSize(GB_MEASURE_EXACT).contains(Utilities.MB)), - () -> assertTrue(Utilities.describeSize(GB_MEASURE_JUST_UNDER).contains(Utilities.MB)) + () -> assertTrue(Utilities.describeSize(GB_MEASURE_JUST_OVER).contains(Utilities.GB), buildMeasureLimitMessage(GB_MEASURE_JUST_OVER, Utilities.GB)), + () -> assertTrue(Utilities.describeSize(GB_MEASURE_EXACT).contains(Utilities.MB), buildMeasureLimitMessage(GB_MEASURE_EXACT, Utilities.MB)), + () -> assertTrue(Utilities.describeSize(GB_MEASURE_JUST_UNDER).contains(Utilities.MB), buildMeasureLimitMessage(GB_MEASURE_JUST_UNDER, Utilities.MB)) ); Assertions.assertAll("MB Measure Limits", - () -> assertTrue(Utilities.describeSize(MB_MEASURE_JUST_OVER).contains(Utilities.MB)), - () -> assertTrue(Utilities.describeSize(MB_MEASURE_EXACT).contains(Utilities.KB)), - () -> assertTrue(Utilities.describeSize(MB_MEASURE_JUST_UNDER).contains(Utilities.KB)) + () -> assertTrue(Utilities.describeSize(MB_MEASURE_JUST_OVER).contains(Utilities.MB), buildMeasureLimitMessage(MB_MEASURE_JUST_OVER, Utilities.MB)), + () -> assertTrue(Utilities.describeSize(MB_MEASURE_EXACT).contains(Utilities.KB), buildMeasureLimitMessage(MB_MEASURE_EXACT, Utilities.KB)), + () -> assertTrue(Utilities.describeSize(MB_MEASURE_JUST_UNDER).contains(Utilities.KB), buildMeasureLimitMessage(MB_MEASURE_JUST_UNDER, Utilities.KB)) ); Assertions.assertAll("KB Measure Limits", - () -> assertTrue(Utilities.describeSize(KB_MEASURE_JUST_OVER).contains(Utilities.KB)), - () -> assertTrue(Utilities.describeSize(KB_MEASURE_EXACT).contains(Utilities.BT)), - () -> assertTrue(Utilities.describeSize(KB_MEASURE_JUST_UNDER).contains(Utilities.BT)) + () -> assertTrue(Utilities.describeSize(KB_MEASURE_JUST_OVER).contains(Utilities.KB), buildMeasureLimitMessage(KB_MEASURE_JUST_OVER, Utilities.KB)), + () -> assertTrue(Utilities.describeSize(KB_MEASURE_EXACT).contains(Utilities.BT), buildMeasureLimitMessage(KB_MEASURE_EXACT, Utilities.BT)), + () -> assertTrue(Utilities.describeSize(KB_MEASURE_JUST_UNDER).contains(Utilities.BT), buildMeasureLimitMessage(KB_MEASURE_JUST_UNDER, Utilities.BT)) ); Assertions.assertAll("BT Measure Limits", - () -> assertTrue(Utilities.describeSize(BT_MEASURE).contains(Utilities.BT)), - () -> assertTrue(Utilities.describeSize(EMPTY).contains(Utilities.BT)) + () -> assertTrue(Utilities.describeSize(BT_MEASURE).contains(Utilities.BT), buildMeasureLimitMessage(BT_MEASURE, Utilities.BT)), + () -> assertTrue(Utilities.describeSize(EMPTY).contains(Utilities.BT), buildMeasureLimitMessage(EMPTY, Utilities.BT)) ); Assertions.assertThrows(IllegalArgumentException.class, () -> Utilities.describeSize(BIG_NEG)); } diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 6e7d2c6dc..9072fa761 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.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml diff --git a/org.hl7.fhir.validation.cli/src/main/resources/logback.xml b/org.hl7.fhir.validation.cli/src/main/resources/logback.xml index 1c8de9da3..7025a2b0d 100644 --- a/org.hl7.fhir.validation.cli/src/main/resources/logback.xml +++ b/org.hl7.fhir.validation.cli/src/main/resources/logback.xml @@ -13,4 +13,6 @@ + + \ No newline at end of file diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index ceebca50f..de9af5dd9 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir org.hl7.fhir.core - 5.5.12-SNAPSHOT + 5.6.16-SNAPSHOT ../pom.xml @@ -155,7 +155,7 @@ org.projectlombok lombok - 1.18.16 + ${lombok_version} provided @@ -235,6 +235,13 @@ true + + org.sonatype.plugins + nexus-staging-maven-plugin + + 15 + + 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 d33363999..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 @@ -1,97 +1,68 @@ package org.hl7.fhir.validation; -import static org.apache.commons.lang3.StringUtils.isBlank; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - */ - - /* -Copyright (c) 2011+, HL7, Inc -All rights reserved. + Copyright (c) 2011+, HL7, Inc. + All rights reserved. -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. */ -import java.util.List; -import java.util.Map; import org.apache.commons.lang3.StringUtils; +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; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.elementmodel.Element; -import org.hl7.fhir.r5.model.Base; -import org.hl7.fhir.r5.model.DomainResource; -import org.hl7.fhir.r5.model.Resource; -import org.hl7.fhir.r5.model.StructureDefinition; -import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.elementmodel.JsonParser; +import org.hl7.fhir.r5.formats.IParser.OutputStyle; +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; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; -import org.hl7.fhir.validation.BaseValidator.TrackedLocationRelatedMessage; import org.hl7.fhir.validation.instance.utils.IndexedElement; -public class BaseValidator { +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.apache.commons.lang3.StringUtils.isBlank; + +public class BaseValidator implements IValidationContextResourceLoader { public class TrackedLocationRelatedMessage { private Object location; @@ -107,8 +78,7 @@ public class BaseValidator { public ValidationMessage getVmsg() { return vmsg; } - - } + } public class ValidationControl { private boolean allowed; @@ -1027,4 +997,61 @@ public class BaseValidator { return system; } } + + @Override + public Resource loadContainedResource(List errors, String path, Element resource, String id, Class class1) throws FHIRException { + for (Element contained : resource.getChildren("contained")) { + if (contained.getIdBase().equals(id)) { + return loadFoundResource(errors, path, contained, class1); + } + } + return null; + } + + protected Resource loadFoundResource(List errors, String path, Element resource, Class class1) throws FHIRException { + try { + FhirPublication v = FhirPublication.fromCode(context.getVersion()); + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + new JsonParser(context).compose(resource, bs, OutputStyle.NORMAL, resource.getIdBase()); + byte[] json = bs.toByteArray(); + Resource r5 = null; + switch (v) { + case DSTU1: + rule(errors, IssueType.INVALID, resource.line(), resource.col(), path, false, I18nConstants.UNSUPPORTED_VERSION_R1, resource.getIdBase()); + return null; // this can't happen + case DSTU2: + org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); + r5 = VersionConvertorFactory_10_50.convertResource(r2); + break; + case DSTU2016May: + org.hl7.fhir.dstu2016may.model.Resource r2a = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(json); + r5 = VersionConvertorFactory_14_50.convertResource(r2a); + break; + case STU3: + org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(json); + r5 = VersionConvertorFactory_30_50.convertResource(r3); + break; + case R4: + org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(json); + r5 = VersionConvertorFactory_40_50.convertResource(r4); + break; + case R5: + r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(json); + break; + default: + return null; // this can't happen + } + if (class1.isInstance(r5)) + return (Resource) r5; + else { + rule(errors, IssueType.INVALID, resource.line(), resource.col(), path, false, I18nConstants.REFERENCE_REF_WRONGTARGET_LOAD, resource.getIdBase(), class1.toString(), r5.fhirType()); + return null; + } + + } catch (IOException e) { + throw new FHIRException(e); + } + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/NativeHostServices.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/NativeHostServices.java index e332a9ae8..57107e01d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/NativeHostServices.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/NativeHostServices.java @@ -34,9 +34,6 @@ package org.hl7.fhir.validation; import java.io.ByteArrayOutputStream; import java.io.IOException; -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; 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; @@ -69,9 +66,9 @@ import org.hl7.fhir.r5.model.FhirPublication; import org.hl7.fhir.r5.model.OperationOutcome; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.ValueSet; -import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel; -import org.hl7.fhir.r5.utils.IResourceValidator.CheckDisplayOption; -import org.hl7.fhir.r5.utils.IResourceValidator.IdStatus; +import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; +import org.hl7.fhir.r5.utils.validation.constants.CheckDisplayOption; +import org.hl7.fhir.r5.utils.validation.constants.IdStatus; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ResourceChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ResourceChecker.java index ab0c772f4..0aa551594 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ResourceChecker.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ResourceChecker.java @@ -99,7 +99,7 @@ public class ResourceChecker { String s = new String(cnt, StandardCharsets.UTF_8); if (s.startsWith("shc:/")) s = SHCParser.decodeQRCode(s); - JWT jwt = SHCParser.decodeJWT(s); + JWT jwt = new SHCParser(context).decodeJWT(s); return Manager.FhirFormat.SHC; } catch (Exception e) { if (debug) { 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 033927d7f..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,9 +3,9 @@ package org.hl7.fhir.validation; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; -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; + +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; @@ -35,10 +35,13 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.IResourceValidator.*; +import org.hl7.fhir.r5.utils.validation.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; +import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; +import org.hl7.fhir.r5.utils.validation.constants.*; import org.hl7.fhir.utilities.TimeTracker; import org.hl7.fhir.utilities.*; import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult; @@ -57,7 +60,6 @@ import org.xml.sax.SAXException; import java.io.*; import java.net.URISyntaxException; -import java.net.URL; import java.util.*; /* @@ -131,7 +133,7 @@ POSSIBILITY OF SUCH DAMAGE. * @author Grahame Grieve */ @Accessors(chain = true) -public class ValidationEngine implements IValidatorResourceFetcher, IPackageInstaller { +public class ValidationEngine implements IValidatorResourceFetcher, IValidationPolicyAdvisor, IPackageInstaller { @Getter @Setter private SimpleWorkerContext context; @Getter @Setter private Map binaries = new HashMap<>(); @@ -146,6 +148,7 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst @Getter @Setter private PrintWriter mapLog; @Getter @Setter private boolean debug = false; @Getter @Setter private IValidatorResourceFetcher fetcher; + @Getter @Setter private IValidationPolicyAdvisor policyAdvisor; @Getter @Setter private ICanonicalResourceLocator locator; @Getter @Setter private boolean assumeValidRestReferences; @Getter @Setter private boolean noExtensibleBindingMessages; @@ -227,6 +230,15 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst 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); } @@ -730,22 +742,38 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst } @Override - public ReferenceValidationPolicy validationPolicy(IResourceValidator validator, Object appContext, String path, String url) { + public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { Resource resource = context.fetchResource(StructureDefinition.class, url); if (resource != null) { return ReferenceValidationPolicy.CHECK_VALID; } if (!(url.contains("hl7.org") || url.contains("fhir.org"))) { return ReferenceValidationPolicy.IGNORE; - } else if (fetcher != null) { - return fetcher.validationPolicy(validator, appContext, path, url); + } else if (policyAdvisor != null) { + return policyAdvisor.policyForReference(validator, appContext, path, url); } else { return ReferenceValidationPolicy.CHECK_EXISTS_AND_TYPE; } } @Override - public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException { + public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, + Object appContext, + String containerType, + String containerId, + Element.SpecialElement containingResourceType, + String path, + String url) { + return ContainedReferenceValidationPolicy.CHECK_VALID; + } + + @Override + public CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, StructureDefinition structure, BindingKind kind, ValueSet valueSet, List systems) { + return CodedContentValidationPolicy.VALUESET; + } + + @Override + public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws FHIRException { if (!url.startsWith("http://") && !url.startsWith("https://")) { // ignore these return true; } @@ -804,4 +832,5 @@ public class ValidationEngine implements IValidatorResourceFetcher, IPackageInst public boolean fetchesCanonicalResource(IResourceValidator validator, String url) { return fetcher != null && fetcher.fetchesCanonicalResource(validator, url); } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorUtils.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorUtils.java index b691e3a84..a04a4d54e 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorUtils.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/ValidatorUtils.java @@ -48,7 +48,7 @@ public class ValidatorUtils { if (version.startsWith("3.0")) return new R3ToR5Loader(new String[]{"CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5()); if (version.startsWith("4.0")) - return new R4ToR5Loader(new String[]{"CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5()); + return new R4ToR5Loader(new String[]{"CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5(), version); if (version.startsWith("5.0")) return new R5ToR5Loader(new String[]{"CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5()); return null; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java index f6ae3964d..1ac810f2d 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/model/CliContext.java @@ -7,7 +7,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; -import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.BundleValidationRule; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.cli.utils.EngineMode; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java index 9eba19d20..acb5caf2c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/StandAloneValidatorFetcher.java @@ -7,10 +7,17 @@ import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext.ICanonicalResourceLocator; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.CanonicalResource; +import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.terminologies.TerminologyClient; -import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher; -import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; +import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; +import org.hl7.fhir.r5.utils.validation.constants.BindingKind; +import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy; +import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy; +import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities.VersionURLInfo; @@ -28,7 +35,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, ICanonicalResourceLocator { +public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IValidationPolicyAdvisor, ICanonicalResourceLocator { List mappingsUris = new ArrayList<>(); private FilesystemPackageCacheManager pcm; @@ -51,10 +58,24 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IC } @Override - public ReferenceValidationPolicy validationPolicy(IResourceValidator validator, Object appContext, String path, String url) { + public ReferenceValidationPolicy policyForReference(IResourceValidator validator, + Object appContext, + String path, + String url) { return ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS; } + @Override + public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, + Object appContext, + String containerType, + String containerId, + Element.SpecialElement containingResourceType, + String path, + String url) { + return ContainedReferenceValidationPolicy.CHECK_VALID; + } + @Override public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException { if (!Utilities.isAbsoluteUrl(url)) { @@ -260,4 +281,10 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IC } } + @Override + public CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, + StructureDefinition structure, BindingKind kind, ValueSet valueSet, List systems) { + return CodedContentValidationPolicy.VALUESET; + } + } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java index e17b99afa..b738a825f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/utils/Params.java @@ -1,7 +1,6 @@ package org.hl7.fhir.validation.cli.utils; -import org.apache.http.auth.AUTH; -import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.BundleValidationRule; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.validation.cli.model.CliContext; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/EnableWhenEvaluator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/EnableWhenEvaluator.java index a070630a5..01bccaed5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/EnableWhenEvaluator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/EnableWhenEvaluator.java @@ -48,6 +48,7 @@ import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemComponent; import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemEnableWhenComponent; import org.hl7.fhir.r5.model.Questionnaire.QuestionnaireItemOperator; import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.validation.instance.type.QuestionnaireValidator.QuestionnaireWithContext; import org.hl7.fhir.validation.instance.utils.ValidatorHostContext; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; @@ -85,17 +86,17 @@ public class EnableWhenEvaluator { public static class QStack extends ArrayList { private static final long serialVersionUID = 1L; - private Questionnaire q; + private QuestionnaireWithContext q; private Element a; - public QStack(Questionnaire q, Element a) { + public QStack(QuestionnaireWithContext q, Element a) { super(); this.q = q; this.a = a; } - public Questionnaire getQ() { + public QuestionnaireWithContext getQ() { return q; } @@ -311,9 +312,9 @@ public class EnableWhenEvaluator { * - any targetA in groupA are input for the enableWhen decision */ private List findQuestionAnswers(QStack qstack, QuestionnaireItemComponent sourceQ, QuestionnaireItemEnableWhenComponent ew) { - QuestionnaireItemComponent targetQ = qstack.getQ().getQuestion(ew.getQuestion()); + QuestionnaireItemComponent targetQ = qstack.getQ().q().getQuestion(ew.getQuestion()); if (targetQ != null) { - QuestionnaireItemComponent groupQ = qstack.getQ().getCommonGroup(sourceQ, targetQ); + QuestionnaireItemComponent groupQ = qstack.getQ().q().getCommonGroup(sourceQ, targetQ); if (groupQ == null) { // root is Q itself return findOnItem(qstack.getA(), ew.getQuestion()); } else { 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 b9610ecf3..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; @@ -47,12 +48,11 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; 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; @@ -135,9 +135,10 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorCla import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r5.utils.IResourceValidator; +import org.hl7.fhir.r5.utils.validation.*; import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.r5.utils.validation.constants.*; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.SIDUtilities; import org.hl7.fhir.utilities.UnicodeUtilities; @@ -174,8 +175,6 @@ import org.w3c.dom.Document; import com.google.gson.Gson; import com.google.gson.JsonObject; -import javax.annotation.Nonnull; - /** * Thinking of using this in a java program? Don't! @@ -305,7 +304,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else if (item instanceof Element) { Element e = (Element) item; - if (e.getName().equals("contained")) { + if (e.getSpecial() != null) { self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e, ctxt.getRootResource()), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage)); } else { self.validateResource(new ValidatorHostContext(ctxt.getAppContext(), e), valerrors, e, e, sd, IdStatus.OPTIONAL, new NodeStack(context, null, e, validationLanguage)); @@ -381,6 +380,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private Map fetchCache = new HashMap<>(); private HashMap resourceTracker = new HashMap<>(); private IValidatorResourceFetcher fetcher; + private IValidationPolicyAdvisor policyAdvisor; long time = 0; private IEvaluationContext externalHostServices; private boolean noExtensibleWarnings; @@ -464,6 +464,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return this; } + @Override + public IValidationPolicyAdvisor getPolicyAdvisor() { + return policyAdvisor; + } + + @Override + public IResourceValidator setPolicyAdvisor(IValidationPolicyAdvisor advisor) { + this.policyAdvisor = advisor; + return this; + } + public IValidationProfileUsageTracker getTracker() { return this.tracker; } @@ -1050,17 +1061,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); + } + } else { try { CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); if (!cc.hasCoding()) { if (binding.getStrength() == BindingStrength.REQUIRED) - rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(binding.getValueSet()), valueset.getUrl()); + rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeValueSet(binding.getValueSet())); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(binding.getValueSet()), valueset.getUrl()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeValueSet(binding.getValueSet())); } } else { long t = System.nanoTime(); @@ -1107,15 +1123,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { if (binding.getStrength() == BindingStrength.REQUIRED) { - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeValueSet(binding.getValueSet()), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeValueSet(binding.getValueSet()), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeValueSet(binding.getValueSet()), ccSummary(cc)); } } } @@ -1177,7 +1193,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); + } + } else { try { CodeableConcept cc = convertToCodeableConcept(element, logical); if (!cc.hasCoding()) { @@ -1187,7 +1208,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")), valueset.getUrl()); else - warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(binding.getValueSet()), valueset.getUrl()); + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeValueSet(binding.getValueSet())); } } else { long t = System.nanoTime(); @@ -1229,15 +1250,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeValueSet(binding.getValueSet()), ccSummary(cc)); else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack); if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeValueSet(binding.getValueSet()), ccSummary(cc)); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(binding.getValueSet()), valueset.getUrl(), ccSummary(cc)); + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeValueSet(binding.getValueSet()), ccSummary(cc)); } } } @@ -1302,7 +1323,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); + } + } else { try { long t = System.nanoTime(); ValidationResult vr = null; @@ -1435,16 +1461,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) { ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(maxVSUrl); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(maxVSUrl))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl)); + } + } else { try { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, valueset, cc, false); timeTracker.tx(t, "vc "+cc.toString()); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeValueSet(maxVSUrl), vr.getMessage()); else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl), valueset.getUrl(), ccSummary(cc)); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeValueSet(maxVSUrl), ccSummary(cc)); } } catch (Exception e) { if (STACK_TRACE) e.printStackTrace(); @@ -1453,18 +1484,32 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } + private String describeValueSet(String url) { + ValueSet vs = context.fetchResource(ValueSet.class, url); + if (vs != null) { + return "'"+vs.present()+"' ("+url+")"; + } else { + return "("+url+")"; + } + } + private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) { ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(maxVSUrl); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(maxVSUrl))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl)); + } + } else { try { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, valueset, c, true); timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeValueSet(maxVSUrl), vr.getMessage()); else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl), valueset.getUrl(), c.getSystem(), c.getCode()); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeValueSet(maxVSUrl), c.getSystem(), c.getCode()); } } catch (Exception e) { if (STACK_TRACE) e.printStackTrace(); @@ -1475,16 +1520,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private void checkMaxValueSet(List errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) { ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(maxVSUrl); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(maxVSUrl))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(maxVSUrl)); + } + } else { try { long t = System.nanoTime(); ValidationResult vr = checkCodeOnServer(stack, valueset, value, baseOptions.setLanguage(stack.getWorkingLang())); timeTracker.tx(t, "vc "+value); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl), valueset.getUrl(), vr.getMessage()); + txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeValueSet(maxVSUrl), vr.getMessage()); else - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl), valueset.getUrl(), "), and a code from this value set is required) (code = " + value + "), (error = " + vr.getMessage() + ")"); + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeValueSet(maxVSUrl), vr.getMessage()); } } catch (Exception e) { if (STACK_TRACE) e.printStackTrace(); @@ -1530,7 +1580,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) { if (binding.hasValueSet()) { ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet()))) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); + } + } else { try { Coding c = ObjectConverter.readAsCoding(element); long t = System.nanoTime(); @@ -1612,7 +1667,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private StructureDefinition checkExtension(ValidatorHostContext hostContext, List errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException { String url = element.getNamedChildValue("url"); boolean isModifier = element.getName().equals("modifierExtension"); - + assert def.getIsModifier() == isModifier; + long t = System.nanoTime(); StructureDefinition ex = Utilities.isAbsoluteUrl(url) ? context.fetchResource(StructureDefinition.class, url) : null; timeTracker.sd(t); @@ -1632,10 +1688,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (ex != null) { trackUsage(ex, hostContext, element); - if (def.getIsModifier()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY); + // check internal definitions are coherent + if (isModifier) { + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY); } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHN); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHN); } // two questions // 1. can this extension be used here? @@ -1987,7 +2044,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), fixedSource, "end", focus, pattern); } - private void checkPrimitive(Object appContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { + private void checkPrimitive(ValidatorHostContext hostContext, List errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException { if (isBlank(e.primitiveValue())) { if (e.primitiveValue() == null) rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT); @@ -2057,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, appContext, path, url, type); - } catch (IOException e1) { - found = false; - } - if (!found) { - if (type.equals("canonical")) { - ReferenceValidationPolicy rp = fetcher.validationPolicy(this, appContext, 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 = fetcher.validationPolicy(this, appContext, 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())) { @@ -2159,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); @@ -2193,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; @@ -2221,7 +2250,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (context.hasBinding() && e.primitiveValue() != null) { - checkPrimitiveBinding(errors, path, type, context, e, profile, node); + checkPrimitiveBinding(hostContext, errors, path, type, context, e, profile, node); } if (type.equals("xhtml")) { @@ -2249,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"); @@ -2280,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) { @@ -2353,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))); @@ -2485,7 +2598,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - private void checkPrimitiveBinding(List errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { + private void checkPrimitiveBinding(ValidatorHostContext hostContext, List errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) { // We ignore bindings that aren't on string, uri or code if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) { return; @@ -2500,30 +2613,43 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ElementDefinitionBindingComponent binding = elementContext.getBinding(); if (binding.hasValueSet()) { ValueSet vs = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl()); - if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND2, describeReference(binding.getValueSet()))) { - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - ValidationOptions options = baseOptions.setLanguage(stack.getWorkingLang()).guessSystem(); - vr = checkCodeOnServer(stack, vs, value, options); + if (vs == null) { + CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); + if (rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { + warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, vs != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); } - timeTracker.tx(t, "vc "+value+""); - if (binding.getStrength() == BindingStrength.REQUIRED) { - removeTrackedMessagesForLocation(errors, element, path); - } - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_15, value); - else if (binding.getStrength() == BindingStrength.REQUIRED) - txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) - checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value, stack); - else if (!noExtensibleWarnings && !isOkExtension(value, vs)) - txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet()), vs.getUrl(), getErrorMessage(vr.getMessage())); + } else { + CodedContentValidationPolicy validationPolicy = getPolicyAdvisor() == null ? + CodedContentValidationPolicy.VALUESET : getPolicyAdvisor().policyForCodedContent(this, hostContext, stack.getLiteralPath(), elementContext, profile, BindingKind.PRIMARY, vs, new ArrayList<>()); + + if (validationPolicy != CodedContentValidationPolicy.IGNORE) { + long t = System.nanoTime(); + ValidationResult vr = null; + if (binding.getStrength() != BindingStrength.EXAMPLE) { + ValidationOptions options = baseOptions.setLanguage(stack.getWorkingLang()).guessSystem(); + if (validationPolicy == CodedContentValidationPolicy.CODE) { + options = options.noCheckValueSetMembership(); + } + vr = checkCodeOnServer(stack, vs, value, options); + } + timeTracker.tx(t, "vc "+value+""); + if (binding.getStrength() == BindingStrength.REQUIRED) { + removeTrackedMessagesForLocation(errors, element, path); + } + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_15, value); + else if (binding.getStrength() == BindingStrength.REQUIRED) + txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeValueSet(binding.getValueSet()), getErrorMessage(vr.getMessage())); + else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet")) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value, stack); + else if (!noExtensibleWarnings && !isOkExtension(value, vs)) + txWarningForLaterRemoval(element, errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeValueSet(binding.getValueSet()), getErrorMessage(vr.getMessage())); + } else if (binding.getStrength() == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeValueSet(binding.getValueSet()), getErrorMessage(vr.getMessage())); + } } } } @@ -2547,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; @@ -2555,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) { @@ -2639,14 +2864,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat checkFixedValue(errors, path + ".denominator", focus.getNamedChild("denominator"), fixed.getDenominator(), fixedSource, "denominator", focus, pattern); } - private void checkReference(ValidatorHostContext hostContext, List errors, String path, Element element, StructureDefinition profile, ElementDefinition container, String parentType, NodeStack stack) throws FHIRException { + private void checkReference(ValidatorHostContext hostContext, + List errors, + String path, + Element element, + StructureDefinition profile, + ElementDefinition container, + String parentType, + NodeStack stack) throws FHIRException { Reference reference = ObjectConverter.readAsReference(element); String ref = reference.getReference(); if (Utilities.noString(ref)) { if (!path.contains("element.pattern")) { // this business rule doesn't apply to patterns if (Utilities.noString(reference.getIdentifier().getSystem()) && Utilities.noString(reference.getIdentifier().getValue())) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !Utilities.noString(element.getNamedChildValue("display")), I18nConstants.REFERENCE_REF_NODISPLAY); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, + !Utilities.noString(element.getNamedChildValue("display")), I18nConstants.REFERENCE_REF_NODISPLAY); } } return; @@ -2656,7 +2889,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, !isSuspiciousReference(ref), I18nConstants.REFERENCE_REF_SUSPICIOUS, ref); - ResolvedReference we = localResolve(ref, stack, errors, path, (Element) hostContext.getAppContext(), element); + ResolvedReference we = localResolve(ref, stack, errors, path, hostContext.getRootResource(), element); String refType; if (ref.startsWith("#")) { refType = "contained"; @@ -2667,7 +2900,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat refType = "bundled"; } } - ReferenceValidationPolicy pol = refType.equals("contained") || refType.equals("bundled") ? ReferenceValidationPolicy.CHECK_VALID : fetcher == null ? ReferenceValidationPolicy.IGNORE : fetcher.validationPolicy(this, hostContext.getAppContext(), path, ref); + ReferenceValidationPolicy pol; + if (refType.equals("contained") || refType.equals("bundled")) { + pol = ReferenceValidationPolicy.CHECK_VALID; + } else { + if (policyAdvisor == null) pol = ReferenceValidationPolicy.IGNORE; + else pol = policyAdvisor.policyForReference(this, hostContext.getAppContext(), path, ref); + } if (pol.checkExists()) { if (we == null) { @@ -2694,7 +2933,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } - boolean ok = (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); + boolean ok = (allowExamples && (ref.contains("example.org") || ref.contains("acme.com"))) + || (we != null || pol == ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS); rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, I18nConstants.REFERENCE_REF_CANTRESOLVE, ref); } @@ -2716,22 +2956,26 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat boolean matchingResource = false; for (CanonicalType target : containerType.getTargetProfile()) { StructureDefinition sd = resolveProfile(profile, target.asStringValue()); - if (rule(errors, IssueType.NOTFOUND, element.line(), element.col(), path, sd != null, I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, target.asStringValue())) { + if (rule(errors, IssueType.NOTFOUND, element.line(), element.col(), path, sd != null, + I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, target.asStringValue())) { if (("http://hl7.org/fhir/StructureDefinition/" + sd.getType()).equals(tu)) { matchingResource = true; break; } } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, I18nConstants.REFERENCE_REF_WRONGTARGET, reference.getType(), container.getType("Reference").getTargetProfile()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, matchingResource, + I18nConstants.REFERENCE_REF_WRONGTARGET, reference.getType(), container.getType("Reference").getTargetProfile()); } // the type has to match the actual - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft == null || ft.equals(reference.getType()), I18nConstants.REFERENCE_REF_BADTARGETTYPE, reference.getType(), ft); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, + ft == null || ft.equals(reference.getType()), I18nConstants.REFERENCE_REF_BADTARGETTYPE, reference.getType(), ft); } if (we != null && pol.checkType()) { - if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, I18nConstants.REFERENCE_REF_NOTYPE)) { + if (warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ft != null, + I18nConstants.REFERENCE_REF_NOTYPE)) { // we validate as much as we can. First, can we infer a type from the profile? boolean ok = false; TypeRefComponent type = getReferenceTypeRef(container.getType()); @@ -2740,7 +2984,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List profiles = new ArrayList<>(); for (UriType u : type.getTargetProfile()) { StructureDefinition sd = resolveProfile(profile, u.getValue()); - if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, u.getValue())) { + if (rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, sd != null, + I18nConstants.REFERENCE_REF_CANTRESOLVEPROFILE, u.getValue())) { types.add(sd.getType()); if (ft.equals(sd.getType())) { ok = true; @@ -2749,14 +2994,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } if (!pol.checkValid()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, I18nConstants.REFERENCE_REF_CANTMATCHTYPE, ref, StringUtils.join("; ", type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() > 0, + I18nConstants.REFERENCE_REF_CANTMATCHTYPE, ref, StringUtils.join("; ", type.getTargetProfile())); } else { - Map> badProfiles = new HashMap>(); - Map> goodProfiles = new HashMap>(); + Map> badProfiles = new HashMap<>(); + Map> goodProfiles = new HashMap<>(); int goodCount = 0; for (StructureDefinition pr : profiles) { List profileErrors = new ArrayList(); - validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, IdStatus.OPTIONAL, we.getStack().resetIds()); + validateResource(we.hostContext(hostContext, pr), profileErrors, we.getResource(), we.getFocus(), pr, + IdStatus.OPTIONAL, we.getStack().resetIds()); if (!hasErrors(profileErrors)) { goodCount++; goodProfiles.put(pr, profileErrors); @@ -2776,14 +3023,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (goodProfiles.size() == 0) { if (!isShowMessagesFromReferences()) { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, areAllBaseProfiles(profiles), + I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile())); for (StructureDefinition sd : badProfiles.keySet()) { slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd))); } } else { - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile())); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, profiles.size() == 1, + I18nConstants.REFERENCE_REF_CANTMATCHCHOICE, ref, asList(type.getTargetProfile())); for (List messages : badProfiles.values()) { for (ValidationMessage vm : messages) { if (!errors.contains(vm)) { @@ -2794,13 +3043,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } else { if (!isShowMessagesFromReferences()) { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, + I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet())); for (StructureDefinition sd : badProfiles.keySet()) { - slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), + slicingHint(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, + false, context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, ref, sd.getUrl()), errorSummaryForSlicingAsHtml(badProfiles.get(sd)), errorSummaryForSlicingAsText(badProfiles.get(sd))); } } else { - warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet())); + warning(errors, IssueType.STRUCTURE, element.line(), element.col(), path, false, + I18nConstants.REFERENCE_REF_MULTIPLEMATCHES, ref, asListByUrl(goodProfiles.keySet())); for (List messages : goodProfiles.values()) { for (ValidationMessage vm : messages) { if (!errors.contains(vm)) { @@ -2811,7 +3063,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, I18nConstants.REFERENCE_REF_BADTARGETTYPE, ft, types.toString()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, ok, + I18nConstants.REFERENCE_REF_BADTARGETTYPE, ft, types.toString()); } if (type.hasAggregation() && !noCheckAggregation) { boolean modeOk = false; @@ -2825,7 +3078,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat else if (mode.getValue().equals(AggregationMode.REFERENCED) && (refType.equals("bundled") || refType.equals("remote"))) modeOk = true; } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, I18nConstants.REFERENCE_REF_AGGREGATION, refType, b.toString()); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, modeOk, + I18nConstants.REFERENCE_REF_AGGREGATION, refType, b.toString()); } } } @@ -2854,7 +3108,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat sdF = sdF.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sdF.getBaseDefinition()) : null; } } - rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || ok, I18nConstants.REFERENCE_REF_BADTARGETTYPE2, ft, ref, types); + rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path, types.isEmpty() || ok, + I18nConstants.REFERENCE_REF_BADTARGETTYPE2, ft, ref, types); } if (pol == ReferenceValidationPolicy.CHECK_VALID) { @@ -3322,7 +3577,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return true; } - private ResolvedReference localResolve(String ref, NodeStack stack, List errors, String path, Element hostContext, Element source) { + private ResolvedReference localResolve(String ref, NodeStack stack, List errors, String path, Element rootResource, Element source) { if (ref.startsWith("#")) { // work back through the parent list. // really, there should only be one level for this (contained resources cannot contain @@ -3422,11 +3677,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat stack = stack.getParent(); } // we can get here if we got called via FHIRPath conformsTo which breaks the stack continuity. - if (hostContext != null && BUNDLE.equals(hostContext.fhirType())) { - String type = hostContext.getChildValue(TYPE); - Element entry = getEntryForSource(hostContext, source); + if (rootResource != null && BUNDLE.equals(rootResource.fhirType())) { + String type = rootResource.getChildValue(TYPE); + Element entry = getEntryForSource(rootResource, source); fullUrl = entry.getChildValue(FULL_URL); - IndexedElement res = getFromBundle(hostContext, ref, fullUrl, errors, path, type, "transaction".equals(type)); + IndexedElement res = getFromBundle(rootResource, ref, fullUrl, errors, path, type, "transaction".equals(type)); if (res == null) { return null; } else { @@ -3434,7 +3689,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rr.setResource(res.getMatch()); rr.setFocus(res.getMatch()); rr.setExternal(false); - rr.setStack(new NodeStack(context, null, hostContext, validationLanguage).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), + rr.setStack(new NodeStack(context, null, rootResource, validationLanguage).push(res.getEntry(), res.getIndex(), res.getEntry().getProperty().getDefinition(), res.getEntry().getProperty().getDefinition()).push(res.getMatch(), -1, res.getMatch().getProperty().getDefinition(), res.getMatch().getProperty().getDefinition())); rr.getStack().qualifyPath(".ofType("+rr.getResource().fhirType()+")"); @@ -4298,84 +4553,108 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } - private void validateContains(ValidatorHostContext hostContext, List errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException { + private void validateContains(ValidatorHostContext hostContext, List errors, String path, + ElementDefinition child, ElementDefinition context, Element resource, + Element element, NodeStack stack, IdStatus idstatus, StructureDefinition parentProfile) throws FHIRException { + + SpecialElement special = element.getSpecial(); + + ContainedReferenceValidationPolicy containedValidationPolicy = getPolicyAdvisor() == null ? + ContainedReferenceValidationPolicy.CHECK_VALID : getPolicyAdvisor().policyForContained(this, + hostContext, context.fhirType(), context.getId(), special, path, parentProfile.getUrl()); + + if (containedValidationPolicy.ignore()) { + return; + } + String resourceName = element.getType(); - TypeRefComponent trr = null; + TypeRefComponent typeForResource = null; CommaSeparatedStringBuilder bt = new CommaSeparatedStringBuilder(); - for (TypeRefComponent tr : child.getType()) { - bt.append(tr.getCode()); - if (tr.getCode().equals("Resource") || tr.getCode().equals(resourceName) ) { - trr = tr; + + // Iterate through all possible types + for (TypeRefComponent type : child.getType()) { + bt.append(type.getCode()); + if (type.getCode().equals("Resource") || type.getCode().equals(resourceName) ) { + typeForResource = type; break; } } + stack.qualifyPath(".ofType("+resourceName+")"); - if (trr == null) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName, bt.toString()); - } else if (isValidResourceType(resourceName, trr)) { - // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise - ValidatorHostContext hc = null; - if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER) { - resource = element; - hc = hostContext.forEntry(element); - } else { - hc = hostContext.forContained(element); - } - stack.resetIds(); - if (element.getSpecial() != null) { - switch (element.getSpecial()) { - case BUNDLE_ENTRY: - idstatus = IdStatus.OPTIONAL; - break; - case BUNDLE_OUTCOME: - idstatus = IdStatus.OPTIONAL; - break; - case CONTAINED: - stack.setContained(true); - idstatus = IdStatus.REQUIRED; - break; - case PARAMETER: - idstatus = IdStatus.OPTIONAL; - break; - default: - break; + + if (typeForResource == null) { + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE, resourceName, bt.toString()); + } else if (isValidResourceType(resourceName, typeForResource)) { + if (containedValidationPolicy.checkValid()) { + // special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise + ValidatorHostContext hc = null; + if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.PARAMETER) { + resource = element; + hc = hostContext.forEntry(element); + } else { + hc = hostContext.forContained(element); } - } - if (trr.getProfile().size() == 1) { - long t = System.nanoTime(); - StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, trr.getProfile().get(0).asStringValue()); - timeTracker.sd(t); - trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) { - validateResource(hc, errors, resource, element, profile, idstatus, stack); + + stack.resetIds(); + if (special != null) { + switch (special) { + case BUNDLE_ENTRY: + case BUNDLE_OUTCOME: + case PARAMETER: + idstatus = IdStatus.OPTIONAL; + break; + case CONTAINED: + stack.setContained(true); + idstatus = IdStatus.REQUIRED; + break; + default: + break; + } } - } else if (trr.getProfile().size() == 0) { - long t = System.nanoTime(); - StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); - timeTracker.sd(t); - trackUsage(profile, hostContext, element); - if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) { - validateResource(hc, errors, resource, element, profile, idstatus, stack); + + if (typeForResource.getProfile().size() == 1) { + long t = System.nanoTime(); + StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, typeForResource.getProfile().get(0).asStringValue()); + timeTracker.sd(t); + trackUsage(profile, hostContext, element); + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), + profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) { + validateResource(hc, errors, resource, element, profile, idstatus, stack); + } + } else if (typeForResource.getProfile().isEmpty()) { + long t = System.nanoTime(); + StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, + "http://hl7.org/fhir/StructureDefinition/" + resourceName); + timeTracker.sd(t); + trackUsage(profile, hostContext, element); + if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), + profile != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOPROFILE, resourceName)) { + validateResource(hc, errors, resource, element, profile, idstatus, stack); + } + } else { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (CanonicalType u : typeForResource.getProfile()) { + b.append(u.asStringValue()); + } + rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES, typeForResource.getCode(), b.toString()); } - } else { - CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); - for (CanonicalType u : trr.getProfile()) { - b.append(u.asStringValue()); - } - rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES, trr.getCode(), b.toString()); } } else { List types = new ArrayList<>(); - for (UriType u : trr.getProfile()) { + for (UriType u : typeForResource.getProfile()) { StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue()); if (sd != null && !types.contains(sd.getType())) { types.add(sd.getType()); } } if (types.size() == 1) { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE2, resourceName, types.get(0)); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE2, resourceName, types.get(0)); } else { - rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types); + rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), + false, I18nConstants.BUNDLE_BUNDLE_ENTRY_TYPE3, resourceName, types); } } } @@ -4539,7 +4818,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.STRUCTURE, element.line(), element.col(), ei.getPath(), false, I18nConstants.EXTENSION_PROF_TYPE, profile.getUrl(), type, stype); } } - // + // Excluding reference is a kludge to get around versioning issues if (checkDefn.getType().get(0).hasProfile()) { for (CanonicalType p : checkDefn.getType().get(0).getProfile()) { @@ -4666,7 +4945,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } } else if (type.equals("Resource") || isResource(type)) { - validateContains(hostContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(), localStack, idStatusForEntry(element, ei)); // if + validateContains(hostContext, errors, ei.getPath(), checkDefn, definition, resource, ei.getElement(), + localStack, idStatusForEntry(element, ei), profile); // if elementValidated = true; // (str.matches(".*([.,/])work\\1$")) } else if (Utilities.isAbsoluteUrl(type)) { @@ -5237,18 +5517,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat /* * The actual base entry point for internal use (re-entrant) */ - private void validateResource(ValidatorHostContext hostContext, List errors, Element resource, Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { + private void validateResource(ValidatorHostContext hostContext, List errors, Element resource, + Element element, StructureDefinition defn, IdStatus idstatus, NodeStack stack) throws FHIRException { + + // check here if we call validation policy here, and then change it to the new interface assert stack != null; assert resource != null; boolean ok = true; String resourceName = element.getType(); // todo: consider namespace...? + if (defn == null) { long t = System.nanoTime(); defn = element.getProperty().getStructure(); if (defn == null) defn = context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName); timeTracker.sd(t); - ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), defn != null, I18nConstants.VALIDATION_VAL_PROFILE_NODEFINITION, resourceName); + //check exists + ok = rule(errors, IssueType.INVALID, element.line(), element.col(), stack.addToLiteralPath(resourceName), + defn != null, I18nConstants.VALIDATION_VAL_PROFILE_NODEFINITION, resourceName); } // special case: we have a bundle, and the profile is not for a bundle. We'll try the first entry instead @@ -5257,19 +5543,17 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (first != null && typeMatchesDefn(first.getElement().getType(), defn)) { element = first.getElement(); stack = first; - resourceName = element.getType(); + resourceName = element.getType(); // todo: consider namespace...? idstatus = IdStatus.OPTIONAL; // why? } // todo: validate everything in this bundle. } - ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), typeMatchesDefn(resourceName, defn), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, defn.getType(), resourceName, defn.getName()); - if (ok) { if (idstatus == IdStatus.REQUIRED && (element.getNamedChild(ID) == null)) { rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MISSING); } else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild(ID) != null)) { rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_PROHIBITED); - } + } if (element.getNamedChild(ID) != null) { Element eid = element.getNamedChild(ID); if (eid.getProperty() != null && eid.getProperty().getDefinition() != null && eid.getProperty().getDefinition().getBase().getPath().equals("Resource.id")) { @@ -5277,7 +5561,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, IssueType.INVALID, eid.line(), eid.col(), ns.getLiteralPath(), FormatUtilities.isValidId(eid.primitiveValue()), I18nConstants.RESOURCE_RES_ID_MALFORMED); } } - start(hostContext, errors, element, element, defn, stack); // root is both definition and type + // validate + 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 + } } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidatorFactory.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidatorFactory.java index d51487f6e..cf69c05fc 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidatorFactory.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidatorFactory.java @@ -1,7 +1,7 @@ -package org.hl7.fhir.validation.instance; - -import org.hl7.fhir.exceptions.FHIRException; - +package org.hl7.fhir.validation.instance; + +import org.hl7.fhir.exceptions.FHIRException; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -30,24 +30,24 @@ import org.hl7.fhir.exceptions.FHIRException; POSSIBILITY OF SUCH DAMAGE. */ - - - -import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.context.SimpleWorkerContext.IValidatorFactory; -import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.XVerExtensionManager; - -public class InstanceValidatorFactory implements IValidatorFactory { - - @Override - public IResourceValidator makeValidator(IWorkerContext ctxt, XVerExtensionManager xverManager) throws FHIRException { - return new InstanceValidator(ctxt, null, xverManager); - } - - @Override - public IResourceValidator makeValidator(IWorkerContext ctxt) throws FHIRException { - return new InstanceValidator(ctxt, null, null); - } - + + + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.SimpleWorkerContext.IValidatorFactory; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; +import org.hl7.fhir.r5.utils.XVerExtensionManager; + +public class InstanceValidatorFactory implements IValidatorFactory { + + @Override + public IResourceValidator makeValidator(IWorkerContext ctxt, XVerExtensionManager xverManager) throws FHIRException { + return new InstanceValidator(ctxt, null, xverManager); + } + + @Override + public IResourceValidator makeValidator(IWorkerContext ctxt) throws FHIRException { + return new InstanceValidator(ctxt, null, null); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/SpecialExtensions.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/SpecialExtensions.java index 587fd4e85..f02e80c28 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/SpecialExtensions.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/SpecialExtensions.java @@ -10,16 +10,18 @@ import org.hl7.fhir.utilities.Utilities; public class SpecialExtensions { // copied from R5 spec - private static final String MUST_SUPPORT_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"elementdefinition-type-must-support\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\",\"version\" : \"4.5.0\",\"name\" : \"type-must-support\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type\"},{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type.profile\"},{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type.targetProfile\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"An element may be labelled as must support. This extension clarifies which types/profiles/targetProfiles are must-support. It has no meaning if the element itself is not must-support. If the element is labelled must-support, and none of the options are labelled as must support, then an application must support at least one of the possible options, but is not required to support all of them.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"boolean\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"An element may be labelled as must support. This extension clarifies which types/profiles/targetProfiles are must-support. It has no meaning if the element itself is not must-support. If the element is labelled must-support, and none of the options are labelled as must support, then an application must support at least one of the possible options, but is not required to support all of them.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"boolean\"}]}]}}"; - private static final String INSTANCE_NAME_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"instance-name\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/instance-name\",\"version\" : \"4.5.0\",\"name\" : \"instance-name\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"Resource.meta\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-name\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"string\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-name\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"string\"}]}]}}"; - private static final String INSTANCE_DESC_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"instance-description\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/instance-description\",\"version\" : \"4.5.0\",\"name\" : \"instance-description\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"Resource.meta\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"markdown\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-description\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"markdown\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-description\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"markdown\"}]}]}}"; - private static final String CODE_SYSTEM_PROPS_EXT = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"codesystem-properties-mode\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"vocab\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\",\"version\" : \"4.6.0\",\"name\" : \"properties-mode\",\"status\" : \"draft\",\"date\" : \"2021-01-02T08:58:31+11:00\",\"publisher\" : \"HL7\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"fhirVersion\" : \"4.6.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"CodeSystem\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"not-present | partial | complete\",\"definition\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUri\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"code\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"binding\" : {\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName\",\"valueString\" : \"CodeSystemPropertiesMode\"}],\"strength\" : \"required\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in a code system resource.\",\"valueSet\" : \"http://hl7.org/fhir/ValueSet/codesystem-properties-mode\"},\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"not-present | partial | complete\",\"definition\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"min\" : 0,\"max\" : \"1\",\"isModifier\" : false},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"code\"}],\"binding\" : {\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName\",\"valueString\" : \"CodeSystemPropertiesMode\"}],\"strength\" : \"required\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in a code system resource.\",\"valueSet\" : \"http://hl7.org/fhir/ValueSet/codesystem-properties-mode\"}}]}}"; - + private static final String MUST_SUPPORT_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"elementdefinition-type-must-support\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\",\"version\" : \"4.5.0\",\"name\" : \"type-must-support\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type\"},{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type.profile\"},{\"type\" : \"element\",\"expression\" : \"ElementDefinition.type.targetProfile\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"An element may be labelled as must support. This extension clarifies which types/profiles/targetProfiles are must-support. It has no meaning if the element itself is not must-support. If the element is labelled must-support, and none of the options are labelled as must support, then an application must support at least one of the possible options, but is not required to support all of them.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"boolean\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"An element may be labelled as must support. This extension clarifies which types/profiles/targetProfiles are must-support. It has no meaning if the element itself is not must-support. If the element is labelled must-support, and none of the options are labelled as must support, then an application must support at least one of the possible options, but is not required to support all of them.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"boolean\"}]}]}}"; + private static final String INSTANCE_NAME_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"instance-name\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/instance-name\",\"version\" : \"4.5.0\",\"name\" : \"instance-name\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"Resource.meta\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-name\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"string\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-name\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"string\"}]}]}}"; + private static final String INSTANCE_DESC_SOURCE = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"instance-description\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/instance-description\",\"version\" : \"4.5.0\",\"name\" : \"instance-description\",\"status\" : \"draft\",\"date\" : \"2015-02-28\",\"publisher\" : \"Health Level Seven, Inc. - FHIR Core WG\",\"contact\" : [{\"telecom\" : [{\"system\" : \"url\",\"value\" : \"http://hl7.org/special/committees/FHIR\"}]}],\"description\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"fhirVersion\" : \"4.5.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"Resource.meta\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"markdown\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-description\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"markdown\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"The specified type/profile/target must be supported by implementations\",\"definition\" : \"If true indicates that the specified type, profile or targetProfile must be supported by implementations.\",\"comment\" : \"todo.\",\"min\" : 0,\"max\" : \"1\"},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/instance-description\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"markdown\"}]}]}}"; + private static final String CODE_SYSTEM_PROPS_EXT = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"codesystem-properties-mode\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"vocab\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\",\"version\" : \"4.6.0\",\"name\" : \"properties-mode\",\"status\" : \"draft\",\"date\" : \"2021-01-02T08:58:31+11:00\",\"publisher\" : \"HL7\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"fhirVersion\" : \"4.6.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"CodeSystem\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"not-present | partial | complete\",\"definition\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"code\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"binding\" : {\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName\",\"valueString\" : \"CodeSystemPropertiesMode\"}],\"strength\" : \"required\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in a code system resource.\",\"valueSet\" : \"http://hl7.org/fhir/ValueSet/codesystem-properties-mode\"},\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"not-present | partial | complete\",\"definition\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"min\" : 0,\"max\" : \"1\",\"isModifier\" : false},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"code\"}],\"binding\" : {\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName\",\"valueString\" : \"CodeSystemPropertiesMode\"}],\"strength\" : \"required\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in a code system resource.\",\"valueSet\" : \"http://hl7.org/fhir/ValueSet/codesystem-properties-mode\"}}]}}"; + private static final String INSTANCE_DEFN = "{\"resourceType\" : \"StructureDefinition\",\"id\" : \"definition\",\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg\",\"valueCode\" : \"fhir\"},{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm\",\"valueInteger\" : 1}],\"url\" : \"http://hl7.org/fhir/StructureDefinition/definition\",\"version\" : \"4.6.0\",\"name\" : \"properties-mode\",\"status\" : \"draft\",\"date\" : \"2021-01-02T08:58:31+11:00\",\"publisher\" : \"HL7\",\"description\" : \"BindingDefinition\",\"fhirVersion\" : \"4.6.0\",\"mapping\" : [{\"identity\" : \"rim\",\"uri\" : \"http://hl7.org/v3\",\"name\" : \"RIM Mapping\"}],\"kind\" : \"complex-type\",\"abstract\" : false,\"context\" : [{\"type\" : \"element\",\"expression\" : \"CodeSystem\"}],\"type\" : \"Extension\",\"baseDefinition\" : \"http://hl7.org/fhir/StructureDefinition/Extension\",\"derivation\" : \"constraint\",\"snapshot\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"not-present | partial | complete\",\"definition\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension\",\"min\" : 0,\"max\" : \"*\"},\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false},{\"id\" : \"Extension.id\",\"path\" : \"Extension.id\",\"representation\" : [\"xmlAttr\"],\"short\" : \"Unique id for inter-element referencing\",\"definition\" : \"Unique id for the element within a resource (for internal references). This may be any string value that does not contain spaces.\",\"min\" : 0,\"max\" : \"1\",\"base\" : {\"path\" : \"Element.id\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"string\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"n/a\"}]},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"slicing\" : {\"discriminator\" : [{\"type\" : \"value\",\"path\" : \"url\"}],\"description\" : \"Extensions are always sliced by (at least) url\",\"rules\" : \"open\"},\"short\" : \"Extension\",\"definition\" : \"An Extension\",\"min\" : 0,\"max\" : \"0\",\"base\" : {\"path\" : \"Element.extension\",\"min\" : 0,\"max\" : \"*\"},\"type\" : [{\"code\" : \"Extension\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"},{\"key\" : \"ext-1\",\"severity\" : \"error\",\"human\" : \"Must have either extensions or value[x], not both\",\"expression\" : \"extension.exists() != value.exists()\",\"xpath\" : \"exists(f:extension)!=exists(f:*[starts-with(local-name(.), \\\"value\\\")])\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Extension\"}],\"isModifier\" : false,\"isSummary\" : false},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"representation\" : [\"xmlAttr\"],\"short\" : \"identifies the meaning of the extension\",\"definition\" : \"Source of the definition for the extension code - a logical name or a URL.\",\"comment\" : \"The definition may point directly to a computable or human-readable definition of the extensibility codes, or it may be a logical URI as declared in some other specification. The definition SHALL be a URI for the Structure Definition defining the extension.\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.url\",\"min\" : 1,\"max\" : \"1\"},\"type\" : [{\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type\",\"valueUrl\" : \"uri\"}],\"code\" : \"http://hl7.org/fhirpath/System.String\"}],\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/definition\",\"isModifier\" : false,\"isSummary\" : false,\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"short\" : \"Value of extension\",\"definition\" : \"Value of extension - must be one of a constrained set of the data types (see [Extensibility](extensibility.html) for a list).\",\"min\" : 1,\"max\" : \"1\",\"base\" : {\"path\" : \"Extension.value[x]\",\"min\" : 0,\"max\" : \"1\"},\"type\" : [{\"code\" : \"string\"}],\"constraint\" : [{\"key\" : \"ele-1\",\"severity\" : \"error\",\"human\" : \"All FHIR elements must have a @value or children\",\"expression\" : \"hasValue() or (children().count() > id.count())\",\"xpath\" : \"@value|f:*|h:div\",\"source\" : \"http://hl7.org/fhir/StructureDefinition/Element\"}],\"isModifier\" : false,\"isSummary\" : false,\"binding\" : {\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName\",\"valueString\" : \"CodeSystemPropertiesMode\"}],\"strength\" : \"required\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in a code system resource.\",\"valueSet\" : \"http://hl7.org/fhir/ValueSet/codesystem-properties-mode\"},\"mapping\" : [{\"identity\" : \"rim\",\"map\" : \"N/A\"}]}]},\"differential\" : {\"element\" : [{\"id\" : \"Extension\",\"path\" : \"Extension\",\"short\" : \"not-present | partial | complete\",\"definition\" : \"The extent to which the properties of the code system (including the filters) are represented in this resource instance.\",\"min\" : 0,\"max\" : \"1\",\"isModifier\" : false},{\"id\" : \"Extension.extension\",\"path\" : \"Extension.extension\",\"max\" : \"0\"},{\"id\" : \"Extension.url\",\"path\" : \"Extension.url\",\"fixedUri\" : \"http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode\"},{\"id\" : \"Extension.value[x]\",\"path\" : \"Extension.value[x]\",\"min\" : 1,\"type\" : [{\"code\" : \"code\"}],\"binding\" : {\"extension\" : [{\"url\" : \"http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName\",\"valueString\" : \"CodeSystemPropertiesMode\"}],\"strength\" : \"required\",\"description\" : \"The extent to which the properties of the code system (including the filters) are represented in a code system resource.\",\"valueSet\" : \"http://hl7.org/fhir/ValueSet/codesystem-properties-mode\"}}]}}"; + public static boolean isKnownExtension(String url) { return Utilities.existsInList(url, "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support", "http://hl7.org/fhir/StructureDefinition/instance-name", "http://hl7.org/fhir/StructureDefinition/instance-description", + "http://hl7.org/fhir/build/StructureDefinition/definition", // wrongly defined in used in early R4B/R5 builds - changed to http://hl7.org/build/fhir/StructureDefinition/binding-definition "http://hl7.org/fhir/StructureDefinition/codesystem-properties-mode", "http://hl7.org/fhir/StructureDefinition/structuredefinition-rdf-type" ); @@ -31,6 +33,7 @@ public class SpecialExtensions { case "http://hl7.org/fhir/StructureDefinition/elementdefinition-type-must-support" : return makeExt(MUST_SUPPORT_SOURCE); case "http://hl7.org/fhir/StructureDefinition/instance-name" : return makeExt(INSTANCE_NAME_SOURCE); case "http://hl7.org/fhir/StructureDefinition/instance-description" : return makeExt(INSTANCE_DESC_SOURCE); + case "http://hl7.org/fhir/StructureDefinition/definition" : return makeExt(INSTANCE_DEFN); case "codesystem-properties-mode" : return makeExt(CODE_SYSTEM_PROPS_EXT); default: return null; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java index 46067244e..2ab7ab597 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/BundleValidator.java @@ -14,7 +14,7 @@ import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.r5.model.Enumerations.FHIRVersion; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.utils.XVerExtensionManager; -import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.BundleValidationRule; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; @@ -369,7 +369,8 @@ public class BundleValidator extends BaseValidator{ if (ref != null && !Utilities.noString(reference) && !reference.startsWith("#")) { Element target = resolveInBundle(entries, reference, fullUrl, type, id); - rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOTFOUND, reference, name); + rule(errors, IssueType.INVALID, ref.line(), ref.col(), stack.addToLiteralPath("reference"), target != null, + I18nConstants.BUNDLE_BUNDLE_ENTRY_NOTFOUND, reference, name); } } @@ -402,7 +403,7 @@ public class BundleValidator extends BaseValidator{ if (r != null) { EntrySummary e = new EntrySummary(i, entry, r); entryList.add(e); - System.out.println("Found entry "+e.dbg()); +// System.out.println("Found entry "+e.dbg()); } i++; } @@ -415,10 +416,10 @@ public class BundleValidator extends BaseValidator{ EntrySummary t = entryForTarget(entryList, tgt); if (t != null ) { if (t != e) { - System.out.println("Entry "+e.getIndex()+" refers to "+t.getIndex()+" by ref '"+ref+"'"); +// System.out.println("Entry "+e.getIndex()+" refers to "+t.getIndex()+" by ref '"+ref+"'"); e.getTargets().add(t); } else { - System.out.println("Entry "+e.getIndex()+" refers to itself by '"+ref+"'"); +// System.out.println("Entry "+e.getIndex()+" refers to itself by '"+ref+"'"); } } } @@ -432,7 +433,7 @@ public class BundleValidator extends BaseValidator{ foundRevLinks = false; for (EntrySummary e : entryList) { if (!visited.contains(e)) { - System.out.println("Not visited "+e.getIndex()+" - check for reverse links"); +// System.out.println("Not visited "+e.getIndex()+" - check for reverse links"); boolean add = false; for (EntrySummary t : e.getTargets()) { if (visited.contains(t)) { @@ -443,7 +444,7 @@ public class BundleValidator extends BaseValidator{ warning(errors, IssueType.INFORMATIONAL, e.getEntry().line(), e.getEntry().col(), stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), isExpectedToBeReverse(e.getResource().fhirType()), I18nConstants.BUNDLE_BUNDLE_ENTRY_REVERSE, (e.getEntry().getChildValue(FULL_URL) != null ? "'" + e.getEntry().getChildValue(FULL_URL) + "'" : "")); - System.out.println("Found reverse links for "+e.getIndex()); +// System.out.println("Found reverse links for "+e.getIndex()); foundRevLinks = true; visitLinked(visited, e); } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java index 04896f54a..648d1b08f 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/CodeSystemValidator.java @@ -27,11 +27,16 @@ public class CodeSystemValidator extends BaseValidator { source = Source.InstanceValidator; this.timeTracker = timeTracker; } - + public void validateCodeSystem(List errors, Element cs, NodeStack stack, ValidationOptions options) { String url = cs.getNamedChildValue("url"); String content = cs.getNamedChildValue("content"); - + String caseSensitive = cs.getNamedChildValue("caseSensitive"); + String hierarchyMeaning = cs.getNamedChildValue("hierarchyMeaning"); + String supp = cs.getNamedChildValue("supplements"); + + metaChecks(errors, cs, stack, url, content, caseSensitive, hierarchyMeaning, !Utilities.noString(supp)); + String vsu = cs.getNamedChildValue("valueSet"); if (!Utilities.noString(vsu)) { hint(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), "complete".equals(content), I18nConstants.CODESYSTEM_CS_NO_VS_NOTCOMPLETE); @@ -46,7 +51,7 @@ public class CodeSystemValidator extends BaseValidator { if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().size() == 1, I18nConstants.CODESYSTEM_CS_VS_INVALID, url, vsu)) { if (rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getCompose().getInclude().get(0).getSystem().equals(url), I18nConstants.CODESYSTEM_CS_VS_WRONGSYSTEM, url, vsu, vs.getCompose().getInclude().get(0).getSystem())) { rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), !vs.getCompose().getInclude().get(0).hasValueSet() - && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu); + && !vs.getCompose().getInclude().get(0).hasConcept() && !vs.getCompose().getInclude().get(0).hasFilter(), I18nConstants.CODESYSTEM_CS_VS_INCLUDEDETAILS, url, vsu); if (vs.hasExpansion()) { int count = countConcepts(cs); rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs.getExpansion().getContains().size() == count, I18nConstants.CODESYSTEM_CS_VS_EXP_MISMATCH, url, vsu, count, vs.getExpansion().getContains().size()); @@ -56,8 +61,7 @@ public class CodeSystemValidator extends BaseValidator { } } } // todo... try getting the value set the other way... - - String supp = cs.getNamedChildValue("supplements"); + if (supp != null) { if (context.supportsSystem(supp)) { List concepts = cs.getChildrenByName("concept"); @@ -74,13 +78,82 @@ public class CodeSystemValidator extends BaseValidator { } } + private void metaChecks(List errors, Element cs, NodeStack stack, String url, String content, String caseSensitive, String hierarchyMeaning, boolean isSupplement) { + if (isSupplement) { + if (!"supplement".equals(content)) { + NodeStack s = stack.push(cs.getNamedChild("content"), -1, null, null); + rule(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_WRONG); + } + if (!Utilities.noString(caseSensitive)) { + NodeStack s = stack.push(cs.getNamedChild("caseSensitive"), -1, null, null); + rule(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL, "caseSensitive"); + } + if (!Utilities.noString(hierarchyMeaning)) { + NodeStack s = stack.push(cs.getNamedChild("hierarchyMeaning"), -1, null, null); + rule(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL, "caseSensitive"); + } + + } else { + boolean isHL7 = url != null && (url.contains("hl7.org") || url.contains("fhir.org")); + if (Utilities.noString(content)) { + NodeStack s = stack; + Element c = cs.getNamedChild("content"); + if (c != null) { + s = stack.push(c, -1, null, null); + } + if (isHL7) { + rule(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHALL, "content"); + } else { + warning(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_NONHL7_MISSING_ELEMENT, "content"); + } + } else if ("supplement".equals(content)) { + NodeStack s = stack.push(cs.getNamedChild("content"), -1, null, null); + rule(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_PRESENT_ELEMENT_SUPPL_MISSING); + } + if (Utilities.noString(caseSensitive)) { + NodeStack s = stack; + Element c = cs.getNamedChild("caseSensitive"); + if (c != null) { + s = stack.push(c, -1, null, null); + } + if (isHL7) { + warning(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHOULD, "caseSensitive"); + } else { + hint(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_NONHL7_MISSING_ELEMENT, "caseSensitive"); + } + } + if (Utilities.noString(hierarchyMeaning) && hasHeirarchy(cs)) { + NodeStack s = stack; + Element c = cs.getNamedChild("hierarchyMeaning"); + if (c != null) { + s = stack.push(c, -1, null, null); + } + if (isHL7) { + warning(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHOULD, "hierarchyMeaning"); + } else { + hint(errors, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_NONHL7_MISSING_ELEMENT, "hierarchyMeaning"); + } + } + } + } + + + private boolean hasHeirarchy(Element cs) { + for (Element c : cs.getChildren("concept")) { + if (c.hasChildren("concept")) { + return true; + } + } + return false; + } + private void validateSupplementConcept(List errors, Element concept, NodeStack stack, String supp, ValidationOptions options) { String code = concept.getChildValue("code"); if (!Utilities.noString(code)) { org.hl7.fhir.r5.context.IWorkerContext.ValidationResult res = context.validateCode(options, systemFromCanonical(supp), versionFromCanonical(supp), code, null); rule(errors, IssueType.BUSINESSRULE, stack.getLiteralPath(), res.isOk(), I18nConstants.CODESYSTEM_CS_SUPP_INVALID_CODE, supp, code); } - + } private int countConcepts(Element cs) { 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 77e5e7e65..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; @@ -26,6 +27,7 @@ import org.hl7.fhir.r5.elementmodel.ObjectConverter; import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.DateType; +import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.Enumerations.FHIRVersion; import org.hl7.fhir.r5.model.FhirPublication; import org.hl7.fhir.r5.model.IntegerType; @@ -39,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; @@ -51,6 +55,8 @@ 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; @@ -58,6 +64,57 @@ 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; + private String containerPath; + + public static QuestionnaireWithContext fromQuestionnaire(Questionnaire q) { + if (q == null) { + return null; + } + QuestionnaireWithContext res = new QuestionnaireWithContext(); + res.q = q; + return res; + } + + public static QuestionnaireWithContext fromContainedResource(String path, Element e, Questionnaire q) { + if (q == null) { + return null; + } + QuestionnaireWithContext res = new QuestionnaireWithContext(); + res.q = q; + res.container = e; + res.containerPath = path; + return res; + } + + public Questionnaire q() { + return q; + } + + } + private EnableWhenEvaluator myEnableWhenEvaluator; private FHIRPathEngine fpe; private QuestionnaireMode questionnaireMode; @@ -191,7 +248,12 @@ public class QuestionnaireValidator extends BaseValidator { rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE) : hint(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), questionnaire != null, I18nConstants.QUESTIONNAIRE_QR_Q_NONE); if (ok) { - Questionnaire qsrc = questionnaire.startsWith("#") ? loadQuestionnaire(element, questionnaire.substring(1)) : context.fetchResource(Questionnaire.class, questionnaire); + QuestionnaireWithContext qsrc = null; + if (questionnaire.startsWith("#")) { + qsrc = QuestionnaireWithContext.fromContainedResource(stack.getLiteralPath(), element, (Questionnaire) loadContainedResource(errors, stack.getLiteralPath(), element, questionnaire.substring(1), Questionnaire.class)); + } else { + qsrc = QuestionnaireWithContext.fromQuestionnaire(context.fetchResource(Questionnaire.class, questionnaire)); + } if (questionnaireMode == QuestionnaireMode.REQUIRED) { ok = rule(errors, IssueType.REQUIRED, q.line(), q.col(), stack.getLiteralPath(), qsrc != null, I18nConstants.QUESTIONNAIRE_QR_Q_NOTFOUND, questionnaire); } else if (questionnaire.startsWith("http://example.org")) { @@ -201,66 +263,12 @@ public class QuestionnaireValidator extends BaseValidator { } if (ok) { boolean inProgress = "in-progress".equals(element.getNamedChildValue("status")); - validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); + validateQuestionannaireResponseItems(hostContext, qsrc, qsrc.q().getItem(), errors, element, stack, inProgress, element, new QStack(qsrc, element)); } } } - private Questionnaire loadQuestionnaire(Element resource, String id) throws FHIRException { - try { - for (Element contained : resource.getChildren("contained")) { - if (contained.getIdBase().equals(id)) { - FhirPublication v = FhirPublication.fromCode(context.getVersion()); - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - new JsonParser(context).compose(contained, bs, OutputStyle.NORMAL, id); - byte[] json = bs.toByteArray(); - switch (v) { - case DSTU1: - throw new FHIRException(context.formatMessage(I18nConstants.UNSUPPORTED_VERSION_R1)); - case DSTU2: - org.hl7.fhir.dstu2.model.Resource r2 = new org.hl7.fhir.dstu2.formats.JsonParser().parse(json); - Resource r5 = VersionConvertorFactory_10_50.convertResource(r2); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case DSTU2016May: - org.hl7.fhir.dstu2016may.model.Resource r2a = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(json); - r5 = VersionConvertorFactory_14_50.convertResource(r2a); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case STU3: - org.hl7.fhir.dstu3.model.Resource r3 = new org.hl7.fhir.dstu3.formats.JsonParser().parse(json); - r5 = VersionConvertorFactory_30_50.convertResource(r3); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case R4: - org.hl7.fhir.r4.model.Resource r4 = new org.hl7.fhir.r4.formats.JsonParser().parse(json); - r5 = VersionConvertorFactory_40_50.convertResource(r4); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - case R5: - r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(json); - if (r5 instanceof Questionnaire) - return (Questionnaire) r5; - else - return null; - } - } - } - return null; - } catch (IOException e) { - throw new FHIRException(e); - } - } - - private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + private void validateQuestionnaireResponseItem(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, QuestionnaireItemComponent qItem, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { String text = element.getNamedChildValue("text"); rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), Utilities.noString(text) || text.equals(qItem.getText()), I18nConstants.QUESTIONNAIRE_QR_ITEM_TEXT, qItem.getLinkId()); @@ -279,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: @@ -353,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()); @@ -379,14 +391,13 @@ public class QuestionnaireValidator extends BaseValidator { return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; } - private void validateQuestionnaireResponseItem(ValidatorHostContext hostcontext, Questionnaire 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())); } } @@ -398,12 +409,13 @@ public class QuestionnaireValidator extends BaseValidator { return -1; } - private void validateQuestionannaireResponseItems(ValidatorHostContext hostContext, Questionnaire qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { + private void validateQuestionannaireResponseItems(ValidatorHostContext hostContext, QuestionnaireWithContext qsrc, List qItems, List errors, Element element, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot, QStack qstack) { 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)) { @@ -412,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); @@ -423,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, Questionnaire 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()); } } @@ -500,8 +511,8 @@ public class QuestionnaireValidator extends BaseValidator { return null; } - private QuestionnaireItemComponent findQuestionnaireItem(Questionnaire qSrc, String linkId) { - return findItem(qSrc.getItem(), linkId); + private QuestionnaireItemComponent findQuestionnaireItem(QuestionnaireWithContext qSrc, String linkId) { + return findItem(qSrc.q.getItem(), linkId); } private QuestionnaireItemComponent findItem(List list, String linkId) { @@ -515,8 +526,13 @@ public class QuestionnaireValidator extends BaseValidator { return null; } - private void validateAnswerCode(List errors, Element value, NodeStack stack, Questionnaire qSrc, String ref, boolean theOpenChoice) { - ValueSet vs = resolveBindingReference(qSrc, ref, qSrc.getUrl()); + private void validateAnswerCode(List errors, Element value, NodeStack stack, QuestionnaireWithContext qSrc, String ref, boolean theOpenChoice) { + ValueSet vs = null; + if (ref.startsWith("#") && qSrc.container != null) { + vs = (ValueSet) loadContainedResource(errors, qSrc.containerPath, qSrc.container, ref.substring(1), ValueSet.class); + } else { + vs = resolveBindingReference(qSrc.q(), ref, qSrc.q().getUrl()); + } if (warning(errors, IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), vs != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(ref))) { try { Coding c = ObjectConverter.readAsCoding(value); @@ -527,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()); @@ -540,7 +557,17 @@ public class QuestionnaireValidator extends BaseValidator { } } - private void validateAnswerCode(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean theOpenChoice) { + 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); if (qItem.getAnswerOption().size() > 0) @@ -552,11 +579,11 @@ public class QuestionnaireValidator extends BaseValidator { hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_NOOPTIONS); } - private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type) { + private void checkOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type) { checkOption(errors, answer, stack, qSrc, qItem, type, false); } - private void checkOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) { + private void checkOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, String type, boolean openChoice) { if (type.equals("integer")) checkIntegerOption(errors, answer, stack, qSrc, qItem, openChoice); else if (type.equals("date")) checkDateOption(errors, answer, stack, qSrc, qItem, openChoice); else if (type.equals("time")) checkTimeOption(errors, answer, stack, qSrc, qItem, openChoice); @@ -564,7 +591,7 @@ public class QuestionnaireValidator extends BaseValidator { else if (type.equals("Coding")) checkCodingOption(errors, answer, stack, qSrc, qItem, openChoice); } - private void checkIntegerOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + private void checkIntegerOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { Element v = answer.getNamedChild("valueInteger"); NodeStack ns = stack.push(v, -1, null, null); if (qItem.getAnswerOption().size() > 0) { @@ -594,7 +621,7 @@ public class QuestionnaireValidator extends BaseValidator { hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_INTNOOPTIONS); } - private void checkDateOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + private void checkDateOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { Element v = answer.getNamedChild("valueDate"); NodeStack ns = stack.push(v, -1, null, null); if (qItem.getAnswerOption().size() > 0) { @@ -624,7 +651,7 @@ public class QuestionnaireValidator extends BaseValidator { hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_DATENOOPTIONS); } - private void checkTimeOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + private void checkTimeOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { Element v = answer.getNamedChild("valueTime"); NodeStack ns = stack.push(v, -1, null, null); if (qItem.getAnswerOption().size() > 0) { @@ -654,7 +681,7 @@ public class QuestionnaireValidator extends BaseValidator { hint(errors, IssueType.STRUCTURE, v.line(), v.col(), stack.getLiteralPath(), false, I18nConstants.QUESTIONNAIRE_QR_ITEM_TIMENOOPTIONS); } - private void checkStringOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + private void checkStringOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { Element v = answer.getNamedChild("valueString"); NodeStack ns = stack.push(v, -1, null, null); if (qItem.getAnswerOption().size() > 0) { @@ -689,7 +716,7 @@ public class QuestionnaireValidator extends BaseValidator { } } - private void checkCodingOption(List errors, Element answer, NodeStack stack, Questionnaire qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { + private void checkCodingOption(List errors, Element answer, NodeStack stack, QuestionnaireWithContext qSrc, QuestionnaireItemComponent qItem, boolean openChoice) { Element v = answer.getNamedChild("valueCoding"); String system = v.getNamedChildValue("system"); String code = v.getNamedChildValue("code"); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java index 64a7f2c17..8ff4504e5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java @@ -10,6 +10,7 @@ import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.npm.CachingPackageClient; import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.PackageClient; import org.hl7.fhir.utilities.npm.PackageInfo; import org.hl7.fhir.utilities.npm.ToolsVersion; @@ -22,7 +23,7 @@ public class PackageValidator { private void execute() throws IOException { FilesystemPackageCacheManager pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); - CachingPackageClient pc = new CachingPackageClient(FilesystemPackageCacheManager.PRIMARY_SERVER); + CachingPackageClient pc = new CachingPackageClient(PackageClient.PRIMARY_SERVER); for (PackageInfo t : pc.search(null, null, null, false)) { System.out.println("Check Package "+t.getId()); List vl = pc.getVersions(t.getId()); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java index db156622d..60972c62e 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/profile/ProfileValidator.java @@ -113,7 +113,7 @@ public class ProfileValidator extends BaseValidator { if (snapshotElements != null) { for (ElementDefinition diffElement : profile.getDifferential().getElement()) { if (diffElement == null) - throw new Error("What?"); + throw new Error("Diff Element is null - this is not an expected thing"); ElementDefinition snapElement = snapshotElements.get(diffElement.getId()); if (snapElement!=null) { // Happens with profiles in the main build - should be able to fix once snapshot generation is fixed - Lloyd warning(errors, IssueType.BUSINESSRULE, diffElement.getId(), !checkMustSupport || snapElement.hasMustSupport(), "Elements included in the differential should declare mustSupport"); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java index 86cc33b16..82017d2e6 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/comparison/tests/ComparisonTests.java @@ -114,7 +114,8 @@ public class ComparisonTests { BaseWorkerContext bc = (BaseWorkerContext) context; boolean dupl = bc.isAllowLoadingDuplicates(); bc.setAllowLoadingDuplicates(true); - context.loadFromPackage(npm, new R4ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5())); + context.loadFromPackage(npm, new R4ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, + new NullLoaderKnowledgeProviderR5(), context.getVersion())); bc.setAllowLoadingDuplicates(dupl); } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java index 9ea700674..f0a78967b 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/R3R4ConversionTests.java @@ -37,11 +37,12 @@ import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r4.model.StructureMap; import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.test.utils.TestingUtilities; -import org.hl7.fhir.r4.utils.IResourceValidator; -import org.hl7.fhir.r4.utils.IResourceValidator.IValidatorResourceFetcher; -import org.hl7.fhir.r4.utils.IResourceValidator.ReferenceValidationPolicy; +import org.hl7.fhir.r4.utils.validation.IResourceValidator; +import org.hl7.fhir.r4.utils.validation.IValidatorResourceFetcher; +import org.hl7.fhir.r4.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.r4.utils.StructureMapUtilities; import org.hl7.fhir.r4.utils.StructureMapUtilities.ITransformerServices; +import org.hl7.fhir.r4.utils.validation.IValidationPolicyAdvisor; import org.hl7.fhir.utilities.IniFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -62,7 +63,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @Disabled -public class R3R4ConversionTests implements ITransformerServices, IValidatorResourceFetcher { +public class R3R4ConversionTests implements ITransformerServices, IValidatorResourceFetcher, IValidationPolicyAdvisor { private static final boolean SAVING = true; private FilesystemPackageCacheManager pcm = null; @@ -454,7 +455,15 @@ public class R3R4ConversionTests implements ITransformerServices, IValidatorReso } @Override - public ReferenceValidationPolicy validationPolicy(Object appContext, String path, String url) { + public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { + return ReferenceValidationPolicy.IGNORE; + } + + @Override + public ReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, + String containerType, String containerId, + Element.SpecialElement containingResourceType, String path, + String url) { return ReferenceValidationPolicy.IGNORE; } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java index d6a087ab5..d5896e537 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java @@ -4,7 +4,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -20,7 +19,6 @@ import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.r5.conformance.ProfileUtilities; import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; import org.hl7.fhir.r5.formats.IParser.OutputStyle; -import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; @@ -37,7 +35,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r5.utils.IResourceValidator; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java index ec1304205..a0aba6e61 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java @@ -107,7 +107,7 @@ public class UtilitiesXTests { if (version.startsWith("3.0")) return new R3ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5()); if (version.startsWith("4.0")) - return new R4ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5()); + return new R4ToR5Loader(new String[] { "CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire","ConceptMap","StructureMap", "NamingSystem"}, new NullLoaderKnowledgeProviderR5(), version); return null; } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java index 0d2ccf2c8..1180262cd 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTests.java @@ -5,7 +5,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -16,9 +15,6 @@ import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.NotImplementedException; -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; 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; @@ -32,12 +28,12 @@ import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat; import org.hl7.fhir.r5.elementmodel.ObjectConverter; -import org.hl7.fhir.r5.elementmodel.SHCParser; import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.Constants; +import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.FhirPublication; import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.r5.model.Patient; @@ -48,11 +44,15 @@ import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.test.utils.TestingUtilities; import org.hl7.fhir.r5.utils.FHIRPathEngine; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; -import org.hl7.fhir.r5.utils.IResourceValidator; -import org.hl7.fhir.r5.utils.IResourceValidator.BestPracticeWarningLevel; -import org.hl7.fhir.r5.utils.IResourceValidator.BundleValidationRule; -import org.hl7.fhir.r5.utils.IResourceValidator.IValidatorResourceFetcher; -import org.hl7.fhir.r5.utils.IResourceValidator.ReferenceValidationPolicy; +import org.hl7.fhir.r5.utils.validation.IResourceValidator; +import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor; +import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; +import org.hl7.fhir.r5.utils.validation.constants.BindingKind; +import org.hl7.fhir.r5.utils.validation.constants.CodedContentValidationPolicy; +import org.hl7.fhir.r5.utils.validation.BundleValidationRule; +import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; +import org.hl7.fhir.r5.utils.validation.constants.ContainedReferenceValidationPolicy; +import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.hl7.fhir.utilities.SimpleHTTPClient; import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult; import org.hl7.fhir.utilities.TextFile; @@ -80,7 +80,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; @RunWith(Parameterized.class) -public class ValidationTests implements IEvaluationContext, IValidatorResourceFetcher { +public class ValidationTests implements IEvaluationContext, IValidatorResourceFetcher, IValidationPolicyAdvisor { public final static boolean PRINT_OUTPUT_TO_CONSOLE = true; @@ -179,6 +179,9 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } else { val.setFetcher(this); } + + val.setPolicyAdvisor(this); + if (content.has("allowed-extension-domain")) val.getExtensionDomains().add(content.get("allowed-extension-domain").getAsString()); if (content.has("allowed-extension-domains")) @@ -509,13 +512,32 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe } @Override - public ReferenceValidationPolicy validationPolicy(IResourceValidator validator, Object appContext, String path, String url) { - if (content.has("validate")) - return ReferenceValidationPolicy.valueOf(content.get("validate").getAsString()); + public ReferenceValidationPolicy policyForReference(IResourceValidator validator, Object appContext, String path, String url) { + if (content.has("validateReference")) + return ReferenceValidationPolicy.valueOf(content.get("validateReference").getAsString()); else return ReferenceValidationPolicy.IGNORE; } + @Override + public ContainedReferenceValidationPolicy policyForContained(IResourceValidator validator, Object appContext, + String containerType, String containerId, + Element.SpecialElement containingResourceType, + String path, String url) { + if (content.has("validateContains")) + return ContainedReferenceValidationPolicy.valueOf(content.get("validateContains").getAsString()); + else + return ContainedReferenceValidationPolicy.CHECK_VALID; + } + + @Override + public CodedContentValidationPolicy policyForCodedContent(IResourceValidator validator, Object appContext, String stackPath, ElementDefinition definition, + StructureDefinition structure, BindingKind kind, ValueSet valueSet, List systems) { + if (content.has("validateCodedContent")) + return CodedContentValidationPolicy.valueOf(content.get("validateCodedContent").getAsString()); + else + return CodedContentValidationPolicy.VALUESET; + } @Override public boolean resolveURL(IResourceValidator validator, Object appContext, String path, String url, String type) throws IOException, FHIRException { return !url.contains("example.org") && !url.startsWith("http://hl7.org/fhir/invalid"); @@ -570,4 +592,5 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe public boolean fetchesCanonicalResource(IResourceValidator validator, String url) { return false; } + } \ No newline at end of file diff --git a/pom.xml b/pom.xml index aa1749e7c..65df5e3a5 100644 --- a/pom.xml +++ b/pom.xml @@ -14,17 +14,18 @@ HAPI FHIR --> org.hl7.fhir.core - 5.5.12-SNAPSHOT + 5.6.16-SNAPSHOT pom 5.1.0 - 1.1.74-SNAPSHOT + 1.1.83-SNAPSHOT 5.7.1 1.7.1 - 3.0.0-M4 - 0.8.5 + 3.0.0-M5 + 0.8.7 1.5.1 + 1.18.22 HL7 Core Artifacts @@ -195,24 +196,6 @@ - - maven-surefire-plugin - ${maven_surefire_version} - - 1 - true - classes - false - false - - ${argLine} -Xmx4096m - false - - org/hl7/fhir/validation/cli/** - - - org.basepom.maven duplicate-finder-maven-plugin @@ -368,6 +351,66 @@ + + surefire-java-8 + + (,1.8] + + + + + maven-surefire-plugin + ${maven_surefire_version} + + 1 + true + classes + false + false + + + ${argLine} -Xmx4096m -XX:+UseConcMarkSweepGC + false + + org/hl7/fhir/validation/cli/** + + + + + + + + surefire-java-9-plus + + [9,) + + + + + maven-surefire-plugin + ${maven_surefire_version} + + 1 + true + classes + false + false + + ${argLine} -Xmx4096m + false + + org/hl7/fhir/validation/cli/** + + + + + + github-repo @@ -436,7 +479,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.6.8 true ossrh diff --git a/pull-request-pipeline-parameterized.yml b/pull-request-pipeline-parameterized.yml index 02e6161d5..672d25181 100644 --- a/pull-request-pipeline-parameterized.yml +++ b/pull-request-pipeline-parameterized.yml @@ -1,15 +1,15 @@ jobs: - - ${{ each imageName in parameters.imageName }}: - - ${{ each jdkVersion in parameters.jdkVersion }}: + - ${{ each image in parameters.images }}: + - ${{ each jdkVersion in image.jdkVersions }}: - job: - displayName: ${{imageName}}_${{jdkVersion}} + displayName: ${{image.name}}_${{jdkVersion}} pool: - vmImage: ${{imageName}} + vmImage: ${{image.name}} variables: - currentImage: ${{imageName}} + currentImage: ${{image.name}} codecov: $(CODECOV_TOKEN) VERSION: diff --git a/pull-request-pipeline.yml b/pull-request-pipeline.yml index aac93bec9..508451c92 100644 --- a/pull-request-pipeline.yml +++ b/pull-request-pipeline.yml @@ -5,59 +5,13 @@ pr: - release # Different users have different machine setups, we run the build three times, on ubuntu, osx, and windows -strategy: - matrix: - linux: - imageName: 'ubuntu-latest' - mac: - imageName: "macos-latest" - windows: - imageName: "windows-2019" - maxParallel: 3 - -pool: - vmImage: $(imageName) - -variables: - currentImage: $(imageName) - codecov: $(CODECOV_TOKEN) - VERSION: - -steps: - # Runs 'mvn clean package' - - task: Maven@3 - inputs: - mavenPomFile: 'pom.xml' - mavenOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: '1.11' - jdkArchitectureOption: 'x64' - publishJUnitResults: true - testResultsFiles: '**/surefire-reports/TEST-*.xml' - goals: 'package' - - - task: Maven@3 - inputs: - mavenPomFile: 'pom.xml' - mavenOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: '1.11' - jdkArchitectureOption: 'x64' - options: '-pl org.hl7.fhir.validation.cli' - publishJUnitResults: false - testResultsFiles: '**/surefire-reports/TEST-*.xml' - goals: 'exec:exec' - - # Upload test results to codecov - - script: bash <(curl https://codecov.io/bash) -t $(codecov) - displayName: 'codecov Bash Uploader' - condition: eq(variables.currentImage, 'ubuntu-latest') - - # Publishes the test results to build artifacts. - - task: PublishCodeCoverageResults@1 - displayName: 'Publish JaCoCo test results' - condition: eq(variables.currentImage, 'ubuntu-latest') - inputs: - codeCoverageTool: 'JaCoCo' - summaryFileLocation: '$(System.DefaultWorkingDirectory)/org.hl7.fhir.report/target/site/jacoco-aggregate/jacoco.xml' - reportDirectory: '$(System.DefaultWorkingDirectory)/org.hl7.fhir.report/target/site/jacoco-aggregate/' \ No newline at end of file +jobs: + - template: pull-request-pipeline-parameterized.yml + parameters: + images: + - name: ubuntu-latest + jdkVersions: [ '1.8', '1.11' ] + - name: macos-latest + jdkVersions: [ '1.8', '1.11', '1.17'] + - name: windows-2019 + jdkVersions: [ '1.8', '1.11', '1.17'] \ No newline at end of file diff --git a/test-java-8-11-17-pipeline.yml b/test-java-8-11-17-pipeline.yml deleted file mode 100644 index 3d791362f..000000000 --- a/test-java-8-11-17-pipeline.yml +++ /dev/null @@ -1,11 +0,0 @@ -trigger: none - -pr: -- master -- release - -jobs: -- template: pull-request-pipeline-parameterized.yml - parameters: - imageName: [ 'ubuntu-latest', 'macos-latest', 'windows-2019' ] - jdkVersion: [ '1.8', '1.11', '1.17'] \ No newline at end of file