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