Merge pull request #539 from hapifhir/gg-20210617-cda

* CDA: Fix erroneous type validation on CDA templates
This commit is contained in:
Grahame Grieve 2021-06-17 17:23:48 +10:00 committed by GitHub
commit 4915f5ccb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 20 deletions

View File

@ -4,14 +4,22 @@ 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
Conversion code:
* Ignoring abatementBoolean when converting from dstu2 to r4
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:
* Fix rendering of slices so type on slicer is not hidden
* 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

View File

@ -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();

View File

@ -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);
}

View File

@ -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());

View File

@ -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;

View File

@ -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) {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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) {