
001package org.hl7.fhir.r5.terminologies; 002 003import java.util.Calendar; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.HashSet; 007import java.util.List; 008import java.util.Set; 009 010/* 011 Copyright (c) 2011+, HL7, Inc. 012 All rights reserved. 013 014 Redistribution and use in source and binary forms, with or without modification, 015 are permitted provided that the following conditions are met: 016 017 * Redistributions of source code must retain the above copyright notice, this 018 list of conditions and the following disclaimer. 019 * Redistributions in binary form must reproduce the above copyright notice, 020 this list of conditions and the following disclaimer in the documentation 021 and/or other materials provided with the distribution. 022 * Neither the name of HL7 nor the names of its contributors may be used to 023 endorse or promote products derived from this software without specific 024 prior written permission. 025 026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 027 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 028 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 029 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 030 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 031 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 032 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 033 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 034 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 035 POSSIBILITY OF SUCH DAMAGE. 036 037 */ 038 039 040import lombok.extern.slf4j.Slf4j; 041import org.hl7.fhir.exceptions.FHIRException; 042import org.hl7.fhir.exceptions.FHIRFormatError; 043import org.hl7.fhir.r5.context.IWorkerContext; 044import org.hl7.fhir.r5.model.BooleanType; 045import org.hl7.fhir.r5.model.CanonicalType; 046import org.hl7.fhir.r5.model.CodeSystem; 047import org.hl7.fhir.r5.model.DateTimeType; 048import org.hl7.fhir.r5.model.StringType; 049import org.hl7.fhir.r5.model.IntegerType; 050import org.hl7.fhir.r5.model.Enumerations.FilterOperator; 051import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; 052import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; 053import org.hl7.fhir.r5.model.Identifier; 054import org.hl7.fhir.r5.model.Meta; 055import org.hl7.fhir.r5.model.Parameters; 056import org.hl7.fhir.r5.model.UriType; 057import org.hl7.fhir.r5.model.ValueSet; 058import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 059import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent; 060import org.hl7.fhir.r5.model.CodeType; 061import org.hl7.fhir.r5.model.Coding; 062import org.hl7.fhir.r5.model.DataType; 063import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent; 064import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; 065import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; 066import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; 067import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; 068import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionPropertyComponent; 069import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter; 070import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptStatus; 071import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache.SourcedValueSet; 072import org.hl7.fhir.r5.utils.CanonicalResourceUtilities; 073import org.hl7.fhir.r5.utils.ToolingExtensions; 074import org.hl7.fhir.r5.utils.UserDataNames; 075import org.hl7.fhir.utilities.StandardsStatus; 076import org.hl7.fhir.utilities.Utilities; 077import org.hl7.fhir.utilities.VersionUtilities; 078 079@Slf4j 080public class ValueSetUtilities extends TerminologyUtilities { 081 082 083 public static class ValueSetSorter implements Comparator<ValueSet> { 084 085 @Override 086 public int compare(ValueSet o1, ValueSet o2) { 087 String url1 = o1.getUrl(); 088 String url2 = o2.getUrl(); 089 int c = compareString(url1, url2); 090 if (c == 0) { 091 String ver1 = o1.getVersion(); 092 String ver2 = o2.getVersion(); 093 c = VersionUtilities.compareVersions(ver1, ver2); 094 if (c == 0) { 095 String d1 = o1.getDateElement().asStringValue(); 096 String d2 = o2.getDateElement().asStringValue(); 097 c = compareString(url1, url2); 098 } 099 } 100 return c; 101 } 102 103 private int compareString(String s1, String s2) { 104 if (s1 == null) { 105 return s2 == null ? 0 : 1; 106 } else { 107 return s1.compareTo(s2); 108 } 109 } 110 111 } 112 113 114 public static boolean isServerSide(String url) { 115 return Utilities.existsInList(url, "http://hl7.org/fhir/sid/cvx"); 116 } 117 118 public static ValueSet makeShareable(ValueSet vs) { 119 if (!vs.hasExperimental()) { 120 vs.setExperimental(false); 121 } 122 if (!vs.hasMeta()) 123 vs.setMeta(new Meta()); 124 for (UriType t : vs.getMeta().getProfile()) 125 if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset")) 126 return vs; 127 vs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablevalueset")); 128 return vs; 129 } 130 131 public static boolean makeVSShareable(ValueSet vs) { 132 if (!vs.hasMeta()) 133 vs.setMeta(new Meta()); 134 for (UriType t : vs.getMeta().getProfile()) 135 if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset")) 136 return false; 137 vs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablevalueset")); 138 return true; 139 } 140 141 public static void checkShareable(ValueSet vs) { 142 if (!vs.hasMeta()) 143 throw new Error("ValueSet "+vs.getUrl()+" is not shareable"); 144 for (UriType t : vs.getMeta().getProfile()) { 145 if (t.getValue().equals("http://hl7.org/fhir/StructureDefinition/shareablevalueset")) 146 return; 147 } 148 throw new Error("ValueSet "+vs.getUrl()+" is not shareable"); 149 } 150 151 public static boolean hasOID(ValueSet vs) { 152 return getOID(vs) != null; 153 } 154 155 public static String getOID(ValueSet vs) { 156 for (Identifier id : vs.getIdentifier()) { 157 if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:")) 158 return id.getValue().substring(8); 159 } 160 return null; 161 } 162 163 public static void setOID(ValueSet vs, String oid) { 164 if (!oid.startsWith("urn:oid:")) 165 oid = "urn:oid:" + oid; 166 for (Identifier id : vs.getIdentifier()) { 167 if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:")) { 168 id.setValue(oid); 169 return; 170 } 171 } 172 vs.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(oid); 173 } 174 175 public static void markStatus(ValueSet vs, String wg, StandardsStatus status, String pckage, String fmm, IWorkerContext context, String normativeVersion) throws FHIRException { 176 if (vs.hasUserData(UserDataNames.render_external_link)) 177 return; 178 179 if (wg != null) { 180 if (!ToolingExtensions.hasExtension(vs, ToolingExtensions.EXT_WORKGROUP) || 181 (!Utilities.existsInList(ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_WORKGROUP), "fhir", "vocab") && Utilities.existsInList(wg, "fhir", "vocab"))) { 182 CanonicalResourceUtilities.setHl7WG(vs, wg); 183 } 184 } 185 if (status != null) { 186 StandardsStatus ss = ToolingExtensions.getStandardsStatus(vs); 187 if (ss == null || ss.isLowerThan(status)) 188 ToolingExtensions.setStandardsStatus(vs, status, normativeVersion); 189 if (pckage != null) { 190 if (!vs.hasUserData(UserDataNames.kindling_ballot_package)) 191 vs.setUserData(UserDataNames.kindling_ballot_package, pckage); 192 else if (!pckage.equals(vs.getUserString(UserDataNames.kindling_ballot_package))) 193 if (!"infrastructure".equals(vs.getUserString(UserDataNames.kindling_ballot_package))) 194 log.warn("Value Set "+vs.getUrl()+": ownership clash "+pckage+" vs "+vs.getUserString(UserDataNames.kindling_ballot_package)); 195 } 196 if (status == StandardsStatus.NORMATIVE) { 197 vs.setStatus(PublicationStatus.ACTIVE); 198 } 199 } 200 if (fmm != null) { 201 String sfmm = ToolingExtensions.readStringExtension(vs, ToolingExtensions.EXT_FMM_LEVEL); 202 if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) { 203 ToolingExtensions.setIntegerExtension(vs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm)); 204 } 205 } 206 if (vs.hasUserData(UserDataNames.TX_ASSOCIATED_CODESYSTEM)) 207 CodeSystemUtilities.markStatus((CodeSystem) vs.getUserData(UserDataNames.TX_ASSOCIATED_CODESYSTEM), wg, status, pckage, fmm, normativeVersion); 208 else if (status == StandardsStatus.NORMATIVE && context != null) { 209 for (ConceptSetComponent csc : vs.getCompose().getInclude()) { 210 if (csc.hasSystem()) { 211 CodeSystem cs = context.fetchCodeSystem(csc.getSystem()); 212 if (cs != null) { 213 CodeSystemUtilities.markStatus(cs, wg, status, pckage, fmm, normativeVersion); 214 } 215 } 216 } 217 } 218 } 219 220 private static int ssval(String status) { 221 if ("Draft".equals("status")) 222 return 1; 223 if ("Informative".equals("status")) 224 return 2; 225 if ("External".equals("status")) 226 return 3; 227 if ("Trial Use".equals("status")) 228 return 3; 229 if ("Normative".equals("status")) 230 return 4; 231 return -1; 232 } 233 234 public static ValueSet generateImplicitValueSet(String uri) { 235 if (uri.startsWith("http://snomed.info/sct")) 236 return generateImplicitSnomedValueSet(uri); 237 if (uri.startsWith("http://loinc.org/vs")) 238 return generateImplicitLoincValueSet(uri); 239 if (uri.equals("http://hl7.org/fhir/ValueSet/mimetypes")) { 240 return generateImplicitMimetypesValueSet(uri); 241 } 242 return null; 243 } 244 245 private static ValueSet generateImplicitMimetypesValueSet(String theUri) { 246 ValueSet valueSet = new ValueSet(); 247 valueSet.setStatus(PublicationStatus.ACTIVE); 248 valueSet.setUrl(theUri); 249 valueSet.setDescription("This value set includes all possible codes from BCP-13 (http://tools.ietf.org/html/bcp13)"); 250 valueSet.getCompose() 251 .addInclude().setSystem("urn:ietf:bcp:13"); 252 return valueSet; 253 } 254 255 private static ValueSet generateImplicitLoincValueSet(String uri) { 256 if ("http://loinc.org/vs".equals(uri)) 257 return makeLoincValueSet(); 258 if (uri.startsWith("http://loinc.org/vs/LL")) 259 return makeAnswerList(makeLoincValueSet(), uri); 260 return null; 261 } 262 263 private static ValueSet makeAnswerList(ValueSet vs, String uri) { 264 vs.setUrl(uri); 265 String c = uri.substring(20); 266 vs.setName("LOINCAnswers"+c); 267 vs.setTitle("LOINC Answer Codes for "+c); 268 vs.getCompose().getIncludeFirstRep().addFilter().setProperty("LIST").setOp(FilterOperator.EQUAL).setValue(c); 269 return vs; 270 } 271 272 private static ValueSet makeLoincValueSet() { 273 ValueSet vs = new ValueSet(); 274 vs.setUrl("http://loinc.org/vs"); 275 vs.setName("LOINCCodes"); 276 vs.setTitle("All LOINC codes"); 277 vs.setCopyright("This content LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use"); 278 vs.setStatus(PublicationStatus.ACTIVE); 279 vs.getCompose().addInclude().setSystem("http://loinc.org"); 280 return vs; 281 } 282 283 private static ValueSet generateImplicitSnomedValueSet(String uri) { 284 if ("http://snomed.info/sct?fhir_vs".equals(uri)) 285 return makeImplicitSnomedValueSet(uri); 286 return null; 287 } 288 289 private static ValueSet makeImplicitSnomedValueSet(String uri) { 290 ValueSet vs = new ValueSet(); 291 vs.setUrl(uri); 292 vs.setName("SCTValueSet"); 293 vs.setTitle("SCT ValueSet"); 294 vs.setDescription("All SNOMED CT Concepts"); 295 vs.setCopyright("This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (SNOMED International), and distributed by agreement between SNOMED International and HL7. Implementer use of SNOMED CT is not covered by this agreement"); 296 vs.setStatus(PublicationStatus.ACTIVE); 297 vs.getCompose().addInclude().setSystem("http://snomed.info/sct"); 298 return vs; 299 } 300 301 public static void setDeprecated(List<ValueSetExpansionPropertyComponent> vsProp, ValueSetExpansionContainsComponent n) { 302 n.addProperty().setCode("status").setValue(new CodeType("deprecated")); 303 for (ValueSetExpansionPropertyComponent o : vsProp) { 304 if ("status".equals(o.getCode())) { 305 return; 306 } 307 } 308 vsProp.add(new ValueSetExpansionPropertyComponent().setCode("status").setUri("http://hl7.org/fhir/concept-properties#status")); 309 } 310 311 312 public static class ConceptReferenceComponentSorter implements Comparator<ConceptReferenceComponent> { 313 314 @Override 315 public int compare(ConceptReferenceComponent o1, ConceptReferenceComponent o2) { 316 return o1.getCode().compareToIgnoreCase(o2.getCode()); 317 } 318 } 319 320 321 public static void sortInclude(ConceptSetComponent inc) { 322 Collections.sort(inc.getConcept(), new ConceptReferenceComponentSorter()); 323 } 324 325 public static String getAllCodesSystem(ValueSet vs) { 326 if (vs.hasCompose()) { 327 ValueSetComposeComponent c = vs.getCompose(); 328 if (c.getExclude().isEmpty() && c.getInclude().size() == 1) { 329 ConceptSetComponent i = c.getIncludeFirstRep(); 330 if (i.hasSystem() && !i.hasValueSet() && !i.hasConcept() && !i.hasFilter()) { 331 return i.getSystem(); 332 } 333 } 334 } 335 return null; 336 } 337 338 public static boolean isDeprecated(ValueSet vs, ValueSetExpansionContainsComponent c) { 339 try { 340 for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent p : c.getProperty()) { 341 if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "deprecated".equals(p.getValueCodeType().getCode())) { 342 return true; 343 } 344 // this, though status should also be set 345 if ("deprecationDate".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof DateTimeType) 346 return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance())); 347 // legacy 348 if ("deprecated".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 349 return ((BooleanType) p.getValue()).getValue(); 350 } 351 StandardsStatus ss = ToolingExtensions.getStandardsStatus(c); 352 if (ss == StandardsStatus.DEPRECATED) { 353 return true; 354 } 355 return false; 356 } catch (FHIRException e) { 357 return false; 358 } 359 } 360 361 public static boolean hasCodeInExpansion(ValueSet vs, Coding code) { 362 return hasCodeInExpansion(vs.getExpansion().getContains(), code); 363 } 364 365 private static boolean hasCodeInExpansion(List<ValueSetExpansionContainsComponent> list, Coding code) { 366 for (ValueSetExpansionContainsComponent c : list) { 367 if (c.getSystem().equals(code.getSystem()) && c.getCode().equals(code.getCode())) { 368 return true; 369 } 370 if (hasCodeInExpansion(c.getContains(), code)) { 371 return true; 372 } 373 } 374 return false; 375 } 376 377 public static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent addProperty(ValueSet vs, ValueSetExpansionContainsComponent ctxt, String url, String code, String value) { 378 if (value != null) { 379 return addProperty(vs, ctxt, url, code, new StringType(value)); 380 } else { 381 return null; 382 } 383 } 384 385 public static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent addProperty(ValueSet vs, ValueSetExpansionContainsComponent ctxt, String url, String code, Integer value) { 386 if (value != null) { 387 return addProperty(vs, ctxt, url, code, new IntegerType(value)); 388 } else { 389 return null; 390 } 391 } 392 393 public static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent addProperty(ValueSet vs, ValueSetExpansionContainsComponent ctxt, String url, String code, DataType value) { 394 code = defineProperty(vs, url, code); 395 org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent p = getProperty(ctxt.getProperty(), code); 396 if (p != null) { 397 p.setValue(value); 398 } else { 399 p = ctxt.addProperty().setCode(code).setValue(value); 400 } 401 return p; 402 } 403 404 private static org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent getProperty(List<org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent> list, String code) { 405 for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent t : list) { 406 if (code.equals(t.getCode())) { 407 return t; 408 } 409 } 410 return null; 411 } 412 413 private static String defineProperty(ValueSet vs, String url, String code) { 414 for (ValueSetExpansionPropertyComponent p : vs.getExpansion().getProperty()) { 415 if (p.hasUri() && p.getUri().equals(url)) { 416 return p.getCode(); 417 } 418 } 419 for (ValueSetExpansionPropertyComponent p : vs.getExpansion().getProperty()) { 420 if (p.hasCode() && p.getCode().equals(code)) { 421 p.setUri(url); 422 return code; 423 } 424 } 425 ValueSetExpansionPropertyComponent p = vs.getExpansion().addProperty(); 426 p.setUri(url); 427 p.setCode(code); 428 return code; 429 } 430 431 public static int countExpansion(ValueSet valueset) { 432 int i = valueset.getExpansion().getContains().size(); 433 for (ValueSetExpansionContainsComponent t : valueset.getExpansion().getContains()) { 434 i = i + countExpansion(t); 435 } 436 return i; 437 } 438 439 public static int countExpansion(List<ValueSetExpansionContainsComponent> list) { 440 int i = list.size(); 441 for (ValueSetExpansionContainsComponent t : list) { 442 i = i + countExpansion(t); 443 } 444 return i; 445 } 446 447 private static int countExpansion(ValueSetExpansionContainsComponent c) { 448 int i = c.getContains().size(); 449 for (ValueSetExpansionContainsComponent t : c.getContains()) { 450 i = i + countExpansion(t); 451 } 452 return i; 453 } 454 455 public static Set<String> listSystems(IWorkerContext ctxt, ValueSet vs) { 456 Set<String> systems = new HashSet<>(); 457 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 458 for (CanonicalType ct : inc.getValueSet()) { 459 ValueSet vsr = ctxt.findTxResource(ValueSet.class, ct.asStringValue(), vs); 460 if (vsr != null) { 461 systems.addAll(listSystems(ctxt, vsr)); 462 } 463 } 464 if (inc.hasSystem()) { 465 systems.add(inc.getSystem()); 466 } 467 } 468 return systems; 469 } 470 471 472 public static boolean isIncompleteExpansion(ValueSet valueSet) { 473 if (valueSet.hasExpansion()) { 474 ValueSetExpansionComponent exp = valueSet.getExpansion(); 475 if (exp.hasTotal()) { 476 if (exp.getTotal() != countExpansion(exp.getContains())) { 477 return true; 478 } 479 } 480 } 481 return false; 482 } 483 484 485 public static Set<String> codes(ValueSet vs, CodeSystem cs) { 486 Set<String> res = new HashSet<>(); 487 for (ConceptSetComponent inc : vs.getCompose().getInclude()) { 488 if (inc.getSystem().equals(cs.getUrl())) { 489 addCodes(res, inc, cs.getConcept()); 490 } 491 } 492 return res; 493 } 494 495 private static void addCodes(Set<String> res, ConceptSetComponent inc, List<ConceptDefinitionComponent> list) { 496 for (ConceptDefinitionComponent cd : list) { 497 if (cd.hasCode() && (!inc.hasConcept() || inc.hasConcept(cd.getCode()))) { 498 res.add(cd.getCode()); 499 } 500 if (cd.hasConcept()) { 501 addCodes(res, inc, cd.getConcept()); 502 } 503 } 504 } 505 506 public static String versionFromExpansionParams(Parameters expParameters, String system, String defaultVersion) { 507 if (expParameters != null) { 508 for (ParametersParameterComponent p : expParameters.getParameter()) { 509 if ("system-version".equals(p.getName()) || "force-system-version".equals(p.getName())) { 510 String v = p.getValue().primitiveValue(); 511 if (v.startsWith(system+"|")) { 512 String ver = v.substring(v.indexOf("|")+1); 513 if (defaultVersion == null || ver.startsWith(defaultVersion) || "force-system-version".equals(p.getName())) { 514 return ver; 515 } 516 } 517 } 518 } 519 } 520 return defaultVersion; 521 } 522 523 public static boolean isImplicitLoincValueSet(String url) { 524 return url.startsWith("http://loinc.org/vs"); 525 } 526 527 public static boolean isImplicitSCTValueSet(String url) { 528 return url.startsWith("http://snomed.info/sct") && url.contains("?fhir_vs"); 529 } 530 531 public static ValueSet makeImplicitValueSet(String url, String version) { 532 if (url.startsWith("http://snomed.info/sct")) { 533 return makeImplicitSCTVS(url, version); 534 } else if (url.startsWith("http://loinc.org/vs")) { 535 return makeImplicitLoincVS(url, version); 536 } else { 537 throw new FHIRException("Unknown implicit value set URL "+url); 538 } 539 } 540 541 private static ValueSet makeImplicitSCTVS(String url, String version) { 542 String query = url.substring(url.indexOf("?")+1); 543 if ("fhir_vs".equals(query)) { 544 ValueSet vs = new ValueSet(); 545 vs.setUrl(url); 546 vs.setVersion(version); 547 vs.getCompose().addInclude().setSystem("http://snomed.info/sct"); 548 return vs; 549 } else if (query.startsWith("fhir_vs=isa/")) { 550 ValueSet vs = new ValueSet(); 551 vs.setUrl(url); 552 vs.setVersion(version); 553 vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue(query.substring(12)); 554 return vs; 555 } else if (query.equals("fhir_vs=refset")) { 556 ValueSet vs = new ValueSet(); 557 vs.setUrl(url); 558 vs.setVersion(version); 559 vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.ISA).setValue("refset-base"); 560 return vs; 561 } else if (query.startsWith("fhir_vs=refset/")) { 562 ValueSet vs = new ValueSet(); 563 vs.setUrl(url); 564 vs.setVersion(version); 565 vs.getCompose().addInclude().setSystem("http://snomed.info/sct").addFilter().setProperty("concept").setOp(FilterOperator.IN).setValue(query.substring(15)); 566 return vs; 567 } else { 568 throw new FHIRException("Unknown implicit SNOMED CT value set URL "+url); 569 } 570 } 571 572 private static ValueSet makeImplicitLoincVS(String url, String version) { 573 if (url.equals("http://loinc.org/vs")) { 574 ValueSet vs = new ValueSet(); 575 vs.setUrl(url); 576 vs.setVersion(version); 577 vs.getCompose().addInclude().setSystem("http://loinc.org"); 578 return vs; 579 } else if (url.startsWith("http://loinc.org/vs/LP")) { 580 ValueSet vs = new ValueSet(); 581 vs.setUrl(url); 582 vs.setVersion(version); 583 vs.getCompose().addInclude().setSystem("http://loinc.org").addFilter().setProperty("ancestor").setOp(FilterOperator.EQUAL).setValue(url.substring("http://loinc.org/vs/".length())); 584 return vs; 585 } else if (url.startsWith("http://loinc.org/vs/LL")) { 586 ValueSet vs = new ValueSet(); 587 vs.setUrl(url); 588 vs.setVersion(version); 589 // this isn't the actual definition, but it won't matter to us internally 590 vs.getCompose().addInclude().setSystem("http://loinc.org").addFilter().setProperty("answer-list").setOp(FilterOperator.EQUAL).setValue(url.substring("http://loinc.org/vs/".length())); 591 return vs; 592 } else { 593 throw new FHIRException("Unknown implicit LOINC value set URL "+url); 594 } 595 } 596 597 public static Set<String> checkExpansionSubset(ValueSet vs1, ValueSet vs2) { 598 Set<String> codes = new HashSet<>(); 599 checkCodes(codes, vs2.getExpansion().getContains(), vs1.getExpansion().getContains()); 600 return codes; 601 } 602 603 private static void checkCodes(Set<String> codes, List<ValueSetExpansionContainsComponent> listS, List<ValueSetExpansionContainsComponent> listT) { 604 for (ValueSetExpansionContainsComponent c : listS) { 605 ValueSetExpansionContainsComponent t = findContained(c, listT); 606 if (t == null) { 607 codes.add(c.getCode()); 608 } 609 if (c.hasContains()) { 610 checkCodes(codes, c.getContains(), listT); 611 } 612 } 613 614 } 615 616 private static ValueSetExpansionContainsComponent findContained(ValueSetExpansionContainsComponent c, List<ValueSetExpansionContainsComponent> listT) { 617 for (ValueSetExpansionContainsComponent t : listT) { 618 if (t.getSystem().equals(c.getSystem()) && t.getCode().equals(c.getCode())) { 619 return t; 620 } 621 if (t.hasContains()) { 622 ValueSetExpansionContainsComponent tt = findContained(c, t.getContains()); 623 if (tt != null) { 624 return tt; 625 } 626 } 627 } 628 return null; 629 } 630 631 632}