FHIR-25206 handle deprecated concepts properly when expanding value sets

This commit is contained in:
Grahame Grieve 2022-08-19 14:24:20 +10:00
parent fdb15b8951
commit ae285401ae
4 changed files with 139 additions and 66 deletions

View File

@ -14,6 +14,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.hamcrest.beans.HasProperty;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.FHIRFormatError;
@ -36,6 +37,7 @@ import org.hl7.fhir.r5.model.PrimitiveType;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceDesignationComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
@ -44,6 +46,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.RenderingContext;
import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
@ -158,6 +161,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
boolean hasExtensions = false; boolean hasExtensions = false;
List<String> langs = new ArrayList<String>(); List<String> langs = new ArrayList<String>();
Map<String, String> designations = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list Map<String, String> designations = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list
Map<String, String> properties = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list
if (header) { if (header) {
XhtmlNode h = x.addTag(getHeader()); XhtmlNode h = x.addTag(getHeader());
@ -198,40 +202,32 @@ public class ValueSetRenderer extends TerminologyRenderer {
} }
} }
boolean doSystem = true; // checkDoSystem(vs, src);
boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains()); boolean doDefinition = checkDoDefinition(vs.getExpansion().getContains());
if (doSystem && allFromOneSystem(vs)) {
doSystem = false;
XhtmlNode p = x.para();
p.tx("All codes in this table are from the system ");
allCS = getContext().getWorker().fetchCodeSystem(vs.getExpansion().getContains().get(0).getSystem());
String ref = null;
if (allCS != null)
ref = getCsRef(allCS);
if (ref == null)
p.code(vs.getExpansion().getContains().get(0).getSystem());
else
p.ah(context.fixReference(ref)).code(vs.getExpansion().getContains().get(0).getSystem());
}
XhtmlNode t = x.table( "codes"); XhtmlNode t = x.table( "codes");
XhtmlNode tr = t.tr(); XhtmlNode tr = t.tr();
if (doLevel) if (doLevel)
tr.td().b().tx("Level"); tr.td().b().tx("Level");
tr.td().attribute("style", "white-space:nowrap").b().tx("Code"); tr.td().attribute("style", "white-space:nowrap").b().tx("Code");
if (doSystem) tr.td().b().tx("System");
tr.td().b().tx("System");
XhtmlNode tdDisp = tr.td(); XhtmlNode tdDisp = tr.td();
tdDisp.b().tx("Display"); tdDisp.b().tx("Display");
boolean doDesignations = false; boolean doDesignations = false;
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
scanForDesignations(c, langs, designations); scanForDesignations(c, langs, designations);
} }
scanForProperties(vs.getExpansion(), langs, properties);
if (doDefinition) { if (doDefinition) {
tr.td().b().tx("Definition"); tr.td().b().tx("Definition");
doDesignations = false; doDesignations = false;
for (String n : Utilities.sorted(properties.keySet())) {
tr.td().b().ah(properties.get(n)).addText(n);
}
} else { } else {
for (String n : Utilities.sorted(properties.keySet())) {
tr.td().b().ah(properties.get(n)).addText(n);
}
// if we're not doing definitions and we don't have too many languages, we'll do them in line // if we're not doing definitions and we don't have too many languages, we'll do them in line
doDesignations = langs.size() + designations.size() < MAX_DESIGNATIONS_IN_LINE; doDesignations = langs.size() + properties.size() + designations.size() < MAX_DESIGNATIONS_IN_LINE;
if (doDesignations) { if (doDesignations) {
if (vs.hasLanguage()) { if (vs.hasLanguage()) {
@ -249,7 +245,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
addMapHeaders(tr, maps); addMapHeaders(tr, maps);
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
addExpansionRowToTable(t, c, 1, doLevel, doSystem, doDefinition, maps, allCS, langs, designations, doDesignations); addExpansionRowToTable(t, c, 1, doLevel, true, doDefinition, maps, allCS, langs, designations, doDesignations, properties);
} }
// now, build observed languages // now, build observed languages
@ -280,6 +276,30 @@ public class ValueSetRenderer extends TerminologyRenderer {
return hasExtensions; return hasExtensions;
} }
private void scanForProperties(ValueSetExpansionComponent exp, List<String> langs, Map<String, String> properties) {
properties.clear();
for (ValueSetExpansionPropertyComponent pp : exp.getProperty()) {
if (pp.hasCode() && pp.hasUri() && anyActualproperties(exp.getContains(), pp.getCode())) {
properties.put(pp.getCode(), pp.getUri());
}
}
}
private boolean anyActualproperties(List<ValueSetExpansionContainsComponent> contains, String pp) {
for (ValueSetExpansionContainsComponent c : contains) {
for (ConceptPropertyComponent cp : c.getProperty()) {
if (pp.equals(cp.getCode())) {
return true;
}
}
if (anyActualproperties(c.getContains(), pp)) {
return true;
}
}
return false;
}
private void generateContentModeNotices(XhtmlNode x, ValueSetExpansionComponent expansion) { private void generateContentModeNotices(XhtmlNode x, ValueSetExpansionComponent expansion) {
generateContentModeNotice(x, expansion, "example", "Expansion based on example code system"); generateContentModeNotice(x, expansion, "example", "Expansion based on example code system");
generateContentModeNotice(x, expansion, "fragment", "Expansion based on code system fragment"); generateContentModeNotice(x, expansion, "fragment", "Expansion based on code system fragment");
@ -715,7 +735,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
} }
} }
private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List<UsedConceptMap> maps, CodeSystem allCS, List<String> langs, Map<String, String> designations, boolean doDesignations) { private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List<UsedConceptMap> maps, CodeSystem allCS, List<String> langs, Map<String, String> designations, boolean doDesignations, Map<String, String> properties) {
XhtmlNode tr = t.tr(); XhtmlNode tr = t.tr();
XhtmlNode td = tr.td(); XhtmlNode td = tr.td();
@ -745,6 +765,13 @@ public class ValueSetRenderer extends TerminologyRenderer {
if (cs != null) if (cs != null)
td.addText(CodeSystemUtilities.getCodeDefinition(cs, c.getCode())); td.addText(CodeSystemUtilities.getCodeDefinition(cs, c.getCode()));
} }
for (String n : Utilities.sorted(properties.keySet())) {
td = tr.td();
String ps = getPropertyValue(c, n);
if (!Utilities.noString(ps)) {
td.addText(ps);
}
}
for (UsedConceptMap m : maps) { for (UsedConceptMap m : maps) {
td = tr.td(); td = tr.td();
List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap()); List<TargetElementComponentWrapper> mappings = findMappingsForCode(c.getCode(), m.getMap());
@ -765,7 +792,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
addLangaugesToRow(c, langs, tr); addLangaugesToRow(c, langs, tr);
} }
for (ValueSetExpansionContainsComponent cc : c.getContains()) { for (ValueSetExpansionContainsComponent cc : c.getContains()) {
addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, designations, doDesignations); addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, designations, doDesignations, properties);
} }
} }
@ -773,6 +800,15 @@ public class ValueSetRenderer extends TerminologyRenderer {
private String getPropertyValue(ValueSetExpansionContainsComponent c, String n) {
for (ConceptPropertyComponent cp : c.getProperty()) {
if (n.equals(cp.getCode())) {
return cp.getValue().primitiveValue();
}
}
return null;
}
private boolean checkSystemMatches(String system, ValueSetExpansionContainsComponent cc) { private boolean checkSystemMatches(String system, ValueSetExpansionContainsComponent cc) {
if (!system.equals(cc.getSystem())) if (!system.equals(cc.getSystem()))
return false; return false;

View File

@ -241,12 +241,34 @@ public class CodeSystemUtilities {
} }
} }
public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def, boolean ignoreStatus) {
try {
for (ConceptPropertyComponent p : def.getProperty()) {
if (!ignoreStatus) {
if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "inactive".equals(p.getValueCodeType().getCode()))
return true;
}
// legacy
if ("inactive".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType)
return ((BooleanType) p.getValue()).getValue();
}
return false;
} catch (FHIRException e) {
return false;
}
}
public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept, DateTimeType date) throws FHIRFormatError { public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept, DateTimeType date) throws FHIRFormatError {
setStatus(cs, concept, ConceptStatus.Deprecated); setStatus(cs, concept, ConceptStatus.Deprecated);
defineDeprecatedProperty(cs); defineDeprecatedProperty(cs);
concept.addProperty().setCode("deprecationDate").setValue(date); concept.addProperty().setCode("deprecationDate").setValue(date);
} }
public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError {
setStatus(cs, concept, ConceptStatus.Deprecated);
}
public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException { public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException {
for (ConceptPropertyComponent p : def.getProperty()) { for (ConceptPropertyComponent p : def.getProperty()) {
if ("status".equals(p.getCode()) && p.hasValueStringType()) if ("status".equals(p.getCode()) && p.hasValueStringType())

View File

@ -108,6 +108,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
@ -207,7 +208,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
} }
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, Parameters expParams, private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, Parameters expParams,
boolean isAbstract, boolean inactive, List<ValueSet> filters, boolean noInactive) { boolean isAbstract, boolean inactive, List<ValueSet> filters, boolean noInactive, boolean deprecated, List<ValueSetExpansionPropertyComponent> vsProp) {
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code)) if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code))
return null; return null;
@ -222,6 +223,9 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
n.setAbstract(true); n.setAbstract(true);
if (inactive) if (inactive)
n.setInactive(true); n.setInactive(true);
if (deprecated) {
ValueSetUtilities.setDeprecated(vsProp, n);
}
if (expParams.getParameterBool("includeDesignations") && designations != null) { if (expParams.getParameterBool("includeDesignations") && designations != null) {
for (ConceptDefinitionDesignationComponent t : designations) { for (ConceptDefinitionDesignationComponent t : designations) {
@ -285,12 +289,12 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
return null; return null;
} }
private void addCodeAndDescendents(ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, boolean noInactive) throws FHIRException { private void addCodeAndDescendents(ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException {
focus.checkNoModifiers("Expansion.contains", "expanding"); focus.checkNoModifiers("Expansion.contains", "expanding");
ValueSetExpansionContainsComponent np = addCode(focus.getSystem(), focus.getCode(), focus.getDisplay(), parent, ValueSetExpansionContainsComponent np = addCode(focus.getSystem(), focus.getCode(), focus.getDisplay(), parent,
convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), filters, noInactive); convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), filters, noInactive, false, vsProps);
for (ValueSetExpansionContainsComponent c : focus.getContains()) for (ValueSetExpansionContainsComponent c : focus.getContains())
addCodeAndDescendents(focus, np, expParams, filters, noInactive); addCodeAndDescendents(focus, np, expParams, filters, noInactive, vsProps);
} }
private List<ConceptDefinitionDesignationComponent> convert(List<ConceptReferenceDesignationComponent> designations) { private List<ConceptDefinitionDesignationComponent> convert(List<ConceptReferenceDesignationComponent> designations) {
@ -305,41 +309,31 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
return list; return list;
} }
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, ConceptDefinitionComponent exclusion, IConceptFilter filterFunc, boolean noInactive) throws FHIRException { private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, ConceptDefinitionComponent exclusion, IConceptFilter filterFunc, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException {
def.checkNoModifiers("Code in Code System", "expanding"); def.checkNoModifiers("Code in Code System", "expanding");
if (exclusion != null) { if (exclusion != null) {
if (exclusion.getCode().equals(def.getCode())) if (exclusion.getCode().equals(def.getCode()))
return; // excluded. return; // excluded.
} }
if (!CodeSystemUtilities.isDeprecated(cs, def, false)) { ValueSetExpansionContainsComponent np = null;
ValueSetExpansionContainsComponent np = null; boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def); boolean inc = CodeSystemUtilities.isInactive(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def); boolean dep = CodeSystemUtilities.isDeprecated(cs, def, false);
if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def)) { if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def)) {
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), expParams, abs, inc, filters, noInactive); np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), expParams, abs, inc, filters, noInactive, dep, vsProps);
} }
for (ConceptDefinitionComponent c : def.getConcept()) { for (ConceptDefinitionComponent c : def.getConcept()) {
addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive); addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps);
} }
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) { if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK); List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children) for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive); addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps);
}
} else {
for (ConceptDefinitionComponent c : def.getConcept()) {
addCodeAndDescendents(cs, system, c, null, expParams, filters, exclusion, filterFunc, noInactive);
}
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(cs, system, c, null, expParams, filters, exclusion, filterFunc, noInactive);
}
} }
} }
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, Parameters expParams, List<ValueSet> filters, boolean noInactive) throws ETooCostly, FHIRException { private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws ETooCostly, FHIRException {
if (expand != null) { if (expand != null) {
if (expand.getContains().size() > maxExpansionSize) if (expand.getContains().size() > maxExpansionSize)
throw failCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); throw failCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
@ -348,7 +342,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
params.add(p); params.add(p);
} }
copyImportContains(expand.getContains(), null, expParams, filters, noInactive); copyImportContains(expand.getContains(), null, expParams, filters, noInactive, vsProps);
} }
} }
@ -578,11 +572,11 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
} }
} }
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filter, boolean noInactive) throws FHIRException { private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filter, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException {
for (ValueSetExpansionContainsComponent c : list) { for (ValueSetExpansionContainsComponent c : list) {
c.checkNoModifiers("Imported Expansion in Code System", "expanding"); c.checkNoModifiers("Imported Expansion in Code System", "expanding");
ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, expParams, c.getAbstract(), c.getInactive(), filter, noInactive); ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, expParams, c.getAbstract(), c.getInactive(), filter, noInactive, false, vsProps);
copyImportContains(c.getContains(), np, expParams, filter, noInactive); copyImportContains(c.getContains(), np, expParams, filter, noInactive, vsProps);
} }
} }
@ -599,18 +593,18 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
ValueSet base = imports.get(0); ValueSet base = imports.get(0);
imports.remove(0); imports.remove(0);
base.checkNoModifiers("Imported ValueSet", "expanding"); base.checkNoModifiers("Imported ValueSet", "expanding");
copyImportContains(base.getExpansion().getContains(), null, expParams, imports, noInactive); copyImportContains(base.getExpansion().getContains(), null, expParams, imports, noInactive, base.getExpansion().getProperty());
} else { } else {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem()); CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if (isServerSide(inc.getSystem()) || (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT))) { if (isServerSide(inc.getSystem()) || (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT))) {
doServerIncludeCodes(inc, heirarchical, exp, imports, expParams, extensions, noInactive); doServerIncludeCodes(inc, heirarchical, exp, imports, expParams, extensions, noInactive, valueSet.getExpansion().getProperty());
} else { } else {
doInternalIncludeCodes(inc, exp, expParams, imports, cs, noInactive); doInternalIncludeCodes(inc, exp, expParams, imports, cs, noInactive);
} }
} }
} }
private void doServerIncludeCodes(ConceptSetComponent inc, boolean heirarchical, ValueSetExpansionComponent exp, List<ValueSet> imports, Parameters expParams, List<Extension> extensions, boolean noInactive) throws FHIRException { private void doServerIncludeCodes(ConceptSetComponent inc, boolean heirarchical, ValueSetExpansionComponent exp, List<ValueSet> imports, Parameters expParams, List<Extension> extensions, boolean noInactive, List<ValueSetExpansionPropertyComponent> vsProps) throws FHIRException {
ValueSetExpansionOutcome vso = context.expandVS(inc, heirarchical, noInactive); ValueSetExpansionOutcome vso = context.expandVS(inc, heirarchical, noInactive);
if (vso.getError() != null) { if (vso.getError() != null) {
throw failTSE("Unable to expand imported value set: " + vso.getError()); throw failTSE("Unable to expand imported value set: " + vso.getError());
@ -634,7 +628,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
} }
} }
for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) { for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) {
addCodeAndDescendents(cc, null, expParams, imports, noInactive); addCodeAndDescendents(cc, null, expParams, imports, noInactive, vsProps);
} }
} }
@ -664,7 +658,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) { if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system // special case - add all the code system
for (ConceptDefinitionComponent def : cs.getConcept()) { for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(), noInactive); addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
} }
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) { if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs); addFragmentWarning(exp, cs);
@ -693,7 +687,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
} else { } else {
inactive = CodeSystemUtilities.isInactive(cs, def); inactive = CodeSystemUtilities.isInactive(cs, def);
} }
addCode(inc.getSystem(), c.getCode(), !Utilities.noString(c.getDisplay()) ? c.getDisplay() : def == null ? null : def.getDisplay(), null, convertDesignations(c.getDesignation()), expParams, false, inactive, imports, noInactive); addCode(inc.getSystem(), c.getCode(), !Utilities.noString(c.getDisplay()) ? c.getDisplay() : def == null ? null : def.getDisplay(), null, convertDesignations(c.getDesignation()), expParams, false, inactive, imports, noInactive, false, exp.getProperty());
} }
} }
if (inc.getFilter().size() > 1) { if (inc.getFilter().size() > 1) {
@ -710,14 +704,14 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null) if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(), noInactive); addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISNOTA) { } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISNOTA) {
// special: all codes in the target code system that are not under the value // special: all codes in the target code system that are not under the value
ConceptDefinitionComponent defEx = getConceptForCode(cs.getConcept(), fc.getValue()); ConceptDefinitionComponent defEx = getConceptForCode(cs.getConcept(), fc.getValue());
if (defEx == null) if (defEx == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent def : cs.getConcept()) { for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(), noInactive); addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(), noInactive, exp.getProperty());
} }
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) { } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value // special: all codes in the target code system under the value
@ -725,11 +719,11 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
if (def == null) if (def == null)
throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept()) for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(), noInactive); addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) { if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK); List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children) for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(), noInactive); addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(), noInactive, exp.getProperty());
} }
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) { } else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
@ -740,13 +734,13 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) { if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) { if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
imports, noInactive); imports, noInactive, false, exp.getProperty());
} }
} }
} }
} else if (isDefinedProperty(cs, fc.getProperty())) { } else if (isDefinedProperty(cs, fc.getProperty())) {
for (ConceptDefinitionComponent def : cs.getConcept()) { for (ConceptDefinitionComponent def : cs.getConcept()) {
addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(fc, getPropertyDefinition(cs, fc.getProperty())), noInactive); addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(fc, getPropertyDefinition(cs, fc.getProperty())), noInactive, exp.getProperty());
} }
} else { } else {
throw fail("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet"); throw fail("Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");

View File

@ -1,5 +1,7 @@
package org.hl7.fhir.r5.terminologies; package org.hl7.fhir.r5.terminologies;
import java.util.List;
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -31,16 +33,24 @@ package org.hl7.fhir.r5.terminologies;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.DateTimeType;
import org.hl7.fhir.r5.model.Enumerations.FilterOperator; import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.Identifier; import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.Meta; import org.hl7.fhir.r5.model.Meta;
import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeType;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptStatus;
import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.StandardsStatus; import org.hl7.fhir.utilities.StandardsStatus;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
@ -234,4 +244,15 @@ public class ValueSetUtilities {
return vs; return vs;
} }
public static void setDeprecated(List<ValueSetExpansionPropertyComponent> vsProp, ValueSetExpansionContainsComponent n) {
n.addProperty().setCode("status").setValue(new CodeType("deprecated"));
for (ValueSetExpansionPropertyComponent o : vsProp) {
if ("status".equals(o.getCode())) {
return;
}
}
vsProp.add(new ValueSetExpansionPropertyComponent().setCode("status").setUri("http://hl7.org/fhir/concept-properties#status"));
}
} }