001package org.hl7.fhir.r5.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 032 033 034import java.util.ArrayList; 035import java.util.Calendar; 036import java.util.Collections; 037import java.util.Comparator; 038import java.util.HashSet; 039import java.util.List; 040import java.util.Set; 041 042import org.hl7.fhir.exceptions.FHIRException; 043import org.hl7.fhir.exceptions.FHIRFormatError; 044import org.hl7.fhir.r5.context.IWorkerContext; 045import org.hl7.fhir.r5.model.BooleanType; 046import org.hl7.fhir.r5.model.CanonicalResource; 047import org.hl7.fhir.r5.model.CanonicalType; 048import org.hl7.fhir.r5.model.CodeSystem; 049import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 050import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent; 051import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; 052import org.hl7.fhir.r5.model.CodeSystem.PropertyType; 053import org.hl7.fhir.r5.model.CodeType; 054import org.hl7.fhir.r5.model.Coding; 055import org.hl7.fhir.r5.model.DataType; 056import org.hl7.fhir.r5.model.DateTimeType; 057import org.hl7.fhir.r5.model.DecimalType; 058import org.hl7.fhir.r5.model.Enumerations.PublicationStatus; 059import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.ConceptDefinitionComponentSorter; 060import org.hl7.fhir.r5.model.Identifier; 061import org.hl7.fhir.r5.model.IntegerType; 062import org.hl7.fhir.r5.model.Meta; 063import org.hl7.fhir.r5.model.StringType; 064import org.hl7.fhir.r5.model.UriType; 065import org.hl7.fhir.r5.utils.ToolingExtensions; 066import org.hl7.fhir.utilities.StandardsStatus; 067import org.hl7.fhir.utilities.Utilities; 068 069public class CodeSystemUtilities { 070 071 public static class SystemReference { 072 private String link; 073 private String text; 074 private boolean local; 075 076 public SystemReference(String text, String link) { 077 super(); 078 this.link = link; 079 this.text = text; 080 } 081 public SystemReference(String text, String link, boolean local) { 082 super(); 083 this.link = link; 084 this.text = text; 085 this.local = local; 086 } 087 088 public String getLink() { 089 return link; 090 } 091 public String getText() { 092 return text; 093 } 094 public boolean isLocal() { 095 return local; 096 } 097 098 } 099 100 public static class ConceptDefinitionComponentSorter implements Comparator<ConceptDefinitionComponent> { 101 102 @Override 103 public int compare(ConceptDefinitionComponent o1, ConceptDefinitionComponent o2) { 104 return o1.getCode().compareToIgnoreCase(o2.getCode()); 105 } 106 107 } 108 109 public static final String USER_DATA_CROSS_LINK = "cs.utils.cross.link"; 110 111 public static class CodeSystemNavigator { 112 113 private CodeSystem cs; 114 private boolean restructure; 115 private Set<String> processed = new HashSet<>(); 116 117 public CodeSystemNavigator(CodeSystem cs) { 118 this.cs = cs; 119 restructure = hasExtraRelationships(cs.getConcept()); 120 } 121 122 public boolean isRestructure() { 123 return restructure; 124 } 125 126 private boolean hasExtraRelationships(List<ConceptDefinitionComponent> concept) { 127 for (ConceptDefinitionComponent cd : concept) { 128 if (!getSubsumedBy(cd).isEmpty()) { 129 return true; 130 } 131 for (ConceptDefinitionComponent cdc : cd.getConcept()) { 132 if (hasExtraRelationships(cdc.getConcept())) { 133 return true; 134 } 135 } 136 } 137 return false; 138 } 139 140 public List<ConceptDefinitionComponent> getConcepts(ConceptDefinitionComponent context) { 141 if (context == null) { 142 if (restructure) { 143 List<ConceptDefinitionComponent> res = new ArrayList<>(); 144 for (ConceptDefinitionComponent cd : cs.getConcept()) { 145 if (getSubsumedBy(cd).isEmpty()) { 146 res.add(cd); 147 processed.add(cd.getCode()); 148 } 149 } 150 return res; 151 } else { 152 return cs.getConcept(); 153 } 154 } else { 155 if (restructure) { 156 List<ConceptDefinitionComponent> res = new ArrayList<>(); 157 for (ConceptDefinitionComponent cd : context.getConcept()) { 158 res.add(cd); 159 processed.add(cd.getCode()); 160 } 161 for (ConceptDefinitionComponent cd : cs.getConcept()) { 162 if (getSubsumedBy(cd).contains(context.getCode()) && !processed.contains(cd.getCode())) { 163 res.add(cd); 164 processed.add(cd.getCode()); 165 } 166 } 167 return res; 168 } else { 169 return context.getConcept(); 170 } 171 } 172 } 173 174 private List<String> getSubsumedBy(ConceptDefinitionComponent cd) { 175 List<String> codes = new ArrayList<>(); 176 for (ConceptPropertyComponent cp : cd.getProperty()) { 177 if ("subsumedBy".equals(cp.getCode())) { 178 codes.add(cp.getValue().primitiveValue()); 179 } 180 } 181 return codes; 182 } 183 184 public List<ConceptDefinitionComponent> getOtherChildren(ConceptDefinitionComponent context) { 185 List<ConceptDefinitionComponent> res = new ArrayList<>(); 186 for (ConceptDefinitionComponent cd : cs.getConcept()) { 187 if (getSubsumedBy(cd).contains(context.getCode()) && processed.contains(cd.getCode())) { 188 res.add(cd); 189 } 190 } 191 return res; 192 } 193 } 194 195 196 public static boolean isNotSelectable(CodeSystem cs, ConceptDefinitionComponent def) { 197 for (ConceptPropertyComponent p : def.getProperty()) { 198 if ("notSelectable".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 199 return ((BooleanType) p.getValue()).getValue(); 200 } 201 return false; 202 } 203 204 public static void setNotSelectable(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError { 205 defineNotSelectableProperty(cs); 206 ConceptPropertyComponent p = getProperty(concept, "notSelectable"); 207 if (p != null) 208 p.setValue(new BooleanType(true)); 209 else 210 concept.addProperty().setCode("notSelectable").setValue(new BooleanType(true)); 211 } 212 213 public static void setProperty(CodeSystem cs, ConceptDefinitionComponent concept, String code, DataType value) throws FHIRFormatError { 214 defineProperty(cs, code, propertyTypeForValue(value)); 215 ConceptPropertyComponent p = getProperty(concept, code); 216 if (p != null) 217 p.setValue(value); 218 else 219 concept.addProperty().setCode(code).setValue(value); 220 } 221 222 223 private static PropertyType propertyTypeForValue(DataType value) { 224 if (value instanceof BooleanType) { 225 return PropertyType.BOOLEAN; 226 } 227 if (value instanceof CodeType) { 228 return PropertyType.CODE; 229 } 230 if (value instanceof Coding) { 231 return PropertyType.CODING; 232 } 233 if (value instanceof DateTimeType) { 234 return PropertyType.DATETIME; 235 } 236 if (value instanceof DecimalType) { 237 return PropertyType.DECIMAL; 238 } 239 if (value instanceof IntegerType) { 240 return PropertyType.INTEGER; 241 } 242 if (value instanceof StringType) { 243 return PropertyType.STRING; 244 } 245 throw new Error("Unknown property type "+value.getClass().getName()); 246 } 247 248 private static void defineProperty(CodeSystem cs, String code, PropertyType pt) { 249 String url = "http://hl7.org/fhir/concept-properties#"+code; 250 for (PropertyComponent p : cs.getProperty()) { 251 if (p.getCode().equals(code)) { 252 if (!p.getUri().equals(url)) { 253 throw new Error("URI mismatch for code "+code+" url = "+p.getUri()+" vs "+url); 254 } 255 if (!p.getType().equals(pt)) { 256 throw new Error("Type mismatch for code "+code+" type = "+p.getType()+" vs "+pt); 257 } 258 return; 259 } 260 } 261 cs.addProperty().setCode(code).setUri(url).setType(pt).setUri(url); 262 263 } 264 265 public static void defineNotSelectableProperty(CodeSystem cs) { 266 defineCodeSystemProperty(cs, "notSelectable", "Indicates that the code is abstract - only intended to be used as a selector for other concepts", PropertyType.BOOLEAN); 267 } 268 269 270 public enum ConceptStatus { 271 Active, Experimental, Deprecated, Retired; 272 273 public String toCode() { 274 switch (this) { 275 case Active: return "active"; 276 case Experimental: return "experimental"; 277 case Deprecated: return "deprecated"; 278 case Retired: return "retired"; 279 default: return null; 280 } 281 } 282 } 283 284 public static void setStatus(CodeSystem cs, ConceptDefinitionComponent concept, ConceptStatus status) throws FHIRFormatError { 285 defineStatusProperty(cs); 286 ConceptPropertyComponent p = getProperty(concept, "status"); 287 if (p != null) 288 p.setValue(new CodeType(status.toCode())); 289 else 290 concept.addProperty().setCode("status").setValue(new CodeType(status.toCode())); 291 } 292 293 public static void defineStatusProperty(CodeSystem cs) { 294 defineCodeSystemProperty(cs, "status", "A property that indicates the status of the concept. One of active, experimental, deprecated, retired", PropertyType.CODE); 295 } 296 297 private static void defineDeprecatedProperty(CodeSystem cs) { 298 defineCodeSystemProperty(cs, "deprecationDate", "The date at which a concept was deprecated. Concepts that are deprecated but not inactive can still be used, but their use is discouraged", PropertyType.DATETIME); 299 } 300 301 public static void defineParentProperty(CodeSystem cs) { 302 defineCodeSystemProperty(cs, "parent", "The concept identified in this property is a parent of the concept on which it is a property. The property type will be 'code'. The meaning of parent/child relationships is defined by the hierarchyMeaning attribute", PropertyType.CODE); 303 } 304 305 public static void defineChildProperty(CodeSystem cs) { 306 defineCodeSystemProperty(cs, "child", "The concept identified in this property is a child of the concept on which it is a property. The property type will be 'code'. The meaning of parent/child relationships is defined by the hierarchyMeaning attribute", PropertyType.CODE); 307 } 308 309 public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def, boolean ignoreStatus) { 310 try { 311 for (ConceptPropertyComponent p : def.getProperty()) { 312 if (!ignoreStatus) { 313 if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "deprecated".equals(p.getValueCodeType().getCode())) 314 return true; 315 } 316 // this, though status should also be set 317 if ("deprecationDate".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof DateTimeType) 318 return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance())); 319 // legacy 320 if ("deprecated".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 321 return ((BooleanType) p.getValue()).getValue(); 322 } 323 StandardsStatus ss = ToolingExtensions.getStandardsStatus(def); 324 if (ss == StandardsStatus.DEPRECATED) { 325 return true; 326 } 327 return false; 328 } catch (FHIRException e) { 329 return false; 330 } 331 } 332 333 public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def, boolean ignoreStatus) { 334 try { 335 for (ConceptPropertyComponent p : def.getProperty()) { 336 if (!ignoreStatus) { 337 if ("status".equals(p.getCode()) && p.hasValue() && p.hasValueCodeType() && "inactive".equals(p.getValueCodeType().getCode())) 338 return true; 339 } 340 // legacy 341 if ("inactive".equals(p.getCode()) && p.hasValue() && p.getValue() instanceof BooleanType) 342 return ((BooleanType) p.getValue()).getValue(); 343 } 344 return false; 345 } catch (FHIRException e) { 346 return false; 347 } 348 } 349 350 public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept, DateTimeType date) throws FHIRFormatError { 351 setStatus(cs, concept, ConceptStatus.Deprecated); 352 defineDeprecatedProperty(cs); 353 concept.addProperty().setCode("deprecationDate").setValue(date); 354 } 355 356 357 public static void setDeprecated(CodeSystem cs, ConceptDefinitionComponent concept) throws FHIRFormatError { 358 setStatus(cs, concept, ConceptStatus.Deprecated); 359 } 360 361 public static boolean isInactive(CodeSystem cs, ConceptDefinitionComponent def) throws FHIRException { 362 for (ConceptPropertyComponent p : def.getProperty()) { 363 if ("status".equals(p.getCode()) && p.hasValueStringType()) { 364 return "inactive".equals(p.getValueStringType().primitiveValue()) || "retired".equals(p.getValueStringType().primitiveValue()); 365 } 366 if ("inactive".equals(p.getCode()) && p.hasValueBooleanType()) { 367 return p.getValueBooleanType().getValue(); 368 } 369 } 370 return false; 371 } 372 373 public static boolean isInactive(CodeSystem cs, String code) throws FHIRException { 374 ConceptDefinitionComponent def = findCode(cs.getConcept(), code); 375 if (def == null) 376 return true; 377 return isInactive(cs, def); 378 } 379 380 public static void defineCodeSystemProperty(CodeSystem cs, String code, String description, PropertyType type) { 381 for (PropertyComponent p : cs.getProperty()) { 382 if (p.getCode().equals(code)) 383 return; 384 } 385 cs.addProperty().setCode(code).setDescription(description).setType(type).setUri("http://hl7.org/fhir/concept-properties#"+code); 386 } 387 388 public static String getCodeDefinition(CodeSystem cs, String code) { 389 return getCodeDefinition(cs.getConcept(), code); 390 } 391 392 private static String getCodeDefinition(List<ConceptDefinitionComponent> list, String code) { 393 for (ConceptDefinitionComponent c : list) { 394 if (c.hasCode() && c.getCode().equals(code)) 395 return c.getDefinition(); 396 String s = getCodeDefinition(c.getConcept(), code); 397 if (s != null) 398 return s; 399 } 400 return null; 401 } 402 403 public static CodeSystem makeShareable(CodeSystem cs) { 404 if (!cs.hasExperimental()) { 405 cs.setExperimental(false); 406 } 407 408 if (!cs.hasMeta()) 409 cs.setMeta(new Meta()); 410 for (UriType t : cs.getMeta().getProfile()) 411 if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue())) 412 return cs; 413 cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem")); 414 return cs; 415 } 416 417 public static boolean makeCSShareable(CodeSystem cs) { 418 if (!cs.hasMeta()) 419 cs.setMeta(new Meta()); 420 for (UriType t : cs.getMeta().getProfile()) 421 if ("http://hl7.org/fhir/StructureDefinition/shareablecodesystem".equals(t.getValue())) 422 return false; 423 cs.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareablecodesystem")); 424 return true; 425 } 426 427 public static void setOID(CodeSystem cs, String oid) { 428 if (!oid.startsWith("urn:oid:")) 429 oid = "urn:oid:" + oid; 430 if (!cs.hasIdentifier()) 431 cs.addIdentifier(new Identifier().setSystem("urn:ietf:rfc:3986").setValue(oid)); 432 else if ("urn:ietf:rfc:3986".equals(cs.getIdentifierFirstRep().getSystem()) && cs.getIdentifierFirstRep().hasValue() && cs.getIdentifierFirstRep().getValue().startsWith("urn:oid:")) 433 cs.getIdentifierFirstRep().setValue(oid); 434 else 435 throw new Error("unable to set OID on code system"); 436 437 } 438 439 public static boolean hasOID(CanonicalResource cs) { 440 return getOID(cs) != null; 441 } 442 443 public static String getOID(CanonicalResource cs) { 444 if (cs.hasIdentifier() && "urn:ietf:rfc:3986".equals(cs.getIdentifierFirstRep().getSystem()) && cs.getIdentifierFirstRep().hasValue() && cs.getIdentifierFirstRep().getValue().startsWith("urn:oid:")) 445 return cs.getIdentifierFirstRep().getValue().substring(8); 446 return null; 447 } 448 449 public static ConceptDefinitionComponent findCode(List<ConceptDefinitionComponent> list, String code) { 450 for (ConceptDefinitionComponent c : list) { 451 if (c.getCode().equals(code)) 452 return c; 453 ConceptDefinitionComponent s = findCode(c.getConcept(), code); 454 if (s != null) 455 return s; 456 } 457 return null; 458 } 459 460 public static void markStatus(CodeSystem cs, String wg, StandardsStatus status, String pckage, String fmm, String normativeVersion) throws FHIRException { 461 if (wg != null) { 462 if (!ToolingExtensions.hasExtension(cs, ToolingExtensions.EXT_WORKGROUP) || 463 (Utilities.existsInList(ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_WORKGROUP), "fhir", "vocab") && !Utilities.existsInList(wg, "fhir", "vocab"))) { 464 ToolingExtensions.setCodeExtension(cs, ToolingExtensions.EXT_WORKGROUP, wg); 465 } 466 } 467 if (status != null) { 468 StandardsStatus ss = ToolingExtensions.getStandardsStatus(cs); 469 if (ss == null || ss.isLowerThan(status)) 470 ToolingExtensions.setStandardsStatus(cs, status, normativeVersion); 471 if (pckage != null) { 472 if (!cs.hasUserData("ballot.package")) 473 cs.setUserData("ballot.package", pckage); 474 else if (!pckage.equals(cs.getUserString("ballot.package"))) 475 if (!"infrastructure".equals(cs.getUserString("ballot.package"))) 476 System.out.println("Code System "+cs.getUrl()+": ownership clash "+pckage+" vs "+cs.getUserString("ballot.package")); 477 } 478 if (status == StandardsStatus.NORMATIVE) { 479 cs.setExperimental(false); 480 cs.setStatus(PublicationStatus.ACTIVE); 481 } 482 } 483 if (fmm != null) { 484 String sfmm = ToolingExtensions.readStringExtension(cs, ToolingExtensions.EXT_FMM_LEVEL); 485 if (Utilities.noString(sfmm) || Integer.parseInt(sfmm) < Integer.parseInt(fmm)) { 486 ToolingExtensions.setIntegerExtension(cs, ToolingExtensions.EXT_FMM_LEVEL, Integer.parseInt(fmm)); 487 } 488 if (Integer.parseInt(fmm) <= 1) { 489 cs.setExperimental(true); 490 } 491 } 492 } 493 494 495 public static DataType readProperty(ConceptDefinitionComponent concept, String code) { 496 for (ConceptPropertyComponent p : concept.getProperty()) 497 if (p.getCode().equals(code)) 498 return p.getValue(); 499 return null; 500 } 501 502 public static ConceptPropertyComponent getProperty(ConceptDefinitionComponent concept, String code) { 503 for (ConceptPropertyComponent p : concept.getProperty()) 504 if (p.getCode().equals(code)) 505 return p; 506 return null; 507 } 508 509 public static List<ConceptPropertyComponent> getPropertyValues(ConceptDefinitionComponent concept, String code) { 510 List<ConceptPropertyComponent> res = new ArrayList<>(); 511 for (ConceptPropertyComponent p : concept.getProperty()) { 512 if (p.getCode().equals(code)) { 513 res.add(p); 514 } 515 } 516 return res; 517 } 518 519 520 // see http://hl7.org/fhir/R4/codesystem.html#hierachy 521 // returns additional parents not in the heirarchy 522 public static List<String> getOtherChildren(CodeSystem cs, ConceptDefinitionComponent c) { 523 List<String> res = new ArrayList<String>(); 524 for (ConceptPropertyComponent p : c.getProperty()) { 525 if ("parent".equals(p.getCode())) { 526 res.add(p.getValue().primitiveValue()); 527 } 528 } 529 return res; 530 } 531 532 // see http://hl7.org/fhir/R4/codesystem.html#hierachy 533 public static void addOtherChild(CodeSystem cs, ConceptDefinitionComponent owner, String code) { 534 defineChildProperty(cs); 535 owner.addProperty().setCode("child").setValue(new CodeType(code)); 536 } 537 538 public static boolean hasProperty(ConceptDefinitionComponent c, String code) { 539 for (ConceptPropertyComponent cp : c.getProperty()) { 540 if (code.equals(cp.getCode())) { 541 return true; 542 } 543 } 544 return false; 545 } 546 547 public static boolean hasCode(CodeSystem cs, String code) { 548 for (ConceptDefinitionComponent cc : cs.getConcept()) { 549 if (hasCode(cc, code)) { 550 return true; 551 } 552 } 553 return false; 554 } 555 556 private static boolean hasCode(ConceptDefinitionComponent cc, String code) { 557 if (code.equals(cc.getCode())) { 558 return true; 559 } 560 for (ConceptDefinitionComponent c : cc.getConcept()) { 561 if (hasCode(c, code)) { 562 return true; 563 } 564 } 565 return false; 566 } 567 568 public static ConceptDefinitionComponent getCode(CodeSystem cs, String code) { 569 if (code == null) { 570 return null; 571 } 572 for (ConceptDefinitionComponent cc : cs.getConcept()) { 573 ConceptDefinitionComponent cd = getCode(cc, code); 574 if (cd != null) { 575 return cd; 576 } 577 } 578 return null; 579 } 580 581 private static ConceptDefinitionComponent getCode(ConceptDefinitionComponent cc, String code) { 582 if (code.equals(cc.getCode())) { 583 return cc; 584 } 585 for (ConceptDefinitionComponent c : cc.getConcept()) { 586 ConceptDefinitionComponent cd = getCode(c, code); 587 if (cd != null) { 588 return cd; 589 } 590 } 591 return null; 592 } 593 594 public static void crossLinkCodeSystem(CodeSystem cs) { 595 String parent = getPropertyByUrl(cs, "http://hl7.org/fhir/concept-properties#parent"); 596 if ((parent != null)) { 597 crossLinkConcepts(cs.getConcept(), cs.getConcept(), parent); 598 } 599 } 600 601 private static String getPropertyByUrl(CodeSystem cs, String url) { 602 for (PropertyComponent pc : cs.getProperty()) { 603 if (url.equals(pc.getUri())) { 604 return pc.getCode(); 605 } 606 } 607 return null; 608 } 609 610 private static void crossLinkConcepts(List<ConceptDefinitionComponent> root, List<ConceptDefinitionComponent> focus, String parent) { 611 for (ConceptDefinitionComponent def : focus) { 612 List<ConceptPropertyComponent> pcl = getPropertyValues(def, parent); 613 for (ConceptPropertyComponent pc : pcl) { 614 String code = pc.getValue().primitiveValue(); 615 ConceptDefinitionComponent tgt = findCode(root, code); 616 if (!tgt.hasUserData(USER_DATA_CROSS_LINK)) { 617 tgt.setUserData(USER_DATA_CROSS_LINK, new ArrayList<>()); 618 } 619 @SuppressWarnings("unchecked") 620 List<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) tgt.getUserData(USER_DATA_CROSS_LINK); 621 children.add(def); 622 } 623 if (def.hasConcept()) { 624 crossLinkConcepts(root, def.getConcept(), parent); 625 } 626 } 627 628 } 629 630 public static boolean hasHierarchy(CodeSystem cs) { 631 for (ConceptDefinitionComponent c : cs.getConcept()) { 632 if (c.hasConcept()) { 633 return true; 634 } 635 } 636 return false; 637 } 638 639 public static void sortAllCodes(CodeSystem cs) { 640 sortAllCodes(cs.getConcept()); 641 } 642 643 private static void sortAllCodes(List<ConceptDefinitionComponent> list) { 644 Collections.sort(list, new ConceptDefinitionComponentSorter()); 645 for (ConceptDefinitionComponent cd : list) { 646 if (cd.hasConcept()) { 647 sortAllCodes(cd.getConcept()); 648 } 649 } 650 } 651 652 public static Coding readCoding(String jurisdiction) { 653 return jurisdiction == null || !jurisdiction.contains("#") ? null : new Coding().setCode(jurisdiction.substring(jurisdiction.indexOf("#")+1)).setSystem(jurisdiction.substring(0, jurisdiction.indexOf("#"))); 654 } 655 656 public static SystemReference getSystemReference(String system, IWorkerContext ctxt) { 657 if (system == null) { 658 return null; 659 } if ("http://snomed.info/sct".equals(system)) { 660 return new SystemReference("SNOMED CT", "https://browser.ihtsdotools.org/"); 661 } else if ("http://loinc.org".equals(system)) { 662 return new SystemReference("LOINC", "https://loinc.org/"); 663 } else if ("http://unitsofmeasure.org".equals(system)) { 664 return new SystemReference("UCUM", "http://ucum.org"); 665 } else if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) { 666 return new SystemReference("RxNorm", "http://www.nlm.nih.gov/research/umls/rxnorm"); 667 } else if (ctxt != null) { 668 CodeSystem cs = ctxt.fetchCodeSystem(system); 669 if (cs != null && cs.hasUserData("path")) { 670 return new SystemReference(cs.present(), cs.getUserString("path"), Utilities.isAbsoluteUrl(cs.getUserString("path"))); 671 } else if (cs != null) { 672 return new SystemReference(cs.present(), null); 673 } 674 } 675 return null; 676 } 677 678 public static boolean isNotCurrent(CodeSystem cs, ConceptDefinitionComponent c) { 679 return isInactive(cs, c) || isDeprecated(cs, c, false); 680 } 681} 682