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