diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java index 27162587f..2a044023f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java @@ -522,6 +522,32 @@ public class ValueSetExpander extends ValueSetProcessBase { } + private void excludeCodeAndDescendents(WorkingContext wc, CodeSystem cs, String system, ConceptDefinitionComponent def, Parameters expParams, List filters, + ConceptDefinitionComponent exclusion, ConceptFilter filterFunc, List otherFilters, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly { + opContext.deadCheck(); + def.checkNoModifiers("Code in Code System", "expanding"); + if (exclusion != null) { + if (exclusion.getCode().equals(def.getCode())) + return; // excluded. + } + boolean abs = CodeSystemUtilities.isNotSelectable(cs, def); + if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def) && passesOtherFilters(otherFilters, cs, def.getCode())) { + for (String code : getCodesForConcept(def, expParams)) { + if (!(filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code, exp))) + excludeCode(wc, system, code); + } + } + for (ConceptDefinitionComponent c : def.getConcept()) { + excludeCodeAndDescendents(wc, cs, system, c, expParams, filters, exclusion, filterFunc, otherFilters, exp); + } + if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) { + List children = (List) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK); + for (ConceptDefinitionComponent c : children) + excludeCodeAndDescendents(wc, cs, system, c, expParams, filters, exclusion, filterFunc, otherFilters, exp); + } + } + + private List getCodesForConcept(ConceptDefinitionComponent focus, Parameters expParams) { List codes = new ArrayList<>(); codes.add(focus.getCode()); @@ -590,8 +616,20 @@ public class ValueSetExpander extends ValueSetProcessBase { excludeCode(wc, exc.getSystem(), c.getCode()); } - if (exc.getFilter().size() > 0) - throw fail("not done yet - multiple filters"); + if (exc.getFilter().size() > 0) { + if (cs.getContent() == CodeSystemContentMode.FRAGMENT) { + addFragmentWarning(exp, cs); + } + List filters = new ArrayList<>(); + for (int i = 1; i < exc.getFilter().size(); i++) { + WorkingContext wc1 = new WorkingContext(); + filters.add(wc1); + processFilter(exc, exp, expParams, null, cs, false, exc.getFilter().get(i), wc1, null, true); + } + ConceptSetFilterComponent fc = exc.getFilter().get(0); + WorkingContext wc1 = dwc; + processFilter(exc, exp, expParams, null, cs, false, fc, wc1, filters, false); + } } private void excludeCodes(WorkingContext wc, ValueSetExpansionComponent expand) { @@ -1083,16 +1121,16 @@ public class ValueSetExpander extends ValueSetProcessBase { for (int i = 1; i < inc.getFilter().size(); i++) { WorkingContext wc = new WorkingContext(); filters.add(wc); - processFilter(inc, exp, expParams, imports, cs, noInactive, inc.getFilter().get(i), wc, null); + processFilter(inc, exp, expParams, imports, cs, noInactive, inc.getFilter().get(i), wc, null, false); } ConceptSetFilterComponent fc = inc.getFilter().get(0); WorkingContext wc = dwc; - processFilter(inc, exp, expParams, imports, cs, noInactive, fc, wc, filters); + processFilter(inc, exp, expParams, imports, cs, noInactive, fc, wc, filters, false); } } - private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, - List imports, CodeSystem cs, boolean noInactive, ConceptSetFilterComponent fc, WorkingContext wc, List filters) + private void processFilter(ConceptSetComponent inc, ValueSetExpansionComponent exp, Parameters expParams, List imports, CodeSystem cs, boolean noInactive, + ConceptSetFilterComponent fc, WorkingContext wc, List filters, boolean exclude) throws ETooCostly { opContext.deadCheck(); if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) { @@ -1100,14 +1138,22 @@ public class ValueSetExpander extends ValueSetProcessBase { ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); if (def == null) throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); - addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + if (exclude) { + excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, null, new AllConceptsFilter(allErrors), filters, exp); + } else { + addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + } } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISNOTA) { // special: all codes in the target code system that are not under the value ConceptDefinitionComponent defEx = getConceptForCode(cs.getConcept(), fc.getValue()); if (defEx == null) throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); for (ConceptDefinitionComponent def : cs.getConcept()) { - addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + if (exclude) { + excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, defEx, new AllConceptsFilter(allErrors), filters, exp); + } else { + addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, defEx, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + } } } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) { // special: all codes in the target code system under the value @@ -1115,11 +1161,20 @@ public class ValueSetExpander extends ValueSetProcessBase { if (def == null) throw failTSE("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); for (ConceptDefinitionComponent c : def.getConcept()) - addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + if (exclude) { + excludeCodeAndDescendents(wc, cs, inc.getSystem(), c, null, imports, null, new AllConceptsFilter(allErrors), filters, exp); + } else { + addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + } if (def.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) { List children = (List) def.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK); - for (ConceptDefinitionComponent c : children) - addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + for (ConceptDefinitionComponent c : children) { + if (exclude) { + excludeCodeAndDescendents(wc, cs, inc.getSystem(), c, null, imports, null, new AllConceptsFilter(allErrors), filters, exp); + } else { + addCodeAndDescendents(wc, cs, inc.getSystem(), c, null, expParams, imports, null, new AllConceptsFilter(allErrors), noInactive, exp.getProperty(), filters, exp); + } + } } } else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) { @@ -1131,19 +1186,31 @@ public class ValueSetExpander extends ValueSetProcessBase { if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) { for (String code : getCodesForConcept(def, expParams)) { opContext.deadCheck(); - ValueSetExpansionContainsComponent t = addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), + if (exclude) { + excludeCode(wc, inc.getSystem(), code); + } else { + addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), null, exp); + } } } } } } else if (isDefinedProperty(cs, fc.getProperty())) { for (ConceptDefinitionComponent def : cs.getConcept()) { - addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(allErrors, fc, getPropertyDefinition(cs, fc.getProperty())), noInactive, exp.getProperty(), filters, exp); + if (exclude) { + excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, null, new PropertyFilter(allErrors, fc, getPropertyDefinition(cs, fc.getProperty())), filters, exp); + } else { + addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new PropertyFilter(allErrors, fc, getPropertyDefinition(cs, fc.getProperty())), noInactive, exp.getProperty(), filters, exp); + } } } else if ("code".equals(fc.getProperty()) && fc.getOp() == FilterOperator.REGEX) { for (ConceptDefinitionComponent def : cs.getConcept()) { - addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new RegexFilter(allErrors, fc.getValue()), noInactive, exp.getProperty(), filters, exp); + if (exclude) { + excludeCodeAndDescendents(wc, cs, inc.getSystem(), def, null, imports, null, new RegexFilter(allErrors, fc.getValue()), filters, exp); + } else { + addCodeAndDescendents(wc, cs, inc.getSystem(), def, null, expParams, imports, null, new RegexFilter(allErrors, fc.getValue()), noInactive, exp.getProperty(), filters, exp); + } } } else { throw fail("Filter by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet");