Fix various issues rendering and validating additional bindings with usage contexts

This commit is contained in:
Grahame Grieve 2024-09-21 00:35:01 -04:00
parent eab8276753
commit 8cfcbf389e
12 changed files with 519 additions and 74 deletions

View File

@ -25,8 +25,10 @@ import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.CodeType;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent;
import org.hl7.fhir.r5.model.UsageContext; import org.hl7.fhir.r5.model.UsageContext;
import org.hl7.fhir.r5.utils.ToolingExtensions;
public class ElementDefinition40_50 { public class ElementDefinition40_50 {
public static org.hl7.fhir.r5.model.ElementDefinition convertElementDefinition(org.hl7.fhir.r4.model.ElementDefinition src) throws FHIRException { public static org.hl7.fhir.r5.model.ElementDefinition convertElementDefinition(org.hl7.fhir.r4.model.ElementDefinition src) throws FHIRException {
if (src == null) return null; if (src == null) return null;
org.hl7.fhir.r5.model.ElementDefinition tgt = new org.hl7.fhir.r5.model.ElementDefinition(); org.hl7.fhir.r5.model.ElementDefinition tgt = new org.hl7.fhir.r5.model.ElementDefinition();
@ -616,7 +618,7 @@ public class ElementDefinition40_50 {
if (src == null) return null; if (src == null) return null;
org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent tgt = new org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent(); org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent tgt = new org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent();
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt, ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt,
"http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional"); "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional", ToolingExtensions.EXT_BINDING_ADDITIONAL);
if (src.hasStrength()) tgt.setStrengthElement(Enumerations40_50.convertBindingStrength(src.getStrengthElement())); if (src.hasStrength()) tgt.setStrengthElement(Enumerations40_50.convertBindingStrength(src.getStrengthElement()));
if (src.hasDescription()) tgt.setDescriptionElement(String40_50.convertStringToMarkdown(src.getDescriptionElement())); if (src.hasDescription()) tgt.setDescriptionElement(String40_50.convertStringToMarkdown(src.getDescriptionElement()));
if (src.hasValueSet()) tgt.setValueSetElement(Canonical40_50.convertCanonical(src.getValueSetElement())); if (src.hasValueSet()) tgt.setValueSetElement(Canonical40_50.convertCanonical(src.getValueSetElement()));
@ -624,6 +626,9 @@ public class ElementDefinition40_50 {
for (org.hl7.fhir.r4.model.Extension ext : src.getExtensionsByUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional")) { for (org.hl7.fhir.r4.model.Extension ext : src.getExtensionsByUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional")) {
tgt.addAdditional(convertAdditional(ext)); tgt.addAdditional(convertAdditional(ext));
} }
for (org.hl7.fhir.r4.model.Extension ext : src.getExtensionsByUrl(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
tgt.addAdditional(convertAdditional(ext));
}
return tgt; return tgt;
} }
@ -652,12 +657,12 @@ public class ElementDefinition40_50 {
return tgt; return tgt;
} }
private static Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) { private static org.hl7.fhir.r4.model.Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) {
if (src == null) return null; if (src == null) return null;
Extension tgt = new Extension(); org.hl7.fhir.r4.model.Extension tgt = new Extension(ToolingExtensions.EXT_BINDING_ADDITIONAL);
ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt); ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt);
if (src.hasPurpose()) { if (src.hasPurpose()) {
tgt.addExtension(new Extension("purpose", new CodeType(src.getPurposeElement().primitiveValue()))); tgt.addExtension(new Extension("purpose", new org.hl7.fhir.r4.model.CodeType(src.getPurposeElement().primitiveValue())));
} }
if (src.hasValueSet()) { if (src.hasValueSet()) {
tgt.addExtension(new Extension("valueSet", Canonical40_50.convertCanonical(src.getValueSetElement()))); tgt.addExtension(new Extension("valueSet", Canonical40_50.convertCanonical(src.getValueSetElement())));

View File

@ -2923,7 +2923,7 @@ public class ProfileUtilities {
private ElementDefinitionBindingAdditionalComponent getMatchingAdditionalBinding(ElementDefinitionBindingComponent nb,ElementDefinitionBindingAdditionalComponent ab) { private ElementDefinitionBindingAdditionalComponent getMatchingAdditionalBinding(ElementDefinitionBindingComponent nb,ElementDefinitionBindingAdditionalComponent ab) {
for (ElementDefinitionBindingAdditionalComponent t : nb.getAdditional()) { for (ElementDefinitionBindingAdditionalComponent t : nb.getAdditional()) {
if (t.getValueSet() != null && t.getValueSet().equals(ab.getValueSet()) && t.getPurpose() == ab.getPurpose()) { if (t.getValueSet() != null && t.getValueSet().equals(ab.getValueSet()) && t.getPurpose() == ab.getPurpose() && !ab.hasUsage()) {
return t; return t;
} }
} }

View File

@ -36,7 +36,7 @@ public class AdditionalBindingsRenderer {
private String valueSet; private String valueSet;
private String doco; private String doco;
private String docoShort; private String docoShort;
private UsageContext usage; private List<UsageContext> usages = new ArrayList<UsageContext>();
private boolean any = false; private boolean any = false;
private boolean isUnchanged = false; private boolean isUnchanged = false;
private boolean matched = false; private boolean matched = false;
@ -72,7 +72,7 @@ public class AdditionalBindingsRenderer {
isUnchanged = isUnchanged && ((valueSet==null && compare.valueSet==null) || valueSet.equals(compare.valueSet)); isUnchanged = isUnchanged && ((valueSet==null && compare.valueSet==null) || valueSet.equals(compare.valueSet));
isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco)); isUnchanged = isUnchanged && ((doco==null && compare.doco==null) || doco.equals(compare.doco));
isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort)); isUnchanged = isUnchanged && ((docoShort==null && compare.docoShort==null) || docoShort.equals(compare.docoShort));
isUnchanged = isUnchanged && ((usage==null && compare.usage==null) || usage.equals(compare.usage)); isUnchanged = isUnchanged && ((usages==null && compare.usages==null) || usages.equals(compare.usages));
return isUnchanged; return isUnchanged;
} }
} }
@ -175,7 +175,11 @@ public class AdditionalBindingsRenderer {
abr.valueSet = ext.getExtensionString("valueSet"); abr.valueSet = ext.getExtensionString("valueSet");
abr.doco = ext.getExtensionString("documentation"); abr.doco = ext.getExtensionString("documentation");
abr.docoShort = ext.getExtensionString("shortDoco"); abr.docoShort = ext.getExtensionString("shortDoco");
abr.usage = (ext.hasExtension("usage")) && ext.getExtensionByUrl("usage").hasValueUsageContext() ? ext.getExtensionByUrl("usage").getValueUsageContext() : null; for (Extension x : ext.getExtensionsByUrl("usage")) {
if (x.hasValueUsageContext()) {
abr.usages.add(x.getValueUsageContext());
}
}
abr.any = "any".equals(ext.getExtensionString("scope")); abr.any = "any".equals(ext.getExtensionString("scope"));
abr.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS); abr.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS);
return abr; return abr;
@ -187,7 +191,7 @@ public class AdditionalBindingsRenderer {
abr.valueSet = ab.getValueSet(); abr.valueSet = ab.getValueSet();
abr.doco = ab.getDocumentation(); abr.doco = ab.getDocumentation();
abr.docoShort = ab.getShortDoco(); abr.docoShort = ab.getShortDoco();
abr.usage = ab.hasUsage() ? ab.getUsageFirstRep() : null; abr.usages.addAll(ab.getUsage());
abr.any = ab.getAny(); abr.any = ab.getAny();
abr.isUnchanged = ab.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS); abr.isUnchanged = ab.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS);
return abr; return abr;
@ -220,7 +224,7 @@ public class AdditionalBindingsRenderer {
boolean any = false; boolean any = false;
for (AdditionalBindingDetail binding : bindings) { for (AdditionalBindingDetail binding : bindings) {
doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null); doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null);
usage = usage || binding.usage != null || (binding.compare!=null && binding.compare.usage!=null); usage = usage || !binding.usages.isEmpty() || (binding.compare!=null && !binding.compare.usages.isEmpty());
any = any || binding.any || (binding.compare!=null && binding.compare.any); any = any || binding.any || (binding.compare!=null && binding.compare.any);
} }
@ -283,9 +287,12 @@ public class AdditionalBindingsRenderer {
renderPurpose(purpose, binding.compare.purpose); renderPurpose(purpose, binding.compare.purpose);
} }
if (usage) { if (usage) {
if (binding.usage != null) { if (!binding.usages.isEmpty()) {
// TODO: This isn't rendered at all yet. Ideally, we want it to render with comparison... XhtmlNode td = tr.td();
new DataRenderer(context).renderBase(new RenderingStatus(), tr.td(), binding.usage); for (UsageContext uc : binding.usages) {
td.sep(", ");
new DataRenderer(context).renderBase(new RenderingStatus(), td, uc);
}
} else { } else {
tr.td(); tr.td();
} }

View File

@ -1302,6 +1302,10 @@ public class DataRenderer extends Renderer implements CodeResolver {
} }
protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c) { protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c) {
renderCoding(status, x, c, true);
}
protected void renderCoding(RenderingStatus status, XhtmlNode x, ResourceWrapper c, boolean details) {
String s = ""; String s = "";
if (c.has("display")) if (c.has("display"))
s = context.getTranslated(c.child("display")); s = context.getTranslated(c.child("display"));
@ -1311,11 +1315,14 @@ public class DataRenderer extends Renderer implements CodeResolver {
if (Utilities.noString(s)) if (Utilities.noString(s))
s = c.primitiveValue("code"); s = c.primitiveValue("code");
if (context.isTechnicalMode()) { if (context.isTechnicalMode() && details) {
x.addText(s+" "+context.formatPhrase(RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.primitiveValue("system")), c.primitiveValue("code"), " = '", lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")), c.primitiveValue("display"), "')")); String d = c.primitiveValue("display") == null ? lookupCode(c.primitiveValue("system"), c.primitiveValue("version"), c.primitiveValue("code")): c.primitiveValue("display");
} else d = context.formatPhrase(d == null || d.equals(c.primitiveValue("code")) ? RenderingContext.DATA_REND_DETAILS_STATED_ND : RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.primitiveValue("system")), c.primitiveValue("code"), d);
x.addText(s+" "+d);
} else {
x.span(null, "{"+c.primitiveValue("system")+" "+c.primitiveValue("code")+"}").addText(s); x.span(null, "{"+c.primitiveValue("system")+" "+c.primitiveValue("code")+"}").addText(s);
} }
}
public String displayCodeableConcept(ResourceWrapper cc) { public String displayCodeableConcept(ResourceWrapper cc) {
String s = context.getTranslated(cc.child("Text")); String s = context.getTranslated(cc.child("Text"));
@ -1860,8 +1867,8 @@ public class DataRenderer extends Renderer implements CodeResolver {
} }
public void renderUsageContext(RenderingStatus status, XhtmlNode x, ResourceWrapper u) throws FHIRFormatError, DefinitionException, IOException { public void renderUsageContext(RenderingStatus status, XhtmlNode x, ResourceWrapper u) throws FHIRFormatError, DefinitionException, IOException {
renderCoding(status, x, u.child("code")); renderCoding(status, x, u.child("code"), false);
x.tx(": "); x.tx(" = ");
renderDataType(status, x, u.child("value")); renderDataType(status, x, u.child("value"));
} }

View File

@ -18,7 +18,9 @@ public class I18nConstants {
public static final String BAD_FILE_PATH_ERROR = "Bad_file_path_error"; public static final String BAD_FILE_PATH_ERROR = "Bad_file_path_error";
public static final String BASE_PROFILE__HAS_NO_TYPE = "Base_profile__has_no_type"; public static final String BASE_PROFILE__HAS_NO_TYPE = "Base_profile__has_no_type";
public static final String BASE__DERIVED_PROFILES_HAVE_DIFFERENT_TYPES____VS___ = "Base__Derived_profiles_have_different_types____vs___"; public static final String BASE__DERIVED_PROFILES_HAVE_DIFFERENT_TYPES____VS___ = "Base__Derived_profiles_have_different_types____vs___";
public static final String BINDING_ADDITIONAL = "BINDING_ADDITIONAL"; public static final String BINDING_ADDITIONAL_D = "BINDING_ADDITIONAL_D";
public static final String BINDING_ADDITIONAL_UC = "BINDING_ADDITIONAL_UC";
public static final String BINDING_ADDITIONAL_USAGE = "BINDING_ADDITIONAL_USAGE";
public static final String BINDING_MAX = "BINDING_MAX"; public static final String BINDING_MAX = "BINDING_MAX";
public static final String BUNDLE_BUNDLE_ENTRY_CANONICAL = "Bundle_BUNDLE_Entry_Canonical"; public static final String BUNDLE_BUNDLE_ENTRY_CANONICAL = "Bundle_BUNDLE_Entry_Canonical";
public static final String BUNDLE_BUNDLE_ENTRY_DOCUMENT = "Bundle_BUNDLE_Entry_Document"; public static final String BUNDLE_BUNDLE_ENTRY_DOCUMENT = "Bundle_BUNDLE_Entry_Document";
@ -1111,4 +1113,7 @@ public class I18nConstants {
public static final String SD_BASE_EXPERIMENTAL = "SD_BASE_EXPERIMENTAL"; public static final String SD_BASE_EXPERIMENTAL = "SD_BASE_EXPERIMENTAL";
public static final String SD_ED_EXPERIMENTAL_BINDING = "SD_ED_EXPERIMENTAL_BINDING"; public static final String SD_ED_EXPERIMENTAL_BINDING = "SD_ED_EXPERIMENTAL_BINDING";
public static final String VALIDATION_NO_EXPERIMENTAL_CONTENT = "VALIDATION_NO_EXPERIMENTAL_CONTENT"; public static final String VALIDATION_NO_EXPERIMENTAL_CONTENT = "VALIDATION_NO_EXPERIMENTAL_CONTENT";
public static final String SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN = "SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN";
public static final String SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT = "SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT";
public static final String SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE = "SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE";
} }

View File

@ -214,6 +214,7 @@ public class RenderingI18nContext extends I18nBase {
public static final String DATA_REND_DATA = "DATA_REND_DATA"; public static final String DATA_REND_DATA = "DATA_REND_DATA";
public static final String DATA_REND_DETAILS = "DATA_REND_DETAILS"; public static final String DATA_REND_DETAILS = "DATA_REND_DETAILS";
public static final String DATA_REND_DETAILS_STATED = "DATA_REND_DETAILS_STATED"; public static final String DATA_REND_DETAILS_STATED = "DATA_REND_DETAILS_STATED";
public static final String DATA_REND_DETAILS_STATED_ND = "DATA_REND_DETAILS_STATED_ND";
public static final String DATA_REND_DICOM = "DATA_REND_DICOM"; public static final String DATA_REND_DICOM = "DATA_REND_DICOM";
public static final String DATA_REND_DIM = "DATA_REND_DIM"; public static final String DATA_REND_DIM = "DATA_REND_DIM";
public static final String DATA_REND_DURATION = "DATA_REND_DURATION"; public static final String DATA_REND_DURATION = "DATA_REND_DURATION";

View File

@ -13,7 +13,9 @@ Attempt_to_a_slice_an_element_that_does_not_repeat__from__in_ = Attempt to a sli
Attempt_to_replace_element_name_for_a_nonchoice_type=Attempt to replace element name for a non-choice type Attempt_to_replace_element_name_for_a_nonchoice_type=Attempt to replace element name for a non-choice type
Attempt_to_use_Terminology_server_when_no_Terminology_server_is_available = Attempt to use Terminology server when no Terminology server is available Attempt_to_use_Terminology_server_when_no_Terminology_server_is_available = Attempt to use Terminology server when no Terminology server is available
Attempt_to_use_a_snapshot_on_profile__as__before_it_is_generated = Attempt to use a snapshot on profile ''{0}'' as {1} before it is generated Attempt_to_use_a_snapshot_on_profile__as__before_it_is_generated = Attempt to use a snapshot on profile ''{0}'' as {1} before it is generated
BINDING_ADDITIONAL = {0} specified in an additional binding BINDING_ADDITIONAL_D = {0} specified in an additional binding
BINDING_ADDITIONAL_UC = {0} specified in an additional binding which applies because {1}
BINDING_ADDITIONAL_USAGE = {0} = {1}
BINDING_MAX = {0} specified in the max binding BINDING_MAX = {0} specified in the max binding
BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE = Found {0} matches for ''{1}'' in the bundle ({2}) BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE = Found {0} matches for ''{1}'' in the bundle ({2})
BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE_FRAGMENT = Found {0} matches for fragment {2} in resource ''{1}'' in the bundle ({3}) BUNDLE_BUNDLE_ENTRY_FOUND_MULTIPLE_FRAGMENT = Found {0} matches for fragment {2} in resource ''{1}'' in the bundle ({3})
@ -1143,3 +1145,6 @@ NO_VALID_DISPLAY_AT_ALL = Cannot validate display Name ''{0}'' for {1}#{2}: No d
SD_BASE_EXPERIMENTAL = The definition builds on ''{0}'' which is experimental, but this definition is not labeled as experimental SD_BASE_EXPERIMENTAL = The definition builds on ''{0}'' which is experimental, but this definition is not labeled as experimental
SD_ED_EXPERIMENTAL_BINDING = The definition for the element ''{0}'' binds to the value set ''{1}'' which is experimental, but this structure is not labeled as experimental SD_ED_EXPERIMENTAL_BINDING = The definition for the element ''{0}'' binds to the value set ''{1}'' which is experimental, but this structure is not labeled as experimental
VALIDATION_NO_EXPERIMENTAL_CONTENT = Experimental content is not allowed in this context VALIDATION_NO_EXPERIMENTAL_CONTENT = Experimental content is not allowed in this context
SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN = The Usage Context {0}#{1} is not recognised and may not be correct
SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT = The Usage Context {0}#{1} is a reference to an element that does not exist
SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE = The Usage Context value must be of type {1} not {0}

View File

@ -207,7 +207,8 @@ DATA_REND_COND = Condition
DATA_REND_COUNT = Count {0} DATA_REND_COUNT = Count {0}
DATA_REND_DATA = Data: {0} DATA_REND_DATA = Data: {0}
DATA_REND_DETAILS = (Details: {0} code DATA_REND_DETAILS = (Details: {0} code
DATA_REND_DETAILS_STATED = (Details: {0} code {1} {2} {3} '', stated as '' {4} {5} DATA_REND_DETAILS_STATED = (Details: {0} code {1} = ''{2}'')
DATA_REND_DETAILS_STATED_ND = (Details: {0} code {1})
DATA_REND_DICOM = DICOM DATA_REND_DICOM = DICOM
DATA_REND_DIM = Dimensions: {0} DATA_REND_DIM = Dimensions: {0}
DATA_REND_DURATION = Duration {0} DATA_REND_DURATION = Duration {0}

View File

@ -217,6 +217,7 @@ import org.hl7.fhir.validation.instance.type.ValueSetValidator;
import org.hl7.fhir.validation.instance.utils.*; import org.hl7.fhir.validation.instance.utils.*;
import org.w3c.dom.Document; import org.w3c.dom.Document;
/** /**
* Thinking of using this in a java program? Don't! * Thinking of using this in a java program? Don't!
* You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine * You should use one of the wrappers instead. Either in HAPI, or use ValidationEngine
@ -1356,19 +1357,20 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength(); BindingStrength strength = binding.getStrength();
Extension maxVS = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); Extension maxVS = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, maxVS, true); checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, maxVS, true, null);
// } else if (binding.hasValueSet()) { // } else if (binding.hasValueSet()) {
// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); // hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
} else if (!noBindingMsgSuppressed) { } else if (!noBindingMsgSuppressed) {
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
} }
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) { StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet(); String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, null, false) && checkDisp; checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, null, false, b.toString()) && checkDisp;
} }
} }
} catch (CheckCodeOnServerException e) { } catch (CheckCodeOnServerException e) {
@ -1394,25 +1396,186 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return checkDisp; return checkDisp;
} }
private boolean isInScope(ElementDefinitionBindingAdditionalComponent ab) { private boolean isInScope(ElementDefinitionBindingAdditionalComponent ab, StructureDefinition profile, Element resource, StringBuilder b) {
if (ab.getUsage().isEmpty()) {
return true;
}
boolean ok = true;
for (UsageContext usage : ab.getUsage()) { for (UsageContext usage : ab.getUsage()) {
if (isInScope(usage)) { if (!isInScope(usage, profile, resource, b)) {
ok = false;
}
}
return ok;
}
private boolean isInScope(UsageContext usage, StructureDefinition profile, Element resource, StringBuilder b) {
if (isKnownUsage(usage)) {
return true;
}
if (usage.getCode().hasSystem() && (usage.getCode().getSystem().equals(profile.getUrl()) || usage.getCode().getSystem().equals(profile.getVersionedUrl()))) {
// if it's not a defined usage from external sources, it might match something in the data content
List<Element> items = findDataValue(resource, usage.getCode().getCode());
if (matchesUsage(items, usage.getValue())) {
b.append(context.formatMessage(I18nConstants.BINDING_ADDITIONAL_USAGE, displayCoding(usage.getCode()), display(usage.getValue())));
return true; return true;
} }
} }
return ab.getUsage().isEmpty(); return false;
} }
private boolean isInScope(UsageContext usage) { private String displayCoding(Coding value) {
if (isKnownUsage(usage)) { return value.getCode();
}
private String displayCodeableConcept(CodeableConcept value) {
for (Coding c : value.getCoding()) {
String s = displayCoding(c);
if (s != null) {
return s;
}
}
return value.getText();
}
private String display(DataType value) {
switch (value.fhirType()) {
case "Coding" : return displayCoding((Coding) value);
case "CodeableConcept" : return displayCodeableConcept((CodeableConcept) value);
}
return value.fhirType();
}
private boolean matchesUsage(List<Element> items, DataType value) {
for (Element item : items) {
if (matchesUsage(item, value)) {
return true;
}
}
return false;
}
private String display(List<Element> items) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (Element item : items) {
display(b, item);
}
return b.toString();
}
private void display(CommaSeparatedStringBuilder b, Element item) {
if (item.isPrimitive()) {
b.append(item.primitiveValue());
} else if (item.fhirType().equals("CodeableConcept")) {
for (Element c : item.getChildren("coding")) {
b.append(c.getNamedChildValue("code"));
}
} else {
b.append(item.toString());
}
}
private boolean matchesUsage(Element item, DataType value) {
switch (value.fhirType()) {
case "CodeableConcept": return matchesUsageCodeableConcept(item, (CodeableConcept) value);
case "Quantity": return false;
case "Range": return false;
case "Reference": return false;
default: return false;
}
}
private boolean matchesUsageCodeableConcept(Element item, CodeableConcept value) {
switch (item.fhirType()) {
case "CodeableConcept": return matchesUsageCodeableConceptCodeableConcept(item, value);
case "Coding": return matchesUsageCodeableConceptCoding(item, value);
default: return false;
}
}
private boolean matchesUsageCodeableConceptCoding(Element item, CodeableConcept value) {
String system = item.getNamedChildValue("system");
String version = item.getNamedChildValue("version");
String code = item.getNamedChildValue("code");
for (Coding c : value.getCoding()) {
if (system == null || !system.equals(c.getSystem())) {
return false;
}
if (code == null || !code.equals(c.getCode())) {
return false;
}
if (c.hasVersion()) {
if (version == null || !version.equals(c.getVersion())) {
return false;
}
}
return true; return true;
} }
return false; return false;
} }
private boolean matchesUsageCodeableConceptCodeableConcept(Element item, CodeableConcept value) {
for (Element code : item.getChildren("coding")) {
if (matchesUsageCodeableConceptCoding(code, value)) {
return true;
}
}
return false;
}
private List<Element> findDataValue(Element resource, String code) {
List<Element> items = new ArrayList<Element>();
if (resource != null) {
findDataValues(items, resource, code);
}
return items;
}
private void findDataValues(List<Element> items, Element element, String path) {
if (element.getPath() == null) {
return;
}
if (pathMatches(element.getPath(), path)) {
items.add(element);
} else if (element.hasChildren() && path.startsWith(element.getPath())) {
for (Element child : element.getChildren()) {
findDataValues(items, child, path);
}
}
}
private boolean pathMatches(String actualPath, String pathSpec) {
String[] ap = actualPath.split("\\.");
String[] ps = pathSpec.split("\\.");
if (ap.length != ps.length) {
return false;
}
for (int i = 0; i < ap.length; i++) {
if (!pathSegmentMatches(ap[i], ps[i])) {
return false;
}
}
return true;
}
private boolean pathSegmentMatches(String ap, String ps) {
if (ps.contains("[")) {
return ap.equals(ps);
} else {
if (ap.contains("[")) {
ap = ap.substring(0, ap.indexOf("["));
}
return ap.equals(ps);
}
}
private BindingStrength convertPurposeToStrength(AdditionalBindingPurposeVS purpose) { private BindingStrength convertPurposeToStrength(AdditionalBindingPurposeVS purpose) {
switch (purpose) { switch (purpose) {
case MAXIMUM: return BindingStrength.REQUIRED; case MAXIMUM: return BindingStrength.REQUIRED;
case EXTENSIBLE: return BindingStrength.EXTENSIBLE;
case PREFERRED: return BindingStrength.PREFERRED; case PREFERRED: return BindingStrength.PREFERRED;
case REQUIRED: return BindingStrength.REQUIRED; case REQUIRED: return BindingStrength.REQUIRED;
default: return null; default: return null;
@ -1424,7 +1587,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
private boolean validateBindingCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, NodeStack stack, BooleanHolder bh, boolean checkDisp, BooleanHolder checked, private boolean validateBindingCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, NodeStack stack, BooleanHolder bh, boolean checkDisp, BooleanHolder checked,
CodeableConcept cc, String vsRef, ValueSet valueset, BindingStrength strength, Extension maxVS, boolean base) throws CheckCodeOnServerException { CodeableConcept cc, String vsRef, ValueSet valueset, BindingStrength strength, Extension maxVS, boolean base, String usageNote) throws CheckCodeOnServerException {
if (valueset == null) { if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(vsRef); CodeSystem cs = context.fetchCodeSystem(vsRef);
if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) { if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) {
@ -1436,12 +1599,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingContext bc = base ? BindingContext.BASE : BindingContext.ADDITIONAL; BindingContext bc = base ? BindingContext.BASE : BindingContext.ADDITIONAL;
if (!cc.hasCoding()) { if (!cc.hasCoding()) {
if (strength == BindingStrength.REQUIRED) if (strength == BindingStrength.REQUIRED)
bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(vsRef, valueset, bc))); bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(vsRef, valueset, bc, usageNote)));
else if (strength == BindingStrength.EXTENSIBLE) { else if (strength == BindingStrength.EXTENSIBLE) {
if (maxVS != null) if (maxVS != null)
bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringFromExtension(maxVS)), valueset.getVersionedUrl())); bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringFromExtension(maxVS)), valueset.getVersionedUrl()));
else if (!noExtensibleWarnings) { else if (!noExtensibleWarnings) {
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(vsRef, valueset, bc)); warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(vsRef, valueset, bc, usageNote));
} }
} }
} else { } else {
@ -1493,15 +1656,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// we've already handled the warnings / errors about this, and set the status correctly. We don't need to do anything more? // we've already handled the warnings / errors about this, and set the status correctly. We don't need to do anything more?
} else { } else {
if (strength == BindingStrength.REQUIRED) { if (strength == BindingStrength.REQUIRED) {
bh.see(txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(vsRef, valueset, bc), ccSummary(cc))); bh.see(txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(vsRef, valueset, bc, usageNote), ccSummary(cc)));
} else if (strength == BindingStrength.EXTENSIBLE) { } else if (strength == BindingStrength.EXTENSIBLE) {
if (maxVS != null) if (maxVS != null)
bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(maxVS), cc, stack)); bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(maxVS), cc, stack));
if (!noExtensibleWarnings) if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(vsRef, valueset, bc), ccSummary(cc)); txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(vsRef, valueset, bc, usageNote), ccSummary(cc));
} else if (strength == BindingStrength.PREFERRED) { } else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) { if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(vsRef, valueset, bc), ccSummary(cc)); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(vsRef, valueset, bc, usageNote), ccSummary(cc));
} }
} }
} }
@ -1648,7 +1811,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength(); BindingStrength strength = binding.getStrength();
Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, vsMax, true); validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, vsMax, true, null);
// special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding.
if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) {
@ -1660,11 +1823,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
} }
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) { StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet(); String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, null, false); validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, null, false, b.toString());
} }
} }
} }
@ -1698,18 +1862,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength(); BindingStrength strength = binding.getStrength();
Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, vsMax, true); ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, vsMax, true, null);
} else if (binding.hasValueSet()) { } else if (binding.hasValueSet()) {
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
} else if (!inCodeableConcept && !noBindingMsgSuppressed) { } else if (!inCodeableConcept && !noBindingMsgSuppressed) {
hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path);
} }
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) { StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet(); String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, null, true) && ok; ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, null, true, b.toString()) && ok;
} }
} }
} }
@ -1728,7 +1893,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean validateBindingTerminologyCoding(List<ValidationMessage> errors, String path, Element element, private boolean validateBindingTerminologyCoding(List<ValidationMessage> errors, String path, Element element,
StructureDefinition profile, NodeStack stack, boolean ok, Coding c, String code, String system, String display, StructureDefinition profile, NodeStack stack, boolean ok, Coding c, String code, String system, String display,
String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base) { String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base, String usageNote) {
if (valueset == null) { if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(vsRef); CodeSystem cs = context.fetchCodeSystem(vsRef);
if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) { if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) {
@ -1754,27 +1919,27 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, system+"#"+code); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, system+"#"+code);
else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) {
if (strength == BindingStrength.REQUIRED) if (strength == BindingStrength.REQUIRED)
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc), vr.getMessage(), system+"#"+code); txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc, usageNote), vr.getMessage(), system+"#"+code);
else if (strength == BindingStrength.EXTENSIBLE) { else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null) if (vsMax != null)
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack);
else if (!noExtensibleWarnings) else if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc)); txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc, usageNote));
} else if (strength == BindingStrength.PREFERRED) { } else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) { if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc)); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc, usageNote));
} }
} }
} else if (strength == BindingStrength.REQUIRED) } else if (strength == BindingStrength.REQUIRED)
ok= txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code) && ok; ok= txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(vsRef, valueset, bc, usageNote), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code) && ok;
else if (strength == BindingStrength.EXTENSIBLE) { else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null) if (vsMax != null)
ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok; ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok;
else else
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code); txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(vsRef, valueset, bc, usageNote), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code);
} else if (strength == BindingStrength.PREFERRED) { } else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) { if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(vsRef, valueset, bc, usageNote), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code);
} }
} }
} else if (vr != null && vr.getMessage() != null){ } else if (vr != null && vr.getMessage() != null){
@ -1896,9 +2061,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+cc.toString()); timeTracker.tx(t, "vc "+cc.toString());
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), vr.getMessage()); txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), vr.getMessage());
else else
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), ccSummary(cc)) && ok; ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), ccSummary(cc)) && ok;
} }
} catch (CheckCodeOnServerException e) { } catch (CheckCodeOnServerException e) {
if (STACK_TRACE) e.getCause().printStackTrace(); if (STACK_TRACE) e.getCause().printStackTrace();
@ -1936,9 +2101,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'");
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), vr.getMessage(), c.getSystem()+"#"+c.getCode()); txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), vr.getMessage(), c.getSystem()+"#"+c.getCode());
else else
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), c.getSystem(), c.getCode()) && ok; ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl, valueset, BindingContext.MAXVS, null), c.getSystem(), c.getCode()) && ok;
} }
} catch (Exception e) { } catch (Exception e) {
if (STACK_TRACE) e.printStackTrace(); if (STACK_TRACE) e.printStackTrace();
@ -1966,9 +2131,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
timeTracker.tx(t, "vc "+value); timeTracker.tx(t, "vc "+value);
if (!vr.isOk()) { if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.BASE), vr.getMessage(), value); txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.BASE, null), vr.getMessage(), value);
else { else {
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl, valueset, BindingContext.BASE), vr.getMessage()) && ok; ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl, valueset, BindingContext.BASE, null), vr.getMessage()) && ok;
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -2029,7 +2194,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
BindingStrength strength = binding.getStrength(); BindingStrength strength = binding.getStrength();
Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET);
ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, vsMax, true); ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, vsMax, true, null);
// } else if (binding.hasValueSet()) { // } else if (binding.hasValueSet()) {
// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); // hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK);
@ -2038,12 +2203,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) {
if (isTestableBinding(ab) && isInScope(ab)) { StringBuilder b = new StringBuilder();
if (isTestableBinding(ab) && isInScope(ab, profile, getResource(stack), b)) {
String vsRef = ab.getValueSet(); String vsRef = ab.getValueSet();
ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile);
BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); BindingStrength strength = convertPurposeToStrength(ab.getPurpose());
ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, null, false) && ok; ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, null, false, b.toString()) && ok;
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -2068,9 +2234,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return ok; return ok;
} }
private Element getResource(NodeStack stack) {
if (stack.getElement().isResource()) {
return stack.getElement();
}
if (stack.getParent() == null) {
return null;
}
return getResource(stack.getParent());
}
private boolean validateBindingCodedElement(List<ValidationMessage> errors, String path, Element element, private boolean validateBindingCodedElement(List<ValidationMessage> errors, String path, Element element,
StructureDefinition profile, NodeStack stack, String theCode, String theSystem, boolean ok, BooleanHolder checked, StructureDefinition profile, NodeStack stack, String theCode, String theSystem, boolean ok, BooleanHolder checked,
Coding c, String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base) { Coding c, String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base, String usageNote) {
if (valueset == null) { if (valueset == null) {
CodeSystem cs = context.fetchCodeSystem(vsRef); CodeSystem cs = context.fetchCodeSystem(vsRef);
if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) { if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) {
@ -2098,28 +2274,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, theSystem+"#"+theCode); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, theSystem+"#"+theCode);
else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) {
if (strength == BindingStrength.REQUIRED) if (strength == BindingStrength.REQUIRED)
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc), vr.getMessage(), theSystem+"#"+theCode) && ok; ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc, usageNote), vr.getMessage(), theSystem+"#"+theCode) && ok;
else if (strength == BindingStrength.EXTENSIBLE) { else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null) if (vsMax != null)
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack); checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack);
else if (!noExtensibleWarnings) else if (!noExtensibleWarnings)
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc), theSystem+"#"+theCode); txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc, usageNote), theSystem+"#"+theCode);
} else if (strength == BindingStrength.PREFERRED) { } else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) { if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc), theSystem+"#"+theCode); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc, usageNote), theSystem+"#"+theCode);
} }
} }
} else if (strength == BindingStrength.REQUIRED) } else if (strength == BindingStrength.REQUIRED)
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode) && ok; ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(vsRef, valueset, bc, usageNote), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode) && ok;
else if (strength == BindingStrength.EXTENSIBLE) { else if (strength == BindingStrength.EXTENSIBLE) {
if (vsMax != null) if (vsMax != null)
ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok; ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok;
else if (!noExtensibleWarnings) { else if (!noExtensibleWarnings) {
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), c.getSystem()+"#"+c.getCode()); txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(vsRef, valueset, bc, usageNote), getErrorMessage(vr.getMessage()), c.getSystem()+"#"+c.getCode());
} }
} else if (strength == BindingStrength.PREFERRED) { } else if (strength == BindingStrength.PREFERRED) {
if (baseOnly) { if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(vsRef, valueset, bc, usageNote), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode);
} }
} }
} else if (vr != null && vr.getMessage() != null) { } else if (vr != null && vr.getMessage() != null) {
@ -3563,15 +3739,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} else if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) { } else if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) {
txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage());
} else if (binding.getStrength() == BindingStrength.REQUIRED) { } else if (binding.getStrength() == BindingStrength.REQUIRED) {
ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())) && ok; ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE, null), getErrorMessage(vr.getMessage())) && ok;
} else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET))
ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), value, stack) && ok; ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), value, stack) && ok;
else if (!noExtensibleWarnings && !isOkExtension(value, vs)) else if (!noExtensibleWarnings && !isOkExtension(value, vs))
txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())); txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE, null), getErrorMessage(vr.getMessage()));
} else if (binding.getStrength() == BindingStrength.PREFERRED) { } else if (binding.getStrength() == BindingStrength.PREFERRED) {
if (baseOnly) { if (baseOnly) {
txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())); txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE, null), getErrorMessage(vr.getMessage()));
} }
} }
} else if (vr != null && vr.getMessage() != null){ } else if (vr != null && vr.getMessage() != null){
@ -4279,7 +4455,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return false; return false;
} }
private String describeReference(String reference, CanonicalResource target, BindingContext ctxt) { private String describeReference(String reference, CanonicalResource target, BindingContext ctxt, String usageNote) {
if (reference == null && target == null) if (reference == null && target == null)
return "null"; return "null";
String res = null; String res = null;
@ -4297,7 +4473,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
} }
} }
switch (ctxt) { switch (ctxt) {
case ADDITIONAL: return context.formatMessage(I18nConstants.BINDING_ADDITIONAL, res); case ADDITIONAL: return context.formatMessage(Utilities.noString(usageNote) ? I18nConstants.BINDING_ADDITIONAL_D : I18nConstants.BINDING_ADDITIONAL_UC, res, usageNote);
case MAXVS: return context.formatMessage(I18nConstants.BINDING_MAX, res); case MAXVS: return context.formatMessage(I18nConstants.BINDING_MAX, res);
default: return res; default: return res;
} }

View File

@ -525,7 +525,7 @@ public class StructureDefinitionValidator extends BaseValidator {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("can-bind") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Binding", typeCodes) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), characteristics.contains("can-bind") , I18nConstants.SD_ILLEGAL_CHARACTERISTICS, "Binding", typeCodes) && ok;
} }
Element binding = element.getNamedChild("binding", false); Element binding = element.getNamedChild("binding", false);
ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path, experimental) && ok; ok = validateBinding(errors, binding, stack.push(binding, -1, null, null), typeCodes, snapshot, path, experimental, sd) && ok;
} else { } else {
// this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back // this is a good idea but there's plenty of cases where the rule isn't met; maybe one day it's worth investing the time to exclude these cases and bring this rule back
// String bt = boundType(typeCodes); // String bt = boundType(typeCodes);
@ -989,7 +989,7 @@ public class StructureDefinitionValidator extends BaseValidator {
return null; return null;
} }
private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path, boolean experimental) { private boolean validateBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, Set<String> typeCodes, boolean snapshot, String path, boolean experimental, StructureDefinition profile) {
boolean ok = true; boolean ok = true;
if (bindableType(typeCodes) == null) { if (bindableType(typeCodes) == null) {
ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString()) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot, I18nConstants.SD_ED_BIND_NO_BINDABLE, path, typeCodes.toString()) && ok;
@ -1019,6 +1019,119 @@ public class StructureDefinitionValidator extends BaseValidator {
} }
} }
} }
if (binding.hasChildren("additional")) {
int i = 0;
for (Element ab : binding.getChildren("additional")) {
ok = validateAdditionalBinding(errors, ab, stack.push(ab, i, null, null), snapshot, path, experimental) && ok;
i++;
}
}
if (binding.hasExtension(ToolingExtensions.EXT_BINDING_ADDITIONAL)) {
int i = 0;
for (Element ab : binding.getChildren("extension")) {
String url = ab.getNamedChildValue("url");
if (ToolingExtensions.EXT_BINDING_ADDITIONAL.equals(url)) {
ok = validateAdditionalBindingExtension(errors, ab, stack.push(ab, i, null, null), snapshot, path, experimental, profile) && ok;
}
i++;
}
}
return ok;
}
private boolean validateAdditionalBinding(List<ValidationMessage> errors, Element binding, NodeStack stack, boolean snapshot, String path, boolean experimental) {
boolean ok = true;
if (binding.hasChild("valueSet", false)) {
Element valueSet = binding.getNamedChild("valueSet", false);
String ref = valueSet.hasPrimitiveValue() ? valueSet.primitiveValue() : valueSet.getNamedChildValue("reference", false);
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || ref != null, I18nConstants.SD_ED_SHOULD_BIND_WITH_VS, path)) {
Resource vs = context.fetchResource(Resource.class, ref);
// just because we can't resolve it directly doesn't mean that terminology server can't. Check with it
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
if (vs != null) {
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) {
ValueSet vsr = (ValueSet) vs;
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref);
} else {
ok = false;
}
}
}
}
}
if (binding.hasChildren("usage")) {
for (Element usage : binding.getChildren("usage")) {
warning(errors, "2024-09-20", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, "test");
}
}
return ok;
}
private boolean validateAdditionalBindingExtension(List<ValidationMessage> errors, Element binding, NodeStack stack, boolean snapshot, String path, boolean experimental, StructureDefinition profile) {
boolean ok = true;
if (binding.hasExtension("valueSet")) {
Element valueSet = binding.getExtension("valueSet");
Element vv = valueSet.getNamedChild("value");
String ref = vv.hasPrimitiveValue() ? vv.primitiveValue() : vv.getNamedChildValue("reference", false);
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), !snapshot || ref != null, I18nConstants.SD_ED_SHOULD_BIND_WITH_VS, path)) {
Resource vs = context.fetchResource(Resource.class, ref);
// just because we can't resolve it directly doesn't mean that terminology server can't. Check with it
if (warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs != null || serverSupportsValueSet(ref), I18nConstants.SD_ED_BIND_UNKNOWN_VS, path, ref)) {
if (vs != null) {
if (rule(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack.getLiteralPath(), vs instanceof ValueSet, I18nConstants.SD_ED_BIND_NOT_VS, path, ref, vs.fhirType())) {
ValueSet vsr = (ValueSet) vs;
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), !vsr.getExperimental() || experimental, I18nConstants.SD_ED_EXPERIMENTAL_BINDING, path, ref);
} else {
ok = false;
}
}
}
}
}
if (binding.hasExtension("usage")) {
int i = 0;
for (Element usage : binding.getChildren("extension")) {
String url = usage.getNamedChildValue("url");
if ("usage".equals(url)) {
Element uv = usage.getNamedChild("value");
ok = validateAdditionalBindingUsage(errors, uv, stack.push(uv, -1, null, null), path, profile) && ok;
}
i++;
}
}
return ok;
}
private boolean validateAdditionalBindingUsage(List<ValidationMessage> errors, Element usage, NodeStack stack, String path, StructureDefinition profile) {
boolean ok = true;
Element cc = usage.getNamedChild("code");
if (cc != null) {
String system = cc.getNamedChildValue("system");
String code = cc.getNamedChildValue("code");
if (system != null && system.equals(profile.getUrl())) {
ElementDefinition ed = profile.getDifferential().getElementByPath(code);
if (ed == null) {
ed = profile.getSnapshot().getElementByPath(code);
}
if (ed == null) {
ok = false;
rule(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_ELEMENT, system, code);
} else {
if (usage.hasChild("value")) {
String t = usage.getNamedChild("value").fhirType();
ok = rule(errors, "2024-09-20", IssueType.BUSINESSRULE, stack.getLiteralPath(), "CodeableConcept".equals(t), I18nConstants.SD_ED_ADDITIONAL_BINDING_USAGE_INVALID_TYPE, t, "CodeableConcept") && ok;
}
}
} else {
warning(errors, "2024-09-17", IssueType.BUSINESSRULE, stack.getLiteralPath(), false, I18nConstants.SD_ED_ADDITIONAL_BINDING_USAGE_UNKNOWN, system, code);
}
}
return ok; return ok;
} }

View File

@ -0,0 +1,42 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://hl7.org/fhir/test/StructureDefinition/additional-bindings-profile-cs",
"code" : "digital-access"
}, "valueSet" :null, "langs":"en-US", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"code" : "digital-access",
"system" : "http://hl7.org/fhir/test/StructureDefinition/additional-bindings-profile-cs",
"severity" : "error",
"error" : "A definition for CodeSystem 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-profile-cs' could not be found, so the code cannot be validated",
"class" : "CODESYSTEM_UNSUPPORTED",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "http://hl7.org/fhir/test/StructureDefinition/additional-bindings-profile-cs",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r4"
}],
"severity" : "error",
"code" : "not-found",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-found"
}],
"text" : "A definition for CodeSystem 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-profile-cs' could not be found, so the code cannot be validated"
},
"location" : ["Coding.system"],
"expression" : ["Coding.system"]
}]
}
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,83 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes",
"code" : "digital-access-x"
}, "valueSet" :null, "langs":"en-US", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"code" : "digital-access-x",
"system" : "http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes",
"severity" : "error",
"error" : "A definition for CodeSystem 'http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes' could not be found, so the code cannot be validated",
"class" : "CODESYSTEM_UNSUPPORTED",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r4"
}],
"severity" : "error",
"code" : "not-found",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-found"
}],
"text" : "A definition for CodeSystem 'http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes' could not be found, so the code cannot be validated"
},
"location" : ["Coding.system"],
"expression" : ["Coding.system"]
}]
}
}
-------------------------------------------------------------------------------------
{"code" : {
"system" : "http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes",
"code" : "digital-access"
}, "valueSet" :null, "langs":"en-US", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": {
"resourceType" : "Parameters",
"parameter" : [{
"name" : "profile-url",
"valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891"
}]
}}####
v: {
"code" : "digital-access",
"system" : "http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes",
"severity" : "error",
"error" : "A definition for CodeSystem 'http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes' could not be found, so the code cannot be validated",
"class" : "CODESYSTEM_UNSUPPORTED",
"server" : "http://tx-dev.fhir.org/r4",
"unknown-systems" : "http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes",
"issues" : {
"resourceType" : "OperationOutcome",
"issue" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server",
"valueUrl" : "http://tx-dev.fhir.org/r4"
}],
"severity" : "error",
"code" : "not-found",
"details" : {
"coding" : [{
"system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
"code" : "not-found"
}],
"text" : "A definition for CodeSystem 'http://hl7.org/fhir/us/sdoh-clinicalcare/CodeSystem/SDOHCC-CodeSystemTemporaryCodes' could not be found, so the code cannot be validated"
},
"location" : ["Coding.system"],
"expression" : ["Coding.system"]
}]
}
}
-------------------------------------------------------------------------------------