From 25cac32326f7a0828b1af89175a68ba353973fd0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:39:14 +1000 Subject: [PATCH 01/16] depend on 1.1.42-snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 87f56d509..6f9443f5c 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 5.1.0 - 1.1.41 + 1.1.42-SNAPSHOT 5.6.2 3.0.0-M4 0.8.5 From 54d320bb7b69cff367861c848eae99d065aa16fc Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:40:06 +1000 Subject: [PATCH 02/16] Don't make a column for definitions in a code system if there are none --- .../fhir/r5/renderers/CodeSystemRenderer.java | 83 ++++++++++++------- 1 file changed, 52 insertions(+), 31 deletions(-) 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 c6abb1b4a..028b2c68a 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 @@ -134,6 +134,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { return false; } XhtmlNode t = x.table( "codes"); + boolean definitions = false; boolean commentS = false; boolean deprecated = false; boolean display = false; @@ -161,14 +162,15 @@ public class CodeSystemRenderer extends TerminologyRenderer { display = display || conceptsHaveDisplay(c); version = version || conceptsHaveVersion(c); hierarchy = hierarchy || c.hasConcept(); + definitions = definitions || conceptsHaveDefinition(c); } CodeSystemNavigator csNav = new CodeSystemNavigator(cs); hierarchy = hierarchy || csNav.isRestructure(); List langs = new ArrayList<>(); - addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, true, commentS, version, deprecated, properties, null, false), maps); + 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, 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) || hasExtensions; } if (langs.size() > 0) { Collections.sort(langs); @@ -185,6 +187,23 @@ public class CodeSystemRenderer extends TerminologyRenderer { return hasExtensions; } + private boolean conceptsHaveDefinition(ConceptDefinitionComponent c) { + if (c.hasDefinition()) { + return true; + } + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { + return true; + } + } + for (ConceptDefinitionComponent g : c.getConcept()) { + if (conceptsHaveDefinition(g)) { + return true; + } + } + return false; + } + private boolean conceptsHaveProperty(ConceptDefinitionComponent c, PropertyComponent cp) { if (CodeSystemUtilities.hasProperty(c, cp.getCode())) return true; @@ -271,7 +290,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { - private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int level, boolean hasHierarchy, boolean hasDisplay, 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) throws FHIRFormatError, DefinitionException, IOException { boolean hasExtensions = false; XhtmlNode tr = t.tr(); XhtmlNode td = tr.td(); @@ -297,36 +316,38 @@ public class CodeSystemRenderer extends TerminologyRenderer { if (hasDisplay) { td = tr.td(); renderDisplayName(c, cs, td); - } - td = tr.td(); - if (c != null && - c.hasDefinitionElement()) { - if (getContext().getLang() == null) { - if (hasMarkdownInDefinitions(cs)) - addMarkdown(td, c.getDefinition()); - else + } + if (hasDefinitions) { + td = tr.td(); + if (c != null && + c.hasDefinitionElement()) { + if (getContext().getLang() == null) { + if (hasMarkdownInDefinitions(cs)) + addMarkdown(td, c.getDefinition()); + else + td.addText(c.getDefinition()); + } else if (getContext().getLang().equals("*")) { + boolean sl = false; + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) + sl = true; + td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDefinition()); + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { + td.br(); + td.addText(cd.getLanguage()+": "+cd.getValue()); + } + } + } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) { td.addText(c.getDefinition()); - } else if (getContext().getLang().equals("*")) { - boolean sl = false; - for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) - if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) - sl = true; - td.addText((sl ? cs.getLanguage("en")+": " : "")+c.getDefinition()); - for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { - if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && !c.getDefinition().equalsIgnoreCase(cd.getValue())) { - td.br(); - td.addText(cd.getLanguage()+": "+cd.getValue()); + } else { + for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { + if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) { + td.addText(cd.getValue()); + } } } - } else if (getContext().getLang().equals(cs.getLanguage()) || (getContext().getLang().equals("en") && !cs.hasLanguage())) { - td.addText(c.getDefinition()); - } else { - for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) { - if (cd.getUse().is("http://terminology.hl7.org/CodeSystem/designation-usage", "definition") && cd.hasLanguage() && cd.getLanguage().equals(getContext().getLang())) { - td.addText(cd.getValue()); - } - } - } + } } if (deprecated) { td = tr.td(); @@ -427,7 +448,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, 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) || hasExtensions; } for (ConceptDefinitionComponent cc : ocl) { tr = t.tr(); From 8ce405cc4dc1c69ab9be225cdfdbf4ae5cfb09bc Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:40:46 +1000 Subject: [PATCH 03/16] special case support for fr-CA language --- .../main/java/org/hl7/fhir/r5/renderers/DataRenderer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 b13bc6fd1..a3531ce7c 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 @@ -168,6 +168,10 @@ public class DataRenderer extends Renderer { } protected String describeLang(String lang) { + // special cases: + if ("fr-CA".equals(lang)) { + return "French (Canadian)"; // this one was omitted from the value set + } ValueSet v = getContext().getWorker().fetchResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages"); if (v != null) { ConceptReferenceComponent l = null; @@ -176,8 +180,9 @@ public class DataRenderer extends Renderer { l = cc; } if (l == null) { - if (lang.contains("-")) + if (lang.contains("-")) { lang = lang.substring(0, lang.indexOf("-")); + } for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { if (cc.getCode().equals(lang) || cc.getCode().startsWith(lang+"-")) l = cc; From 3464643921c5db33687b8bb2f14d450b1503e62d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:41:32 +1000 Subject: [PATCH 04/16] Prevent NPE when auto-generating narrative and an illegal resource type is encountered --- .../fhir/r5/renderers/ProfileDrivenRenderer.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) 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 9f3ebdbdd..4ab610b78 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 @@ -106,13 +106,14 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } try { StructureDefinition sd = r.getDefinition(); - ElementDefinition ed = sd.getSnapshot().getElement().get(0); - if (sd.getType().equals("NamingSystem") && "icd10".equals(r.getId())) { - System.out.println("hah!"); + if (sd == null) { + throw new FHIRException("Cannot find definition for "+r.fhirType()); + } else { + ElementDefinition ed = sd.getSnapshot().getElement().get(0); + containedIds.clear(); + hasExtensions = false; + generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0); } - containedIds.clear(); - hasExtensions = false; - generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), false, 0); } catch (Exception e) { e.printStackTrace(); x.para().b().style("color: maroon").tx("Exception generating Narrative: "+e.getMessage()); From 2e7e277c97e6a1833dd3ea928052fb75803480fd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:42:03 +1000 Subject: [PATCH 05/16] Prevent NPE resolving resource in batch --- .../src/main/java/org/hl7/fhir/r5/renderers/utils/Resolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 94c413b31..84f0a748d 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 @@ -126,7 +126,7 @@ 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 (value.equals(res.fhirType()+"/"+res.getChildValue("id"))) + if (res != null && value.equals(res.fhirType()+"/"+res.getChildValue("id"))) return p; } } From 9d223379299020c071b4ba10ebce9794158c622c Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:42:45 +1000 Subject: [PATCH 06/16] fix value set validation for primitive types when an expansion is provided, and the code system is not known --- .../org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java | 3 +++ 1 file changed, 3 insertions(+) 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 e8f89e701..cbb70ea89 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 @@ -164,6 +164,9 @@ public class ValueSetCheckerSimple implements ValueSetChecker { throw new FHIRException("Unable to evaluate based on empty code system"); } res = validateCode(code, cs); + } else if (cs == null && valueset.hasExpansion() && inExpansion) { + // we just take the value set as face value then + res = new ValidationResult(IssueSeverity.INFORMATION, null); } else { // well, we didn't find a code system - try the expansion? // disabled waiting for discussion From e33ffca7495c3b6d0245f6f0e65f234aa9c867d0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:44:45 +1000 Subject: [PATCH 07/16] FHIRPath engine: correction for allowing boolean conversion of primitive types --- .../org/hl7/fhir/r5/utils/FHIRPathEngine.java | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) 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 609e4ffec..4a71849c6 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 @@ -3853,7 +3853,7 @@ public class FHIRPathEngine { } else {// (exp.getParameters().size() == 0) { boolean all = true; for (Base item : focus) { - Equality eq = asBool(item); + Equality eq = asBool(item, true); if (eq != Equality.True) { all = false; break; @@ -4471,7 +4471,11 @@ public class FHIRPathEngine { } else { boolean all = true; for (Base item : focus) { - Equality v = asBool(item); + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + + Equality v = asBool(item, true); if (v != Equality.False) { all = false; break; @@ -4501,7 +4505,11 @@ public class FHIRPathEngine { } else { boolean any = false; for (Base item : focus) { - Equality v = asBool(item); + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + + Equality v = asBool(item, true); if (v == Equality.False) { any = true; break; @@ -4531,8 +4539,11 @@ public class FHIRPathEngine { } else { boolean all = true; for (Base item : focus) { - Equality v = asBool(item); - if (v != Equality.True) { + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + Equality v = asBool(item, true); + if (v != Equality.True) { all = false; break; } @@ -4561,7 +4572,11 @@ public class FHIRPathEngine { } else { boolean any = false; for (Base item : focus) { - Equality v = asBool(item); + if (!canConvertToBoolean(item)) { + throw new FHIRException("Unable to convert '"+convertToString(item)+"' to a boolean"); + } + + Equality v = asBool(item, true); if (v == Equality.True) { any = true; break; @@ -4572,7 +4587,11 @@ public class FHIRPathEngine { return result; } - private List funcTrace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { + private boolean canConvertToBoolean(Base item) { + return (item.isBooleanPrimitive); + } + + private List funcTrace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { List nl = execute(context, focus, exp.getParameters().get(0), true); String name = nl.get(0).primitiveValue(); if (exp.getParameters().size() == 2) { @@ -5493,8 +5512,10 @@ public class FHIRPathEngine { private Equality asBool(List items) throws PathEngineException { if (items.size() == 0) { return Equality.Null; + } else if (items.size() == 1 && items.get(0).isBooleanPrimitive()) { + return asBool(items.get(0), true); } else if (items.size() == 1) { - return asBool(items.get(0)); + return Equality.True; } else { throw makeException(I18nConstants.FHIRPATH_UNABLE_BOOLEAN, convertToString(items)); } @@ -5528,7 +5549,7 @@ public class FHIRPathEngine { } } - private Equality asBool(Base item) { + private Equality asBool(Base item, boolean narrow) { if (item instanceof BooleanType) { return boolToTriState(((BooleanType) item).booleanValue()); } else if (item.isBooleanPrimitive()) { @@ -5539,6 +5560,8 @@ public class FHIRPathEngine { } else { return Equality.Null; } + } else if (narrow) { + return Equality.False; } else if (item instanceof IntegerType || Utilities.existsInList(item.fhirType(), "integer", "positiveint", "unsignedInt")) { return asBoolFromInt(item.primitiveValue()); } else if (item instanceof DecimalType || Utilities.existsInList(item.fhirType(), "decimal")) { From fa778fbf03ccfe155b6d7b5a7f6e647eca418fc3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 15:45:47 +1000 Subject: [PATCH 08/16] Fix handling resources in bundles when type is profiled --- .../src/main/resources/Messages.properties | 10 +++++----- .../fhir/validation/instance/InstanceValidator.java | 11 ++++++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 8a13d0256..562e34887 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -10,8 +10,8 @@ Bundle_BUNDLE_Entry_NoFullUrl = Bundle entry missing fullUrl Bundle_BUNDLE_Entry_NoProfile = No profile found for contained resource of type ''{0}'' Bundle_BUNDLE_Entry_NotFound = Can''t find ''{0}'' in the bundle ({1}) Bundle_BUNDLE_Entry_Orphan = Entry {0} isn''t reachable by traversing from first Bundle entry -Bundle_BUNDLE_Entry_Type = The type ''{0}'' is not valid - no resources allowed here -Bundle_BUNDLE_Entry_Type2 = The type ''{0}'' is not valid - must be {1} +Bundle_BUNDLE_Entry_Type = The type ''{0}'' is not valid - no resources allowed here (allowed = {1}) +Bundle_BUNDLE_Entry_Type2 = The type ''{0}'' is not valid - must be {1} (allowed = {2}) Bundle_BUNDLE_Entry_Type3 = The type ''{0}'' is not valid - must be one of {1} Bundle_BUNDLE_FullUrl_Missing = Relative Reference appears inside Bundle whose entry is missing a fullUrl Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare meta/versionId because there are version-specific references @@ -156,9 +156,9 @@ Terminology_TX_NoValid_17 = The value provided (''{0}'') is not in the value set 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 = None of the codes provided are in the value set {0} ({1}), and a code should come from this value set unless it has no suitable code) (codes = {2}) Terminology_TX_NoValid_3 = None of the codes provided are in the value set {0} ({1}), and a code is recommended to come from this value set) (codes = {2}) -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{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_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 {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}) 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 5f641c3c9..02aee0df3 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 @@ -3943,15 +3943,17 @@ 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 { String resourceName = element.getType(); TypeRefComponent trr = null; + CommaSeparatedStringBuilder bt = new CommaSeparatedStringBuilder(); for (TypeRefComponent tr : child.getType()) { - if (tr.getCode().equals("Resource")) { + bt.append(tr.getCode()); + if (tr.getCode().equals("Resource") || tr.getCode().equals(resourceName) ) { trr = tr; 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); + 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; @@ -4002,7 +4004,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } private boolean isValidResourceType(String type, TypeRefComponent def) { - if (!def.hasProfile()) { + if (!def.hasProfile() && def.getCode().equals("Resource")) { + return true; + } + if (def.getCode().equals(type)) { return true; } List list = new ArrayList<>(); From 0dcf64cb7028661f52aee6c203dc0382f3d1549f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 17:04:42 +1000 Subject: [PATCH 09/16] Add test cases for wildcard versions --- .../org/hl7/fhir/r5/utils/FHIRPathEngine.java | 2 +- .../hl7/fhir/utilities/VersionUtilities.java | 2 +- .../cache/FilesystemPackageCacheManager.java | 19 +++++++++++-------- .../utilities/tests/PackageCacheTests.java | 10 ++++++++++ 4 files changed, 23 insertions(+), 10 deletions(-) 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 4a71849c6..5b6c553ff 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 @@ -4588,7 +4588,7 @@ public class FHIRPathEngine { } private boolean canConvertToBoolean(Base item) { - return (item.isBooleanPrimitive); + return (item.isBooleanPrimitive()); } private List funcTrace(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException { 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 91e8d46b2..c90530f2a 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 @@ -240,7 +240,7 @@ public class VersionUtilities { public static boolean isMajMinOrLaterPatch(String test, String current) { String t = getMajMin(test); String c = getMajMin(current); - if (c.compareTo(t) == 0) { + if (c != null && c.compareTo(t) == 0) { String pt = getPatch(test); String pc = getPatch(current); if (pt==null || "x".equals(pt)) { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java index 4415901af..4d6b9f11d 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/cache/FilesystemPackageCacheManager.java @@ -311,14 +311,17 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple String foundPackage = null; String foundVersion = null; for (String f : sorted(new File(cacheFolder).list())) { - if (f.equals(id + "#" + version) || (Utilities.noString(version) && f.startsWith(id + "#"))) { - return loadPackageInfo(Utilities.path(cacheFolder, f)); - } - if (version!=null && version.endsWith(".x") && f.contains("#")) { - String[] parts = f.split("#"); - if (parts[0].equals(id) && VersionUtilities.isMajMinOrLaterPatch((foundVersion!=null ? foundVersion : version),parts[1])) { - foundVersion = parts[1]; - foundPackage = f; + File cf = new File(Utilities.path(cacheFolder, f)); + if (cf.isDirectory()) { + if (f.equals(id + "#" + version) || (Utilities.noString(version) && f.startsWith(id + "#"))) { + return loadPackageInfo(Utilities.path(cacheFolder, f)); + } + if (version != null && version.endsWith(".x") && f.contains("#")) { + String[] parts = f.split("#"); + if (parts[0].equals(id) && VersionUtilities.isMajMinOrLaterPatch((foundVersion!=null ? foundVersion : version),parts[1])) { + foundVersion = parts[1]; + foundPackage = f; + } } } } diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java index 4f42035eb..8e8289fbd 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/tests/PackageCacheTests.java @@ -37,4 +37,14 @@ public class PackageCacheTests { list = cache.listPackages(); Assertions.assertFalse(list.isEmpty()); } + + @Test + public void testPatchWildCard() throws IOException { + FilesystemPackageCacheManager cache = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); + cache.clear(); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.1.0").version(), "3.1.0"); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.1.1").version(), "3.1.1"); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.1.x").version(), "3.1.1"); + Assertions.assertEquals(cache.loadPackage("hl7.fhir.us.core", "3.0.x").version(), "3.0.1"); + } } \ No newline at end of file From a47e8060160b6109eba01ab50404be95f029652d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Sep 2020 17:04:59 +1000 Subject: [PATCH 10/16] release notes --- RELEASE_NOTES.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e69de29bb..5c49d3361 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -0,0 +1,11 @@ +Validator: +* Fix handling resources in bundles when type is profiled +* Prevent NPE resolving resource in batch +* fix value set validation for primitive types when an expansion is provided, and the code system is not known + +Other Changes: +* Package Subsystem - Support wildcars for patch version +* Renderer: Don't make a column for definitions in a code system if there are none +* Renderer: special case support for fr-CA language +* Renderer: Prevent NPE when auto-generating narrative and an illegal resource type is encountered +* FHIRPath Engine: correction for allowing boolean conversion of primitive types From 71e4b5d6e52a8456808cb546d62100fc37b8b6a0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 21 Sep 2020 19:53:55 +1000 Subject: [PATCH 11/16] Fix bug producing nested links and producing invalid langauge rendering --- .../hl7/fhir/r5/renderers/DataRenderer.java | 92 +++++++++++++------ 1 file changed, 62 insertions(+), 30 deletions(-) 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 667bd5c15..ce468ce9f 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 @@ -184,8 +184,18 @@ public class DataRenderer extends Renderer { lang = lang.substring(0, lang.indexOf("-")); } for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { - if (cc.getCode().equals(lang) || cc.getCode().startsWith(lang+"-")) + if (cc.getCode().equals(lang)) { l = cc; + break; + } + } + if (l == null) { + for (ConceptReferenceComponent cc : v.getCompose().getIncludeFirstRep().getConcept()) { + if (cc.getCode().startsWith(lang+"-")) { + l = cc; + break; + } + } } } if (l != null) { @@ -690,37 +700,59 @@ public class DataRenderer extends Renderer { protected void renderContactPoint(XhtmlNode x, ContactPoint contact) { if (contact != null) { - switch (contact.getSystem()) { - case EMAIL: - x.ah("mailto:"+contact.getValue()).tx(contact.getValue()); - break; - case FAX: - x.addText(displayContactPoint(contact)); - break; - case NULL: - x.addText(displayContactPoint(contact)); - break; - case OTHER: - x.addText(displayContactPoint(contact)); - break; - case PAGER: - x.addText(displayContactPoint(contact)); - break; - case PHONE: - if (contact.hasValue() && contact.getValue().startsWith("+")) { - x.ah("tel:"+contact.getValue()).tx(contact.getValue()); - } else { + if (!contact.hasSystem()) { + x.addText(displayContactPoint(contact)); + } else { + switch (contact.getSystem()) { + case EMAIL: + x.ah("mailto:"+contact.getValue()).tx(contact.getValue()); + break; + case FAX: x.addText(displayContactPoint(contact)); + break; + case NULL: + x.addText(displayContactPoint(contact)); + break; + case OTHER: + x.addText(displayContactPoint(contact)); + break; + case PAGER: + x.addText(displayContactPoint(contact)); + break; + case PHONE: + if (contact.hasValue() && contact.getValue().startsWith("+")) { + x.ah("tel:"+contact.getValue()).tx(contact.getValue()); + } else { + x.addText(displayContactPoint(contact)); + } + break; + case SMS: + x.addText(displayContactPoint(contact)); + break; + case URL: + x.ah(contact.getValue()).tx(contact.getValue()); + break; + default: + break; + } + } + } + } + + protected void displayContactPoint(XhtmlNode p, ContactPoint c) { + if (c != null) { + if (c.getSystem() == ContactPointSystem.PHONE) { + p.tx("Phone: "+c.getValue()); + } else if (c.getSystem() == ContactPointSystem.FAX) { + p.tx("Fax: "+c.getValue()); + } else if (c.getSystem() == ContactPointSystem.EMAIL) { + p.tx(c.getValue()); + } else if (c.getSystem() == ContactPointSystem.URL) { + if (c.getValue().length() > 30) { + p.addText(c.getValue().substring(0, 30)+"..."); + } else { + p.addText(c.getValue()); } - break; - case SMS: - x.addText(displayContactPoint(contact)); - break; - case URL: - x.ah(contact.getValue()).tx(contact.getValue()); - break; - default: - break; } } } From 17ed666fef2568e6cd51cefdf005da66ae6d8637 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 21 Sep 2020 19:54:45 +1000 Subject: [PATCH 12/16] fix bug with nested links --- .../java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 359432b46..5d1f0797f 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 @@ -488,7 +488,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { renderAddress(x, (Address) e); return true; } else if (e instanceof ContactPoint) { - renderContactPoint(x, (ContactPoint) e); + displayContactPoint(x, (ContactPoint) e); return true; } else if (e instanceof Timing) { renderTiming(x, (Timing) e); @@ -530,7 +530,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { boolean first = true; for (ContactPoint c : cd.getTelecom()) { if (first) first = false; else x.tx(","); - renderContactPoint(x, c); + displayContactPoint(x, c); } return true; } else if (e instanceof Range) { From eba38941d385b2d8dc52829346e74e4eada92f90 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 21 Sep 2020 19:55:51 +1000 Subject: [PATCH 13/16] fix resource leaks --- .../java/org/hl7/fhir/utilities/Utilities.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 2015c3e22..82d06017a 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 @@ -47,6 +47,8 @@ import java.math.RoundingMode; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; import java.util.ArrayList; @@ -320,13 +322,13 @@ public class Utilities { destFile.createNewFile(); } - FileChannel source = null; - FileChannel destination = null; + FileInputStream source = null; + FileOutputStream destination = null; try { - source = new FileInputStream(sourceFile).getChannel(); - destination = new FileOutputStream(destFile).getChannel(); - destination.transferFrom(source, 0, source.size()); + source = new FileInputStream(sourceFile); + destination = new FileOutputStream(destFile); + destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size()); } finally { if (source != null) { source.close(); @@ -398,8 +400,8 @@ public class Utilities { clearDirectory(fh.getAbsolutePath()); fh.delete(); } + } } - } } } } From d07966e4226890c110617020a6eda56ab101bd92 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 22 Sep 2020 02:14:23 +1000 Subject: [PATCH 14/16] remove spaces from generated tel: links --- .../src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ce468ce9f..ac04d007d 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 @@ -721,7 +721,7 @@ public class DataRenderer extends Renderer { break; case PHONE: if (contact.hasValue() && contact.getValue().startsWith("+")) { - x.ah("tel:"+contact.getValue()).tx(contact.getValue()); + x.ah("tel:"+contact.getValue().replace(" ", "")).tx(contact.getValue()); } else { x.addText(displayContactPoint(contact)); } From a15be7d31405fc50b0a276eac0c301be55f97a7f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 22 Sep 2020 02:14:55 +1000 Subject: [PATCH 15/16] allow links when ok --- .../hl7/fhir/r5/renderers/ProfileDrivenRenderer.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 5d1f0797f..155e8bbc0 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 @@ -488,7 +488,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer { renderAddress(x, (Address) e); return true; } else if (e instanceof ContactPoint) { - displayContactPoint(x, (ContactPoint) e); + if (allowLinks) { + renderContactPoint(x, (ContactPoint) e); + } else { + displayContactPoint(x, (ContactPoint) e); + } return true; } else if (e instanceof Timing) { renderTiming(x, (Timing) e); @@ -530,7 +534,11 @@ public class ProfileDrivenRenderer extends ResourceRenderer { boolean first = true; for (ContactPoint c : cd.getTelecom()) { if (first) first = false; else x.tx(","); - displayContactPoint(x, c); + if (allowLinks) { + renderContactPoint(x, c); + } else { + displayContactPoint(x, c); + } } return true; } else if (e instanceof Range) { From 3c0b20eb16128252934d6f2d0101a34a9cf32d7d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 22 Sep 2020 02:34:04 +1000 Subject: [PATCH 16/16] Add quality code (yet to turn it on) --- .../hl7/fhir/utilities/xhtml/XhtmlNode.java | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) 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 9a0d0cba7..e561009f2 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 @@ -90,6 +90,9 @@ public class XhtmlNode implements IBaseXhtml { private List childNodes = new ArrayList(); private String content; private boolean notPretty; + private boolean inPara; + private boolean inLink; + public XhtmlNode() { super(); @@ -144,7 +147,7 @@ public class XhtmlNode implements IBaseXhtml { return this; } - public void validate(List errors, String path, boolean inResource, boolean inPara) { + public void validate(List errors, String path, boolean inResource, boolean inPara, boolean inLink) { if (nodeType == NodeType.Element || nodeType == NodeType.Document) { path = Utilities.noString(path) ? name : path+"/"+name; if (inResource) { @@ -172,13 +175,19 @@ public class XhtmlNode implements IBaseXhtml { if (inPara && Utilities.existsInList(name, "div", "blockquote", "table", "ol", "ul", "p")) { errors.add("Error at "+path+": Found "+name+" inside an html paragraph"); } + if (inLink && Utilities.existsInList(name, "a")) { + errors.add("Error at "+path+": Found an inside an paragraph"); + } if (childNodes != null) { if ("p".equals(name)) { inPara = true; } + if ("a".equals(name)) { + inLink = true; + } for (XhtmlNode child : childNodes) { - child.validate(errors, path, inResource, inPara); + child.validate(errors, path, inResource, inPara, inLink); } } } @@ -191,8 +200,20 @@ public class XhtmlNode implements IBaseXhtml { throw new Error("Wrong node type - node is "+nodeType.toString()+" ('"+getName()+"/"+getContent()+"')"); } +// if (inPara && name.equals("p")) { +// throw new FHIRException("nested Para"); +// } +// if (inLink && name.equals("a")) { +// throw new FHIRException("Nested Link"); +// } XhtmlNode node = new XhtmlNode(NodeType.Element); node.setName(name); + if (inPara || name.equals("p")) { + node.inPara = true; + } + if (inLink || name.equals("a")) { + node.inLink = true; + } childNodes.add(node); return node; } @@ -203,6 +224,12 @@ public class XhtmlNode implements IBaseXhtml { if (!(nodeType == NodeType.Element || nodeType == NodeType.Document)) throw new Error("Wrong node type. is "+nodeType.toString()); XhtmlNode node = new XhtmlNode(NodeType.Element); + if (inPara || name.equals("p")) { + node.inPara = true; + } + if (inLink || name.equals("a")) { + node.inLink = true; + } node.setName(name); childNodes.add(index, node); return node;