diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index a1c23af68..cadbb5429 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -866,16 +866,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte if (noTerminologyServer) { return new ValueSetExpansionOutcome(formatMessage(I18nConstants.ERROR_EXPANDING_VALUESET_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, false); } - Map params = new HashMap(); - params.put("_limit", Integer.toString(expandCodesLimit )); - params.put("_incomplete", "true"); + p.addParameter("count", expandCodesLimit); + p.addParameter("offset", 0); txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress()); if (addDependentResources(tc, p, vs)) { p.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId())); } try { - ValueSet result = tc.getClient().expandValueset(vs, p, params); + ValueSet result = tc.getClient().expandValueset(vs, p, null); res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId()); } catch (Exception e) { res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, true); @@ -904,6 +903,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean hierarchical, boolean incompleteOk, Parameters pIn) { + return expandVS(vs, cacheOk, hierarchical, incompleteOk, pIn, false); + } + + public ValueSetExpansionOutcome expandVS(ValueSet vs, boolean cacheOk, boolean hierarchical, boolean incompleteOk, Parameters pIn, boolean noLimits) { if (pIn == null) { throw new Error(formatMessage(I18nConstants.NO_PARAMETERS_PROVIDED_TO_EXPANDVS)); } @@ -934,7 +937,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte return res; } } - + + if (!noLimits) { + p.addParameter("count", expandCodesLimit); + p.addParameter("offset", 0); + } p.setParameter("excludeNested", !hierarchical); if (incompleteOk) { p.setParameter("incomplete-ok", true); @@ -973,15 +980,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte Set systems = findRelevantSystems(vs); TerminologyClientContext tc = terminologyClientManager.chooseServer(systems, true); addDependentResources(tc, p, vs); - - Map params = new HashMap(); - params.put("_limit", Integer.toString(expandCodesLimit )); - params.put("_incomplete", "true"); + txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress()); try { - ValueSet result = tc.getClient().expandValueset(vs, p, params); + ValueSet result = tc.getClient().expandValueset(vs, p, null); if (result != null) { if (!result.hasUrl()) { result.setUrl(vs.getUrl()); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java index 145f39ad0..27e3b18b6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java @@ -1729,6 +1729,11 @@ public String toString() { return this; } + public Parameters addParameter(String name, int i) { + addParameter().setName(name).setValue(new IntegerType(i)); + return this; + } + public Parameters addParameter(String name, String s) { if (s != null) addParameter().setName(name).setValue(new StringType(s)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index 888895d58..b9de46760 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -7,8 +7,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; @@ -191,22 +193,35 @@ public class ValueSetRenderer extends TerminologyRenderer { generateVersionNotice(x, vs.getExpansion(), vs); if (ToolingExtensions.hasExtension(vs.getExpansion(), ToolingExtensions.EXT_EXP_TOOCOSTLY)) { - List exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_TOOCOSTLY); - boolean other = false; - for (Extension ex : exl) { - if (ex.getValue() instanceof BooleanType) { - x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(vs.getExpansion().getContains().isEmpty() ? getContext().getTooCostlyNoteEmpty() : getContext().getTooCostlyNoteNotEmpty()); - } else if (!other) { - x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(vs.getExpansion().getContains().isEmpty() ? getContext().getTooCostlyNoteEmptyDependent() : getContext().getTooCostlyNoteNotEmptyDependent()); - other = true; - } +// List exl = vs.getExpansion().getExtensionsByUrl(ToolingExtensions.EXT_EXP_TOOCOSTLY); +// boolean other = false; +// for (Extension ex : exl) { +// if (ex.getValue() instanceof BooleanType) { +// x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(vs.getExpansion().getContains().isEmpty() ? getContext().getTooCostlyNoteEmpty() : getContext().getTooCostlyNoteNotEmpty()); +// } else if (!other) { +// x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(vs.getExpansion().getContains().isEmpty() ? getContext().getTooCostlyNoteEmptyDependent() : getContext().getTooCostlyNoteNotEmptyDependent()); +// other = true; +// } +// } + String msg = null; + if (vs.getExpansion().getContains().isEmpty()) { + msg = "This value set cannot be expanded because of the way it is defined - it has an infinite number of members"; // not sure that's true? + } else { + msg = "This value set cannot be fully expanded, but a selection ("+countMembership(vs)+" codes) of the whole set of codes is shown here"; } + x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(msg); } else { - Integer count = countMembership(vs); - if (count == null) - x.para().tx("This value set does not contain a fixed number of concepts"); - else - x.para().tx("This value set contains "+(hasFragment ? "at least " : "")+count.toString()+" concepts"); + int count = conceptCount(vs.getExpansion().getContains()); + if (vs.getExpansion().hasTotal()) { + if (count != vs.getExpansion().getTotal()) { + x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px") + .addText("This value set has "+(hasFragment ? "at least " : "")+vs.getExpansion().getTotal()+" codes in it. In order to keep the publication size manageable, only a selection ("+count+" codes) of the whole set of codes is shown"); + } else { + x.para().tx("This value set contains "+(hasFragment ? "at least " : "")+vs.getExpansion().getTotal()+" concepts"); + } + } else { + x.para().tx("This value set expansion contains "+count+" concepts"); + } } @@ -403,7 +418,6 @@ public class ValueSetRenderer extends TerminologyRenderer { return null; } - private Integer countMembership(ValueSet vs) { int count = 0; if (vs.hasExpansion()) @@ -457,9 +471,11 @@ public class ValueSetRenderer extends TerminologyRenderer { @SuppressWarnings("rawtypes") private void generateVersionNotice(XhtmlNode x, ValueSetExpansionComponent expansion, Resource vs) { Multimap versions = HashMultimap.create(); + Set vlist = new HashSet<>(); for (ValueSetExpansionParameterComponent p : expansion.getParameter()) { - if (p.getName().startsWith("used-") || p.getName().equals("version")) { + if ((p.getName().startsWith("used-") || p.getName().equals("version")) && !vlist.contains(p.getValue().primitiveValue())) { String name = p.getName().equals("version") ? "system" : p.getName().substring(5); + vlist.add(p.getValue().primitiveValue()); String[] parts = ((PrimitiveType) p.getValue()).asStringValue().split("\\|"); if (parts.length == 2 && !Utilities.noString(parts[0])) versions.put(name+"|"+parts[0], parts[1]); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java index 6f4886c7b..5442dc28d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetUtilities.java @@ -60,6 +60,7 @@ import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; +import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter; @@ -456,5 +457,29 @@ public class ValueSetUtilities extends TerminologyUtilities { } return systems; } + + + private static int conceptCount(List list) { + int count = 0; + for (ValueSetExpansionContainsComponent c : list) { + if (!c.getAbstract()) + count++; + count = count + conceptCount(c.getContains()); + } + return count; + } + + + public static boolean isIncompleteExpansion(ValueSet valueSet) { + if (valueSet.hasExpansion()) { + ValueSetExpansionComponent exp = valueSet.getExpansion(); + if (exp.hasTotal()) { + if (exp.getTotal() != conceptCount(exp.getContains())) { + return true; + } + } + } + return false; + } } \ No newline at end of file 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 932594c25..01581ce8d 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 @@ -35,6 +35,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.FileNotFoundException; import java.io.IOException; +import java.text.MessageFormat; /* * Copyright (c) 2011+, HL7, Inc @@ -186,7 +187,7 @@ public class ValueSetExpander extends ValueSetProcessBase { private ValueSetExpansionContainsComponent addCode(WorkingContext wc, String system, String code, String display, String dispLang, ValueSetExpansionContainsComponent parent, List designations, Parameters expParams, boolean isAbstract, boolean inactive, List filters, boolean noInactive, boolean deprecated, List vsProp, - List csProps, CodeSystem cs, List expProps, List csExtList, List vsExtList, ValueSetExpansionComponent exp) throws ETooCostly { + List csProps, CodeSystem cs, List expProps, List csExtList, List vsExtList, ValueSetExpansionComponent exp, boolean countToTotal) throws ETooCostly { opContext.deadCheck(); if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code, exp)) @@ -323,14 +324,16 @@ public class ValueSetExpander extends ValueSetProcessBase { } else { wc.getCodes().add(n); wc.getMap().put(s, n); - wc.incTotal(); - if (wc == dwc && wc.getTotal() > maxExpansionSize) { - if (wc.getOffset()+wc.getCount() > 0 && wc.getTotal() > wc.getOffset()+wc.getCount()) { - wc.setTotal(-1); - throw new EFinished(); - } - throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY, focus.getUrl(), ">" + Integer.toString(maxExpansionSize))); + if (countToTotal) { + wc.incTotal(); } +// if (wc == dwc && wc.getTotal() > maxExpansionSize) { +// if (wc.getOffset()+wc.getCount() > 0 && wc.getTotal() > wc.getOffset()+wc.getCount()) { +// wc.setTotal(-1); +// throw new EFinished(); +// } +// throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY, focus.getUrl(), ">" + Integer.toString(maxExpansionSize))); +// } } if (wc.isCanBeHeirarchy() && parent != null) { parent.getContains().add(n); @@ -443,19 +446,19 @@ public class ValueSetExpander extends ValueSetProcessBase { return null; } - private void addCodeAndDescendents(WorkingContext wc, ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List filters, boolean noInactive, List vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp) throws FHIRException, ETooCostly { + private void addCodeAndDescendents(WorkingContext wc, ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List filters, boolean noInactive, List vsProps, ValueSet vsSrc, ValueSetExpansionComponent exp, boolean countToTotal) throws FHIRException, ETooCostly { opContext.deadCheck(); focus.checkNoModifiers("Expansion.contains", "expanding"); ValueSetExpansionContainsComponent np = null; for (String code : getCodesForConcept(focus, expParams)) { ValueSetExpansionContainsComponent t = addCode(wc, focus.getSystem(), code, focus.getDisplay(), vsSrc.getLanguage(), parent, - convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), filters, noInactive, false, vsProps, makeCSProps(focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), null), null, focus.getProperty(), null, focus.getExtension(), exp); + convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), filters, noInactive, false, vsProps, makeCSProps(focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), null), null, focus.getProperty(), null, focus.getExtension(), exp, countToTotal); if (np == null) { np = t; } } for (ValueSetExpansionContainsComponent c : focus.getContains()) - addCodeAndDescendents(wc, c, np, expParams, filters, noInactive, vsProps, vsSrc, exp); + addCodeAndDescendents(wc, c, np, expParams, filters, noInactive, vsProps, vsSrc, exp, countToTotal); } private List makeCSProps(String definition, List list) { @@ -506,7 +509,7 @@ public class ValueSetExpander extends ValueSetProcessBase { boolean dep = CodeSystemUtilities.isDeprecated(cs, def, false); if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def) && passesOtherFilters(otherFilters, cs, def.getCode())) { for (String code : getCodesForConcept(def, expParams)) { - ValueSetExpansionContainsComponent t = addCode(wc, system, code, def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, filters, noInactive, dep, vsProps, makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), null, exp); + ValueSetExpansionContainsComponent t = addCode(wc, system, code, def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, filters, noInactive, dep, vsProps, makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), null, exp, true); if (np == null) { np = t; } @@ -579,7 +582,7 @@ public class ValueSetExpander extends ValueSetProcessBase { if (!existsInParams(params, p.getName(), p.getValue())) params.add(p); } - + copyImportContains(expand.getContains(), null, expParams, filters, noInactive, vsProps, vsSrc, exp); } } @@ -707,17 +710,17 @@ public class ValueSetExpander extends ValueSetProcessBase { if ("offset".equals(name) && value instanceof IntegerType) { focus.getExpansion().getParameter().removeIf(p -> p.getName().equals(name)); focus.getExpansion().addParameter().setName(name).setValue(value); - dwc.setOffset(((IntegerType) value).getValue()); - if (dwc.getOffset() < 0) { - dwc.setOffset(0); + dwc.setOffsetParam(((IntegerType) value).getValue()); + if (dwc.getOffsetParam() < 0) { + dwc.setOffsetParam(0); } } if ("count".equals(name)) { focus.getExpansion().getParameter().removeIf(p -> p.getName().equals(name)); focus.getExpansion().addParameter().setName(name).setValue(value); - dwc.setCount(((IntegerType) value).getValue()); - if (dwc.getCount() < 0) { - dwc.setCount(0); + dwc.setCountParam(((IntegerType) value).getValue()); + if (dwc.getCountParam() < 0) { + dwc.setCountParam(0); } } } @@ -757,7 +760,13 @@ public class ValueSetExpander extends ValueSetProcessBase { // nothing - we intended to trap this here } - if (dwc.isCanBeHeirarchy()) { + if (dwc.getTotal() > maxExpansionSize && dwc.getOffsetParam() + dwc.getCountParam() == 0) { + if (dwc.isNoTotal()) { + throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY, focus.getVersionedUrl(), ">" + MessageFormat.format("{0,number,#}", maxExpansionSize))); + } else { + throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_COUNT, focus.getVersionedUrl(), ">" + MessageFormat.format("{0,number,#}", maxExpansionSize), MessageFormat.format("{0,number,#}", dwc.getTotal()))); + } + } else if (dwc.isCanBeHeirarchy() && (dwc.getCountParam() == 0) || dwc.getCountParam() > dwc.getCodes().size()) { for (ValueSetExpansionContainsComponent c : dwc.getRoots()) { focus.getExpansion().getContains().add(c); } @@ -766,11 +775,11 @@ public class ValueSetExpander extends ValueSetProcessBase { int cc = 0; for (ValueSetExpansionContainsComponent c : dwc.getCodes()) { if (dwc.getMap().containsKey(key(c)) && (includeAbstract || !c.getAbstract())) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them - if (dwc.getOffset() == 0 || i >= dwc.getOffset()) { + if (dwc.getOffsetParam() == 0 || i >= dwc.getOffsetParam()) { focus.getExpansion().getContains().add(c); c.getContains().clear(); // make sure any heirarchy is wiped cc++; - if (cc == dwc.getCount()) { + if (cc == dwc.getCountParam()) { break; } } @@ -779,10 +788,10 @@ public class ValueSetExpander extends ValueSetProcessBase { } } - if (dwc.hasOffset()) { - focus.getExpansion().setOffset(dwc.getOffset()); + if (dwc.hasOffsetParam()) { + focus.getExpansion().setOffset(dwc.getOffsetParam()); } - if (dwc.getTotal() >= 0) { + if (!dwc.isNoTotal()) { focus.getExpansion().setTotal(dwc.getTotal()); } if (!requiredSupplements.isEmpty()) { @@ -826,7 +835,7 @@ public class ValueSetExpander extends ValueSetProcessBase { // Exclude comes first because we build up a map of things to exclude for (ConceptSetComponent inc : compose.getExclude()) excludeCodes(dwc, inc, expParams, exp, valueSet); - dwc.setCanBeHeirarchy(!expParams.getParameterBool("excludeNested") && dwc.getExcludeKeys().isEmpty() && dwc.getExcludeSystems().isEmpty() && dwc.getOffset()+dwc.getCount() == 0); + dwc.setCanBeHeirarchy(!expParams.getParameterBool("excludeNested") && dwc.getExcludeKeys().isEmpty() && dwc.getExcludeSystems().isEmpty() && dwc.getOffsetParam() == 0); includeAbstract = !expParams.getParameterBool("excludeNotForUI"); boolean first = true; for (ConceptSetComponent inc : compose.getInclude()) { @@ -878,7 +887,8 @@ public class ValueSetExpander extends ValueSetProcessBase { if (!existsInParams(exp.getParameter(), "used-valueset", u)) exp.getParameter().add(new ValueSetExpansionParameterComponent().setName("used-valueset").setValue(u)); } - for (Extension ex : vso.getValueset().getExpansion().getExtension()) { + ValueSetExpansionComponent evs = vso.getValueset().getExpansion(); + for (Extension ex : evs.getExtension()) { if (ex.getUrl().equals(ToolingExtensions.EXT_EXP_TOOCOSTLY)) { if (ex.getValue() instanceof BooleanType) { exp.getExtension().add(new Extension(ToolingExtensions.EXT_EXP_TOOCOSTLY).setValue(new CanonicalType(value))); @@ -887,12 +897,17 @@ public class ValueSetExpander extends ValueSetProcessBase { } } } - for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { + if (evs.hasTotal()) { + dwc.incTotal(evs.getTotal()); + } else { + dwc.setNoTotal(true); + } + for (ValueSetExpansionParameterComponent p : evs.getParameter()) { if (!existsInParams(exp.getParameter(), p.getName(), p.getValue())) exp.getParameter().add(p); } if (isValueSetUnionImports(valueSet)) { - copyExpansion(wc, vso.getValueset().getExpansion().getContains()); + copyExpansion(wc, evs.getContains()); } wc.setCanBeHeirarchy(false); // if we're importing a value set, we have to be combining, so we won't try for a heirarchy return vso.getValueset(); @@ -976,7 +991,7 @@ public class ValueSetExpander extends ValueSetProcessBase { for (ValueSetExpansionContainsComponent c : list) { c.checkNoModifiers("Imported Expansion in Code System", "expanding"); ValueSetExpansionContainsComponent np = addCode(dwc, c.getSystem(), c.getCode(), c.getDisplay(), vsSrc.getLanguage(), parent, null, expParams, c.getAbstract(), c.getInactive(), - filter, noInactive, false, vsProps, makeCSProps(c.getExtensionString(ToolingExtensions.EXT_DEFINITION), null), null, c.getProperty(), null, c.getExtension(), exp); + filter, noInactive, false, vsProps, makeCSProps(c.getExtensionString(ToolingExtensions.EXT_DEFINITION), null), null, c.getProperty(), null, c.getExtension(), exp, false); copyImportContains(c.getContains(), np, expParams, filter, noInactive, vsProps, vsSrc, exp); } } @@ -1031,6 +1046,11 @@ public class ValueSetExpander extends ValueSetProcessBase { exp.getParameter().add(new ValueSetExpansionParameterComponent().setName("used-valueset").setValue(u)); } } + if (vs.getExpansion().hasTotal()) { + dwc.incTotal(vs.getExpansion().getTotal()); + } else { + dwc.setNoTotal(true); + } for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { if (!existsInParams(exp.getParameter(), p.getName(), p.getValue())) { exp.getParameter().add(p); @@ -1044,7 +1064,7 @@ public class ValueSetExpander extends ValueSetProcessBase { } } for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) { - addCodeAndDescendents(dwc, cc, null, expParams, imports, noInactive, vsProps, vs, exp); + addCodeAndDescendents(dwc, cc, null, expParams, imports, noInactive, vsProps, vs, exp, !vs.getExpansion().hasTotal()); } } @@ -1109,7 +1129,7 @@ public class ValueSetExpander extends ValueSetProcessBase { inactive = CodeSystemUtilities.isInactive(cs, def); isAbstract = CodeSystemUtilities.isNotSelectable(cs, def); addCode(dwc, inc.getSystem(), c.getCode(), !Utilities.noString(c.getDisplay()) ? c.getDisplay() : def.getDisplay(), c.hasDisplay() ? vsSrc.getLanguage() : cs.getLanguage(), null, mergeDesignations(def, convertDesignations(c.getDesignation())), - expParams, isAbstract, inactive, imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), c.getExtension(), exp); + expParams, isAbstract, inactive, imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), c.getExtension(), exp, true); } } } @@ -1193,7 +1213,7 @@ public class ValueSetExpander extends ValueSetProcessBase { 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); + imports, noInactive, false, exp.getProperty(), makeCSProps(def.getDefinition(), def.getProperty()), cs, null, def.getExtension(), null, exp, true); } } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/WorkingContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/WorkingContext.java index 344fd16e8..f0a9e2e1a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/WorkingContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/WorkingContext.java @@ -18,10 +18,10 @@ class WorkingContext { private Set excludeSystems = new HashSet(); private boolean canBeHeirarchy = true; - private int offset; - private boolean offs; - private int count; - private int total; + private Integer offsetParam; + private Integer countParam; // allowed count. Because of internal processing, we allow more + private int total; // running count. This might be more than actually seen if we call out to an external server and only get the first 1000 codes + private boolean noTotal; // we lost count of the correct total public List getCodes() { return codes; @@ -51,37 +51,48 @@ class WorkingContext { this.canBeHeirarchy = canBeHeirarchy; } - public int getOffset() { - return offset; + public boolean hasOffsetParam() { + return offsetParam != null; } - public void setOffset(int offset) { - this.offs = true; - this.offset = offset; + public int getOffsetParam() { + return offsetParam == null ? 0 : offsetParam; } - public boolean hasOffset() { - return offs; + public void setOffsetParam(int offsetParam) { + this.offsetParam = offsetParam; } - public int getCount() { - return count; + public boolean hasCountParam() { + return countParam != null; } - public void setCount(int count) { - this.count = count; + public int getCountParam() { + return countParam == null ? 0 : countParam; + } + + public void setCountParam(int countParam) { + this.countParam = countParam; } public int getTotal() { return total; } - - public void setTotal(int total) { - this.total = total; - } public void incTotal() { total++; } + + public void incTotal(int amount) { + total += amount; + } + + public boolean isNoTotal() { + return noTotal; + } + + public void setNoTotal(boolean noTotal) { + this.noTotal = noTotal; + } } \ No newline at end of file diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 08a496853..661b7fb71 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -878,6 +878,7 @@ public class I18nConstants { public static final String UNKNOWN_CODESYSTEM = "UNKNOWN_CODESYSTEM"; public static final String UNKNOWN_CODESYSTEM_VERSION = "UNKNOWN_CODESYSTEM_VERSION"; public static final String VALUESET_TOO_COSTLY = "VALUESET_TOO_COSTLY"; + public static final String VALUESET_TOO_COSTLY_COUNT = "VALUESET_TOO_COSTLY_COUNT"; public static final String VALUESET_TOO_COSTLY_TIME = "VALUESET_TOO_COSTLY_TIME"; public static final String NO_VALID_DISPLAY_FOUND = "NO_VALID_DISPLAY_FOUND"; public static final String SD_NO_CONTEXT_WHEN_NOT_EXTENSION = "SD_NO_CONTEXT_WHEN_NOT_EXTENSION"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index eaaa56314..ae97c7812 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -933,8 +933,9 @@ SD_NO_TYPE_CODE_ON_CODE = Snapshot for {1} element {0} has type.code without a v UNKNOWN_CODESYSTEM = A definition for CodeSystem ''{0}'' could not be found, so the code cannot be validated UNKNOWN_CODESYSTEM_VERSION = A definition for CodeSystem ''{0}'' version ''{1}'' could not be found, so the code cannot be validated. Valid versions: {2} UNABLE_TO_INFER_CODESYSTEM = The System URI could not be determined for the code {0} in the ValueSet {1} -VALUESET_TOO_COSTLY = The value set {0} has too many codes to display ({1}) -VALUESET_TOO_COSTLY_TIME = The value set {0} took too long to process (>{1}sec) +VALUESET_TOO_COSTLY = The value set ''{0}'' expansion has too many codes to display ({1}) +VALUESET_TOO_COSTLY_COUNT = The value set ''{0}'' expansion has {2} codes, which is too many to display ({1}) +VALUESET_TOO_COSTLY_TIME = The value set ''{0}'' expansion took too long to process (>{1}sec) NO_VALID_DISPLAY_FOUND_one = No valid Display Names found for {1}#{2} in the language {4} NO_VALID_DISPLAY_FOUND_other = No valid Display Names found for {1}#{2} in the languages {4} SD_NO_CONTEXT_WHEN_NOT_EXTENSION = The type is {0} so an extension context should not be specified diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java index 2891f4ecf..957dab3a2 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java @@ -164,7 +164,7 @@ public class TerminologyServiceTests { if (lang != null && !p.hasParameter("displayLanguage")) { p.addParameter("displayLanguage", new CodeType(lang)); } - ValueSetExpansionOutcome vse = engine.getContext().expandVS(vs, false, hierarchical, false, p); + ValueSetExpansionOutcome vse = engine.getContext().expandVS(vs, false, hierarchical, false, p, true); if (resp.contains("\"ValueSet\"")) { if (vse.getValueset() == null) { Assertions.fail(vse.getError()); diff --git a/pom.xml b/pom.xml index 5c2ca3214..9abd25099 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 32.0.1-jre 6.4.1 - 1.4.25 + 1.4.26-SNAPSHOT 2.16.0 5.9.2 1.8.2