001package org.hl7.fhir.r4.terminologies; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032import static org.apache.commons.lang3.StringUtils.isNotBlank; 033 034import java.io.FileNotFoundException; 035import java.io.IOException; 036 037/* 038 * Copyright (c) 2011+, HL7, Inc 039 * All rights reserved. 040 * 041 * Redistribution and use in source and binary forms, with or without modification, 042 * are permitted provided that the following conditions are met: 043 * 044 * Redistributions of source code must retain the above copyright notice, this 045 * list of conditions and the following disclaimer. 046 * Redistributions in binary form must reproduce the above copyright notice, 047 * this list of conditions and the following disclaimer in the documentation 048 * and/or other materials provided with the distribution. 049 * Neither the name of HL7 nor the names of its contributors may be used to 050 * endorse or promote products derived from this software without specific 051 * prior written permission. 052 * 053 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 054 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 055 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 056 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 057 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 058 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 059 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 060 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 061 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 062 * POSSIBILITY OF SUCH DAMAGE. 063 * 064 */ 065 066import java.util.ArrayList; 067import java.util.HashMap; 068import java.util.HashSet; 069import java.util.List; 070import java.util.Map; 071import java.util.Set; 072 073import org.apache.commons.lang3.NotImplementedException; 074import org.hl7.fhir.exceptions.FHIRException; 075import org.hl7.fhir.exceptions.FHIRFormatError; 076import org.hl7.fhir.exceptions.NoTerminologyServiceException; 077import org.hl7.fhir.exceptions.TerminologyServiceException; 078import org.hl7.fhir.r4.context.IWorkerContext; 079import org.hl7.fhir.r4.model.CodeSystem; 080import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode; 081import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; 082import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionDesignationComponent; 083import org.hl7.fhir.r4.model.DateTimeType; 084import org.hl7.fhir.r4.model.Factory; 085import org.hl7.fhir.r4.model.Parameters; 086import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; 087import org.hl7.fhir.r4.model.PrimitiveType; 088import org.hl7.fhir.r4.model.Type; 089import org.hl7.fhir.r4.model.UriType; 090import org.hl7.fhir.r4.model.ValueSet; 091import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceComponent; 092import org.hl7.fhir.r4.model.ValueSet.ConceptReferenceDesignationComponent; 093import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent; 094import org.hl7.fhir.r4.model.ValueSet.ConceptSetFilterComponent; 095import org.hl7.fhir.r4.model.ValueSet.FilterOperator; 096import org.hl7.fhir.r4.model.ValueSet.ValueSetComposeComponent; 097import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent; 098import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; 099import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionParameterComponent; 100import org.hl7.fhir.r4.utils.ToolingExtensions; 101import org.hl7.fhir.utilities.Utilities; 102 103public class ValueSetExpanderSimple implements ValueSetExpander { 104 105 private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>(); 106 private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>(); 107 private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>(); 108 private IWorkerContext context; 109 private boolean canBeHierarchy = true; 110 private Set<String> excludeKeys = new HashSet<String>(); 111 private Set<String> excludeSystems = new HashSet<String>(); 112 private ValueSet focus; 113 private int maxExpansionSize = 500; 114 115 private int total; 116 117 public ValueSetExpanderSimple(IWorkerContext context) { 118 super(); 119 this.context = context; 120 } 121 122 public void setMaxExpansionSize(int theMaxExpansionSize) { 123 maxExpansionSize = theMaxExpansionSize; 124 } 125 126 private ValueSetExpansionContainsComponent addCode(String system, String code, String display, 127 ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, 128 Parameters expParams, boolean isAbstract, boolean inactive, List<ValueSet> filters) { 129 130 if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code)) 131 return null; 132 ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); 133 n.setSystem(system); 134 n.setCode(code); 135 if (isAbstract) 136 n.setAbstract(true); 137 if (inactive) 138 n.setInactive(true); 139 140 if (expParams.getParameterBool("includeDesignations") && designations != null) { 141 for (ConceptDefinitionDesignationComponent t : designations) { 142 ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue()); 143 } 144 } 145 ConceptDefinitionDesignationComponent t = expParams.hasLanguage() 146 ? getMatchingLang(designations, expParams.getLanguage()) 147 : null; 148 if (t == null) 149 n.setDisplay(display); 150 else 151 n.setDisplay(t.getValue()); 152 153 String s = key(n); 154 if (map.containsKey(s) || excludeKeys.contains(s)) { 155 canBeHierarchy = false; 156 } else { 157 codes.add(n); 158 map.put(s, n); 159 total++; 160 } 161 if (canBeHierarchy && parent != null) { 162 parent.getContains().add(n); 163 } else { 164 roots.add(n); 165 } 166 return n; 167 } 168 169 private boolean filterContainsCode(List<ValueSet> filters, String system, String code) { 170 for (ValueSet vse : filters) 171 if (expansionContainsCode(vse.getExpansion().getContains(), system, code)) 172 return true; 173 return false; 174 } 175 176 private boolean expansionContainsCode(List<ValueSetExpansionContainsComponent> contains, String system, String code) { 177 for (ValueSetExpansionContainsComponent cc : contains) { 178 if (system.equals(cc.getSystem()) && code.equals(cc.getCode())) 179 return true; 180 if (expansionContainsCode(cc.getContains(), system, code)) 181 return true; 182 } 183 return false; 184 } 185 186 private ConceptDefinitionDesignationComponent getMatchingLang(List<ConceptDefinitionDesignationComponent> list, 187 String lang) { 188 for (ConceptDefinitionDesignationComponent t : list) 189 if (t.getLanguage().equals(lang)) 190 return t; 191 for (ConceptDefinitionDesignationComponent t : list) 192 if (t.getLanguage().startsWith(lang)) 193 return t; 194 return null; 195 } 196 197 private void addCodeAndDescendents(ValueSetExpansionContainsComponent focus, 198 ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters) throws FHIRException { 199 focus.checkNoModifiers("Expansion.contains", "expanding"); 200 ValueSetExpansionContainsComponent np = addCode(focus.getSystem(), focus.getCode(), focus.getDisplay(), parent, 201 convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), filters); 202 for (ValueSetExpansionContainsComponent c : focus.getContains()) 203 addCodeAndDescendents(focus, np, expParams, filters); 204 } 205 206 private List<ConceptDefinitionDesignationComponent> convert(List<ConceptReferenceDesignationComponent> designations) { 207 List<ConceptDefinitionDesignationComponent> list = new ArrayList<ConceptDefinitionDesignationComponent>(); 208 for (ConceptReferenceDesignationComponent d : designations) { 209 ConceptDefinitionDesignationComponent n = new ConceptDefinitionDesignationComponent(); 210 n.setLanguage(d.getLanguage()); 211 n.setUse(d.getUse()); 212 n.setValue(d.getValue()); 213 list.add(n); 214 } 215 return list; 216 } 217 218 private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, 219 ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filters, 220 ConceptDefinitionComponent exclusion) throws FHIRException { 221 def.checkNoModifiers("Code in Code System", "expanding"); 222 if (exclusion != null) { 223 if (exclusion.getCode().equals(def.getCode())) 224 return; // excluded. 225 } 226 if (!CodeSystemUtilities.isDeprecated(cs, def)) { 227 ValueSetExpansionContainsComponent np = null; 228 boolean abs = CodeSystemUtilities.isNotSelectable(cs, def); 229 boolean inc = CodeSystemUtilities.isInactive(cs, def); 230 if (canBeHierarchy || !abs) 231 np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), expParams, abs, inc, 232 filters); 233 for (ConceptDefinitionComponent c : def.getConcept()) 234 addCodeAndDescendents(cs, system, c, np, expParams, filters, exclusion); 235 } else { 236 for (ConceptDefinitionComponent c : def.getConcept()) 237 addCodeAndDescendents(cs, system, c, null, expParams, filters, exclusion); 238 } 239 240 } 241 242 private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, 243 Parameters expParams, List<ValueSet> filters) throws ETooCostly, FHIRException { 244 if (expand != null) { 245 if (expand.getContains().size() > maxExpansionSize) 246 throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); 247 for (ValueSetExpansionParameterComponent p : expand.getParameter()) { 248 if (!existsInParams(params, p.getName(), p.getValue())) 249 params.add(p); 250 } 251 252 copyImportContains(expand.getContains(), null, expParams, filters); 253 } 254 } 255 256 private void excludeCode(String theSystem, String theCode) { 257 ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); 258 n.setSystem(theSystem); 259 n.setCode(theCode); 260 String s = key(n); 261 excludeKeys.add(s); 262 } 263 264 private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params, String ctxt) 265 throws FHIRException { 266 exc.checkNoModifiers("Compose.exclude", "expanding"); 267 if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) { 268 excludeSystems.add(exc.getSystem()); 269 } 270 271 if (exc.hasValueSet()) 272 throw new Error("Processing Value set references in exclude is not yet done in " + ctxt); 273 // importValueSet(imp.getValue(), params, expParams); 274 275 CodeSystem cs = context.fetchCodeSystem(exc.getSystem()); 276 if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) { 277 ValueSetExpansionOutcome vse = context.expandVS(exc, false); 278 ValueSet valueset = vse.getValueset(); 279 if (valueset == null) 280 throw new TerminologyServiceException("Error Expanding ValueSet: " + vse.getError()); 281 excludeCodes(valueset.getExpansion(), params); 282 return; 283 } 284 285 for (ConceptReferenceComponent c : exc.getConcept()) { 286 excludeCode(exc.getSystem(), c.getCode()); 287 } 288 289 if (exc.getFilter().size() > 0) 290 throw new NotImplementedException("not done yet"); 291 } 292 293 private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) { 294 for (ValueSetExpansionContainsComponent c : expand.getContains()) { 295 excludeCode(c.getSystem(), c.getCode()); 296 } 297 } 298 299 private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) { 300 for (ValueSetExpansionParameterComponent p : params) { 301 if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false)) 302 return true; 303 } 304 return false; 305 } 306 307 @Override 308 public ValueSetExpansionOutcome expand(ValueSet source, Parameters expParams) { 309 try { 310 return doExpand(source, expParams); 311 } catch (NoTerminologyServiceException e) { 312 // well, we couldn't expand, so we'll return an interface to a checker that can 313 // check membership of the set 314 // that might fail too, but it might not, later. 315 return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.NOSERVICE); 316 } catch (RuntimeException e) { 317 // TODO: we should put something more specific instead of just Exception below, 318 // since 319 // it swallows bugs.. what would be expected to be caught there? 320 throw e; 321 } catch (Exception e) { 322 // well, we couldn't expand, so we'll return an interface to a checker that can 323 // check membership of the set 324 // that might fail too, but it might not, later. 325 return new ValueSetExpansionOutcome(e.getMessage(), TerminologyServiceErrorClass.UNKNOWN); 326 } 327 } 328 329 public ValueSetExpansionOutcome doExpand(ValueSet source, Parameters expParams) 330 throws FHIRException, ETooCostly, FileNotFoundException, IOException { 331 if (expParams == null) 332 expParams = makeDefaultExpansion(); 333 source.checkNoModifiers("ValueSet", "expanding"); 334 focus = source.copy(); 335 focus.setExpansion(new ValueSet.ValueSetExpansionComponent()); 336 focus.getExpansion().setTimestampElement(DateTimeType.now()); 337 focus.getExpansion().setIdentifier(Factory.createUUID()); 338 for (ParametersParameterComponent p : expParams.getParameter()) { 339 if (Utilities.existsInList(p.getName(), "includeDesignations", "excludeNested")) 340 focus.getExpansion().addParameter().setName(p.getName()).setValue(p.getValue()); 341 } 342 343 if (source.hasCompose()) 344 handleCompose(source.getCompose(), focus.getExpansion().getParameter(), expParams, source.getUrl()); 345 346 if (canBeHierarchy) { 347 for (ValueSetExpansionContainsComponent c : roots) { 348 focus.getExpansion().getContains().add(c); 349 } 350 } else { 351 for (ValueSetExpansionContainsComponent c : codes) { 352 if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still 353 // thought it might be heirarchical, but later we gave up, so 354 // now ignore them 355 focus.getExpansion().getContains().add(c); 356 c.getContains().clear(); // make sure any hierarchy is wiped 357 } 358 } 359 } 360 361 if (total > 0) { 362 focus.getExpansion().setTotal(total); 363 } 364 365 return new ValueSetExpansionOutcome(focus); 366 } 367 368 private Parameters makeDefaultExpansion() { 369 Parameters res = new Parameters(); 370 res.addParameter("excludeNested", true); 371 res.addParameter("includeDesignations", false); 372 return res; 373 } 374 375 private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException { 376 ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code); 377 if (def == null) 378 throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl()); 379 return def.getDisplay(); 380 } 381 382 private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) { 383 for (ConceptDefinitionComponent c : clist) { 384 if (code.equals(c.getCode())) 385 return c; 386 ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code); 387 if (v != null) 388 return v; 389 } 390 return null; 391 } 392 393 private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, 394 Parameters expParams, String ctxt) throws ETooCostly, FileNotFoundException, IOException, FHIRException { 395 compose.checkNoModifiers("ValueSet.compose", "expanding"); 396 // Exclude comes first because we build up a map of things to exclude 397 for (ConceptSetComponent inc : compose.getExclude()) 398 excludeCodes(inc, params, ctxt); 399 canBeHierarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty(); 400 boolean first = true; 401 for (ConceptSetComponent inc : compose.getInclude()) { 402 if (first == true) 403 first = false; 404 else 405 canBeHierarchy = false; 406 includeCodes(inc, params, expParams, canBeHierarchy); 407 } 408 409 } 410 411 private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, Parameters expParams) 412 throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException, FHIRFormatError { 413 if (value == null) 414 throw new TerminologyServiceException("unable to find value set with no identity"); 415 ValueSet vs = context.fetchResource(ValueSet.class, value); 416 if (vs == null) 417 throw new TerminologyServiceException("Unable to find imported value set " + value); 418 ValueSetExpansionOutcome vso = new ValueSetExpanderSimple(context).expand(vs, expParams); 419 if (vso.getError() != null) 420 throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError()); 421 if (vs.hasVersion()) 422 if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion()))) 423 params.add(new ValueSetExpansionParameterComponent().setName("version") 424 .setValue(new UriType(vs.getUrl() + "|" + vs.getVersion()))); 425 for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { 426 if (!existsInParams(params, p.getName(), p.getValue())) 427 params.add(p); 428 } 429 canBeHierarchy = false; // if we're importing a value set, we have to be combining, so we won't try for 430 // a hierarchy 431 return vso.getValueset(); 432 } 433 434 private void copyImportContains(List<ValueSetExpansionContainsComponent> list, 435 ValueSetExpansionContainsComponent parent, Parameters expParams, List<ValueSet> filter) throws FHIRException { 436 for (ValueSetExpansionContainsComponent c : list) { 437 c.checkNoModifiers("Imported Expansion in Code System", "expanding"); 438 ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, 439 expParams, c.getAbstract(), c.getInactive(), filter); 440 copyImportContains(c.getContains(), np, expParams, filter); 441 } 442 } 443 444 private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, 445 Parameters expParams, boolean heirarchical) throws ETooCostly, FileNotFoundException, IOException, FHIRException { 446 inc.checkNoModifiers("Compose.include", "expanding"); 447 List<ValueSet> imports = new ArrayList<ValueSet>(); 448 for (UriType imp : inc.getValueSet()) { 449 imports.add(importValueSet(imp.getValue(), params, expParams)); 450 } 451 452 if (!inc.hasSystem()) { 453 if (imports.isEmpty()) // though this is not supposed to be the case 454 return; 455 ValueSet base = imports.get(0); 456 imports.remove(0); 457 base.checkNoModifiers("Imported ValueSet", "expanding"); 458 copyImportContains(base.getExpansion().getContains(), null, expParams, imports); 459 } else { 460 CodeSystem cs = context.fetchCodeSystem(inc.getSystem()); 461 if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE)) { 462 doServerIncludeCodes(inc, heirarchical, params, imports, expParams); 463 } else { 464 doInternalIncludeCodes(inc, params, expParams, imports, cs); 465 } 466 } 467 } 468 469 private void doServerIncludeCodes(ConceptSetComponent inc, boolean heirarchical, 470 List<ValueSetExpansionParameterComponent> params, List<ValueSet> imports, Parameters expParams) 471 throws FHIRException { 472 ValueSetExpansionOutcome vso = context.expandVS(inc, heirarchical); 473 if (vso.getError() != null) 474 throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError()); 475 ValueSet vs = vso.getValueset(); 476 if (vs.hasVersion()) 477 if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion()))) 478 params.add(new ValueSetExpansionParameterComponent().setName("version") 479 .setValue(new UriType(vs.getUrl() + "|" + vs.getVersion()))); 480 for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { 481 if (!existsInParams(params, p.getName(), p.getValue())) 482 params.add(p); 483 } 484 for (ValueSetExpansionContainsComponent cc : vs.getExpansion().getContains()) { 485 addCodeAndDescendents(cc, null, expParams, imports); 486 } 487 } 488 489 public void doInternalIncludeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, 490 Parameters expParams, List<ValueSet> imports, CodeSystem cs) 491 throws NoTerminologyServiceException, TerminologyServiceException, FHIRException { 492 if (cs == null) { 493 if (context.isNoTerminologyServer()) 494 throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString()); 495 else 496 throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString()); 497 } 498 cs.checkNoModifiers("Code System", "expanding"); 499 if (cs.getContent() != CodeSystemContentMode.COMPLETE) 500 throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete"); 501 if (cs.hasVersion()) 502 if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion()))) 503 params.add(new ValueSetExpansionParameterComponent().setName("version") 504 .setValue(new UriType(cs.getUrl() + "|" + cs.getVersion()))); 505 506 if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) { 507 // special case - add all the code system 508 for (ConceptDefinitionComponent def : cs.getConcept()) { 509 addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null); 510 } 511 } 512 513 if (!inc.getConcept().isEmpty()) { 514 canBeHierarchy = false; 515 for (ConceptReferenceComponent c : inc.getConcept()) { 516 c.checkNoModifiers("Code in Code System", "expanding"); 517 addCode(inc.getSystem(), c.getCode(), 518 Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, 519 convertDesignations(c.getDesignation()), expParams, false, CodeSystemUtilities.isInactive(cs, c.getCode()), 520 imports); 521 } 522 } 523 if (inc.getFilter().size() > 1) { 524 canBeHierarchy = false; // which will bt the case if we get around to supporting this 525 throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't 526 // done yet. But this shouldn't arise 527 // in non loinc and snomed value sets 528 } 529 if (inc.getFilter().size() == 1) { 530 ConceptSetFilterComponent fc = inc.getFilter().get(0); 531 if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISA) { 532 // special: all codes in the target code system under the value 533 ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); 534 if (def == null) 535 throw new TerminologyServiceException( 536 "Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); 537 addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, null); 538 } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.ISNOTA) { 539 // special: all codes in the target code system that are not under the value 540 ConceptDefinitionComponent defEx = getConceptForCode(cs.getConcept(), fc.getValue()); 541 if (defEx == null) 542 throw new TerminologyServiceException( 543 "Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); 544 for (ConceptDefinitionComponent def : cs.getConcept()) { 545 addCodeAndDescendents(cs, inc.getSystem(), def, null, expParams, imports, defEx); 546 } 547 } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) { 548 // special: all codes in the target code system under the value 549 ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); 550 if (def == null) 551 throw new TerminologyServiceException( 552 "Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'"); 553 for (ConceptDefinitionComponent c : def.getConcept()) 554 addCodeAndDescendents(cs, inc.getSystem(), c, null, expParams, imports, null); 555 } else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) { 556 // gg; note: wtf is this: if the filter is display=v, look up the code 'v', and 557 // see if it's diplsay is 'v'? 558 canBeHierarchy = false; 559 ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); 560 if (def != null) { 561 if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) { 562 if (def.getDisplay().contains(fc.getValue())) { 563 addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), expParams, 564 CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), imports); 565 } 566 } 567 } 568 } else 569 throw new NotImplementedException( 570 "Search by property[" + fc.getProperty() + "] and op[" + fc.getOp() + "] is not supported yet"); 571 } 572 } 573 574 private List<ConceptDefinitionDesignationComponent> convertDesignations( 575 List<ConceptReferenceDesignationComponent> list) { 576 List<ConceptDefinitionDesignationComponent> res = new ArrayList<CodeSystem.ConceptDefinitionDesignationComponent>(); 577 for (ConceptReferenceDesignationComponent t : list) { 578 ConceptDefinitionDesignationComponent c = new ConceptDefinitionDesignationComponent(); 579 c.setLanguage(t.getLanguage()); 580 c.setUse(t.getUse()); 581 c.setValue(t.getValue()); 582 } 583 return res; 584 } 585 586 private String key(String uri, String code) { 587 return "{" + uri + "}" + code; 588 } 589 590 private String key(ValueSetExpansionContainsComponent c) { 591 return key(c.getSystem(), c.getCode()); 592 } 593 594}