From d67cecc83f7718f61def6fb8ad1dee1aaf02eb2e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Jun 2021 16:55:15 +1000 Subject: [PATCH 1/2] * CDA: Fix erroneous type validation on CDA templates * CDA: Suppress erroneous "Expansion" text appearing in view * CDA: Don't delete binding information in snapshot for CDA bindable data types * Rendering: add support for CodeableReference * Rendering: Support binding mode and XML element information --- .../fhir/r5/conformance/ProfileUtilities.java | 39 ++++++++++++++++++- .../fhir/r5/context/BaseWorkerContext.java | 2 +- .../fhir/r5/context/SimpleWorkerContext.java | 2 +- .../hl7/fhir/r5/renderers/DataRenderer.java | 10 +++++ .../r5/renderers/ProfileDrivenRenderer.java | 6 ++- .../hl7/fhir/r5/utils/ToolingExtensions.java | 3 ++ .../hl7/fhir/utilities/VersionUtilities.java | 7 +++- .../type/StructureDefinitionValidator.java | 30 +++++++------- 8 files changed, 79 insertions(+), 20 deletions(-) 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 222732365..cff6e4442 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 @@ -1,5 +1,9 @@ package org.hl7.fhir.r5.conformance; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; + /* Copyright (c) 2011+, HL7, Inc. All rights reserved. @@ -2226,7 +2230,8 @@ public class ProfileUtilities extends TranslatingUtilities { private boolean checkExtensionDoco(ElementDefinition base) { // see task 3970. For an extension, there's no point copying across all the underlying definitional stuff - boolean isExtension = base.getPath().equals("Extension") || base.getPath().endsWith(".extension") || base.getPath().endsWith(".modifierExtension"); + boolean isExtension = (base.getPath().equals("Extension") || base.getPath().endsWith(".extension") || base.getPath().endsWith(".modifierExtension")) && + (!base.hasBase() || !"II.extension".equals(base.getBase().getPath())); if (isExtension) { base.setDefinition("An Extension"); base.setShort("Extension"); @@ -3086,6 +3091,10 @@ public class ProfileUtilities extends TranslatingUtilities { if (Utilities.existsInList(tr.getWorkingCode(), "Coding", "CodeableConcept", "Quantity", "uri", "string", "code")) { return true; } + StructureDefinition sd = context.fetchTypeDefinition(tr.getCode()); + if (sd != null && sd.hasExtension(ToolingExtensions.EXT_BINDING_STYLE)) { + return true; + } } return false; } @@ -4496,6 +4505,31 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(gen.new Piece(null, translate("sd.table", "Slice")+": ", null).addStyle("font-weight:bold")); c.getPieces().add(gen.new Piece(null, describeSlice(definition.getSlicing()), null)); } + if (!definition.getPath().contains(".") && ToolingExtensions.hasExtension(profile, ToolingExtensions.EXT_BINDING_STYLE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, "This type can be bound to a value set using the ", null)); + c.getPieces().add(gen.new Piece(null, ToolingExtensions.readStringExtension(profile, ToolingExtensions.EXT_BINDING_STYLE), null)); + c.getPieces().add(gen.new Piece(null, " binding style", null)); + + } + if (definition.hasExtension(ToolingExtensions.EXT_XML_NAME)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); + c.getPieces().add(gen.new Piece(null, " (", null)); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); + c.getPieces().add(gen.new Piece(null, ")", null)); + } else { + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Element Name")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAME), null)); + } + } else if (definition.hasExtension(ToolingExtensions.EXT_XML_NAMESPACE)) { + if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } + c.getPieces().add(gen.new Piece(null, translate("sd.table", "XML Namespace")+": ", null).addStyle("font-weight:bold")); + c.getPieces().add(gen.new Piece(null, definition.getExtensionString(ToolingExtensions.EXT_XML_NAMESPACE), null)); + } if (definition != null) { ElementDefinitionBindingComponent binding = null; if (valueDefn != null && valueDefn.hasBinding() && !valueDefn.getBinding().isEmpty()) @@ -4742,8 +4776,9 @@ public class ProfileUtilities extends TranslatingUtilities { String s = b.primitiveValue(); // ok. let's see if we can find a relevant link for this String link = null; - if (Utilities.isAbsoluteUrl(s)) + if (Utilities.isAbsoluteUrl(s)) { link = pkp.getLinkForUrl(corePath, s); + } c.getPieces().add(gen.new Piece(link, s, null).addStyle("color: darkgreen")); } else { c = gen.new Cell(); 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 f2d8d6f02..eef7cf3d0 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 @@ -1209,7 +1209,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte if (class_ == Resource.class || class_ == null) { if (structures.has(uri)) { return (T) structures.get(uri, version); - } + } if (guides.has(uri)) { return (T) guides.get(uri, version); } 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 9c1108c93..0eaabce29 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 @@ -408,7 +408,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon for (String e : pi.dependencies()) { if (!loadedPackages.contains(e) && !VersionUtilities.isCorePackage(e)) { NpmPackage npm = pcm.loadPackage(e); - if (!version.equals(npm.fhirVersion())) { + if (!VersionUtilities.versionsMatch(version, npm.fhirVersion())) { System.out.println(formatMessage(I18nConstants.PACKAGE_VERSION_MISMATCH, e, version, npm.fhirVersion(), path)); } t = t + loadFromPackageAndDependenciesInt(npm, loader.getNewLoader(npm), pcm, path+" -> "+npm.name()+"#"+npm.version()); 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 409a6d681..e8864e860 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 @@ -17,6 +17,7 @@ import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeableConcept; +import org.hl7.fhir.r5.model.CodeableReference; import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.ContactPoint; import org.hl7.fhir.r5.model.DataRequirement; @@ -552,6 +553,15 @@ public class DataRenderer extends Renderer { renderCodeableConcept(x, cc, false); } + protected void renderCodeableReference(XhtmlNode x, CodeableReference e, boolean showCodeDetails) { + if (e.hasConcept()) { + renderCodeableConcept(x, e.getConcept(), showCodeDetails); + } + if (e.hasReference()) { + renderReference(x, e.getReference()); + } + } + protected void renderCodeableConcept(XhtmlNode x, CodeableConcept cc, boolean showCodeDetails) { if (cc.isEmpty()) { return; 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 4a40f4f6d..c7420060a 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 @@ -306,12 +306,14 @@ public class ProfileDrivenRenderer extends ResourceRenderer { 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) + } else if (e instanceof BooleanType) { x.addText(((BooleanType) e).getValue().toString()); - else if (e instanceof CodeableConcept) { + } else if (e instanceof CodeableConcept) { renderCodeableConcept(x, (CodeableConcept) e, showCodeDetails); } else if (e instanceof Coding) { renderCoding(x, (Coding) e, showCodeDetails); + } else if (e instanceof CodeableReference) { + renderCodeableReference(x, (CodeableReference) e, showCodeDetails); } else if (e instanceof Annotation) { renderAnnotation(x, (Annotation) e); } else if (e instanceof Identifier) { 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 7b23d2990..0c78f86d6 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 @@ -184,6 +184,9 @@ public class ToolingExtensions { public static final String EXT_TRANSLATABLE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-translatable"; public static final String EXT_PATTERN = "http://hl7.org/fhir/StructureDefinition/elementdefinition-pattern"; public static final String EXT_BINDING_METHOD = "http://hl7.org/fhir/StructureDefinition/elementdefinition-binding-method"; + public static final String EXT_XML_NAMESPACE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-namespace"; + 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"; // specific extension helpers 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 35b83bfbb..ce706d0fe 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 @@ -452,7 +452,6 @@ public class VersionUtilities { res.add("TerminologyCapabilities"); res.add("TestScript"); res.add("ValueSet"); - } return res; } @@ -465,5 +464,11 @@ public class VersionUtilities { return null; } + public static boolean versionsMatch(String v1, String v2) { + String mm1 = getMajMin(v1); + String mm2 = getMajMin(v2); + return mm1 != null && mm2 != null && mm1.equals(mm2); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index 29faccefb..3d0f93c37 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -245,9 +245,10 @@ public class StructureDefinitionValidator extends BaseValidator { StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); if (code.equals("Reference")) { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { - String t = determineBaseType(sd); + StructureDefinition t = determineBaseType(sd); if (t == null) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + throw new Error("What to do about this?"); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); } @@ -257,9 +258,10 @@ public class StructureDefinitionValidator extends BaseValidator { sd = getXverExt(errors, stack.getLiteralPath(), profile, p); } if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { - String t = determineBaseType(sd); + StructureDefinition t = determineBaseType(sd); if (t == null) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + throw new Error("What to do about this?"); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); } @@ -288,9 +290,9 @@ public class StructureDefinitionValidator extends BaseValidator { sd = getXverExt(errors, stack.getLiteralPath(), profile, p); } if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { - String t = determineBaseType(sd); + StructureDefinition t = determineBaseType(sd); if (t == null) { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), isInstanceOf(t, code), I18nConstants.SD_ED_TYPE_PROFILE_WRONG, p, t, code, path); } @@ -302,18 +304,18 @@ public class StructureDefinitionValidator extends BaseValidator { StructureDefinition sd = context.fetchResource(StructureDefinition.class, p); if (code.equals("Reference")) { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { - String t = determineBaseType(sd); + StructureDefinition t = determineBaseType(sd); if (t == null) { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd.getKind() == StructureDefinitionKind.RESOURCE, I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Resource"); } } } else if (code.equals("canonical")) { if (warning(errors, IssueType.EXCEPTION, stack.getLiteralPath(), sd != null, I18nConstants.SD_ED_TYPE_PROFILE_UNKNOWN, p)) { - String t = determineBaseType(sd); + StructureDefinition t = determineBaseType(sd); if (t == null) { - rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), code.equals(t), I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); + rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.SD_ED_TYPE_PROFILE_NOTYPE, p); } else if (!VersionUtilities.isR5Ver(context.getVersion())) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), VersionUtilities.getCanonicalResourceNames(context.getVersion()).contains(t) || "Resource".equals(t), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TARGET, p, t, code, path, "Canonical Resource"); } else { @@ -325,14 +327,16 @@ public class StructureDefinitionValidator extends BaseValidator { } } - private boolean isInstanceOf(String t, String code) { - StructureDefinition sd = context.fetchTypeDefinition(t); + private boolean isInstanceOf(StructureDefinition sd, String code) { while (sd != null) { if (sd.getType().equals(code)) { return true; } + if (sd.getUrl().equals(code)) { + return true; + } sd = sd.hasBaseDefinition() ? context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()) : null; - if (!(VersionUtilities.isR2Ver(context.getVersion()) || VersionUtilities.isR2BVer(context.getVersion())) && sd != null && !sd.getAbstract()) { + if (!(VersionUtilities.isR2Ver(context.getVersion()) || VersionUtilities.isR2BVer(context.getVersion())) && sd != null && !sd.getAbstract() && sd.getKind() != StructureDefinitionKind.LOGICAL) { sd = null; } } @@ -340,11 +344,11 @@ public class StructureDefinitionValidator extends BaseValidator { return false; } - private String determineBaseType(StructureDefinition sd) { + private StructureDefinition determineBaseType(StructureDefinition sd) { while (sd != null && !sd.hasType() && sd.getDerivation() == TypeDerivationRule.CONSTRAINT) { sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); } - return sd == null ? null : sd.getType(); + return sd; } private boolean hasMustSupportExtension(Element type) { From a77ae61616132494d248ebe9cbb72bb55a70d62d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 17 Jun 2021 16:55:56 +1000 Subject: [PATCH 2/2] release notes --- RELEASE_NOTES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d0fa12e68..e3752f3d6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,11 @@ Validator: * don't fail with an exception if an unknown resource type appears in contained resource * improved validation for some value sets that are based on unknown code systems * add the -verbose parameter, and add additional verbose messages +* CDA: Fix erroneous type validation on CDA templates + +Snapshot generator: +* CDA: Suppress erroneous "Expansion" text appearing in view +* CDA: Don't delete binding information in snapshot for CDA bindable data types Other code changes: @@ -11,4 +16,7 @@ Other code changes: * Fix rendering for most resources - remove empty tables (e.g. text element, that shouldn't render) * Fix NPE rendering code systems with some kinds of properties * Improve rendering of questionnaires (icons, option sets) +* Rendering: add support for CodeableReference +* Rendering: Support binding mode and XML element information +