
001package org.hl7.fhir.r5.terminologies; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.HashMap; 008import java.util.HashSet; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012 013import org.hl7.fhir.r5.model.Base; 014import org.hl7.fhir.r5.model.CanonicalType; 015import org.hl7.fhir.r5.model.CodeSystem; 016import org.hl7.fhir.r5.model.Coding; 017import org.hl7.fhir.r5.model.ConceptMap; 018import org.hl7.fhir.r5.model.ConceptMap.ConceptMapGroupComponent; 019import org.hl7.fhir.r5.model.ConceptMap.SourceElementComponent; 020import org.hl7.fhir.r5.model.ConceptMap.TargetElementComponent; 021import org.hl7.fhir.r5.model.Enumerations.ConceptMapRelationship; 022import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ConceptMapElementSorter; 023import org.hl7.fhir.r5.terminologies.ConceptMapUtilities.ElementMappingPair; 024import org.hl7.fhir.utilities.Utilities; 025import org.hl7.fhir.r5.model.Identifier; 026import org.hl7.fhir.r5.model.Meta; 027import org.hl7.fhir.r5.model.UriType; 028import org.hl7.fhir.r5.model.ValueSet; 029 030public class ConceptMapUtilities { 031 032 public static class MappingTriple { 033 private ConceptMapGroupComponent grp; 034 private SourceElementComponent src; 035 private TargetElementComponent tgt; 036 037 public MappingTriple(ConceptMapGroupComponent grp, SourceElementComponent src, TargetElementComponent tgt) { 038 this.grp = grp; 039 this.src = src; 040 this.tgt = tgt; 041 } 042 043 public ConceptMapGroupComponent getGrp() { 044 return grp; 045 } 046 047 public SourceElementComponent getSrc() { 048 return src; 049 } 050 051 public TargetElementComponent getTgt() { 052 return tgt; 053 } 054 } 055 056 public static class TargetSorter implements Comparator<TargetElementComponent> { 057 058 @Override 059 public int compare(TargetElementComponent o1, TargetElementComponent o2) { 060 return o1.getCode().compareTo(o2.getCode()); 061 } 062 063 } 064 065 public static class ElementSorter implements Comparator<SourceElementComponent> { 066 067 @Override 068 public int compare(SourceElementComponent o1, SourceElementComponent o2) { 069 return o1.getCode().compareTo(o2.getCode()); 070 } 071 072 } 073 074 public static class ElementMappingPair { 075 076 private SourceElementComponent src; 077 private TargetElementComponent tgt; 078 079 public ElementMappingPair(SourceElementComponent src, TargetElementComponent tgt) { 080 this.src = src; 081 this.tgt = tgt; 082 } 083 084 } 085 086 public static class TranslatedCode { 087 private String code; 088 private ConceptMapRelationship relationship; 089 public TranslatedCode(String code, ConceptMapRelationship relationship) { 090 super(); 091 this.code = code; 092 this.relationship = relationship; 093 } 094 public String getCode() { 095 return code; 096 } 097 public ConceptMapRelationship getRelationship() { 098 return relationship; 099 } 100 101 } 102 103 public static class ConceptMapElementSorter implements Comparator<SourceElementComponent> { 104 105 @Override 106 public int compare(SourceElementComponent o1, SourceElementComponent o2) { 107 return o1.getCode().compareTo(o2.getCode()); 108 } 109 110 } 111 112 public static class ConceptMapTargetElementSorter implements Comparator<TargetElementComponent> { 113 114 @Override 115 public int compare(TargetElementComponent o1, TargetElementComponent o2) { 116 return o1.getCode().compareTo(o2.getCode()); 117 } 118 119 } 120 public static boolean hasOID(ConceptMap cm) { 121 return getOID(cm) != null; 122 } 123 124 public static String getOID(ConceptMap cm) { 125 for (Identifier id : cm.getIdentifier()) { 126 if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:")) 127 return id.getValue().substring(8); 128 } 129 return null; 130 } 131 132 public static void setOID(ConceptMap cm, String oid) { 133 if (!oid.startsWith("urn:oid:")) 134 oid = "urn:oid:" + oid; 135 for (Identifier id : cm.getIdentifier()) { 136 if ("urn:ietf:rfc:3986".equals(id.getSystem()) && id.hasValue() && id.getValue().startsWith("urn:oid:")) { 137 id.setValue(oid); 138 return; 139 } 140 } 141 cm.addIdentifier().setSystem("urn:ietf:rfc:3986").setValue(oid); 142 } 143 144 public static boolean hasMappingForSource(ConceptMap cm, Coding code) { 145 return hasMappingForSource(cm, code.getSystem(), code.getVersion(), code.getCode()); 146 } 147 148 public static boolean hasMappingForSource(ConceptMap cm, String system, String version, String code) { 149 for (ConceptMapGroupComponent grp : cm.getGroup()) { 150 if (system.equals(grp.getSource())) { // to do: version 151 for (SourceElementComponent e : grp.getElement()) { 152 if (code.equals(e.getCode())) { 153 return true; // doesn't matter if it's actually unmapped 154 } 155 } 156 } 157 } 158 return false; 159 } 160 161 public static boolean hasMappingForTarget(ConceptMap cm, Coding code) { 162 return hasMappingForTarget(cm, code.getSystem(), code.getVersion(), code.getCode()); 163 } 164 165 public static boolean hasMappingForTarget(ConceptMap cm, String system, String version, String code) { 166 for (ConceptMapGroupComponent grp : cm.getGroup()) { 167 if (system.equals(grp.getTarget())) { // to do: version 168 for (SourceElementComponent e : grp.getElement()) { 169 for (TargetElementComponent t : e.getTarget()) { 170 if (code.equals(t.getCode())) { 171 return true; // doesn't matter if it's actually unmapped 172 } 173 } 174 } 175 } 176 } 177 return false; 178 } 179 180 public static List<Coding> listTargets(ConceptMap cm, List<String> systems) { 181 List<Coding> list = new ArrayList<>(); 182 for (ConceptMapGroupComponent grp : cm.getGroup()) { 183 if (systems.isEmpty() || systems.contains(grp.getSource())) { // to do: version 184 for (SourceElementComponent e : grp.getElement()) { 185 for (TargetElementComponent t : e.getTarget()) { 186 if (t.hasCode()) { 187 list.add(new Coding(grp.getTarget(), t.getCode(), t.getDisplay())); 188 } 189 } 190 } 191 } 192 } 193 return list; 194 } 195 196 197 public static ConceptMap makeShareable(ConceptMap cm) { 198 if (!cm.hasExperimental()) { 199 cm.setExperimental(false); 200 } 201 202 if (!cm.hasMeta()) 203 cm.setMeta(new Meta()); 204 for (UriType t : cm.getMeta().getProfile()) 205 if ("http://hl7.org/fhir/StructureDefinition/shareableconceptmap".equals(t.getValue())) 206 return cm; 207 cm.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareableconceptmap")); 208 return cm; 209 } 210 211 public static ConceptMap invert(ConceptMap src, String id, String url, String name, boolean collate) { 212 ConceptMap dst = src.copy(); 213 dst.setId(id); 214 dst.setUrl(url); 215 dst.setName(name); 216 dst.getGroup().clear(); 217 dst.setSourceScope(src.getTargetScope()); 218 dst.setTargetScope(src.getSourceScope()); 219 for (ConceptMapGroupComponent gs : src.getGroup()) { 220 ConceptMapGroupComponent gd = dst.addGroup(); 221 gd.setTargetElement(gs.getSourceElement()); 222 gd.setSourceElement(gs.getTargetElement()); 223 Map<String, SourceElementComponent> dstMap = new HashMap<>(); 224 for (SourceElementComponent es : gs.getElement()) { 225 for (TargetElementComponent ts : es.getTarget()) { 226 SourceElementComponent ed = collate ? dstMap.get(ts.getCode()) : null; 227 if (ed == null) { 228 ed = gd.addElement(); 229 ed.setCodeElement(ts.getCodeElement()); 230 if (collate) { 231 dstMap.put(ed.getCode(), ed); 232 } 233 } 234 TargetElementComponent td = ed.addTarget(); 235 td.setCode(es.getCode()); 236 td.setComment(ts.getComment()); 237 td.setRelationship(invertRelationship(ts.getRelationship())); 238 } 239 } 240 } 241 return dst; 242 } 243 244 private static ConceptMapRelationship invertRelationship(ConceptMapRelationship relationship) { 245 if (relationship == null) { 246 return null; 247 } 248 switch (relationship) { 249 case EQUIVALENT: 250 return ConceptMapRelationship.EQUIVALENT; 251 case NOTRELATEDTO: 252 return ConceptMapRelationship.NOTRELATEDTO; 253 case NULL: 254 return ConceptMapRelationship.NULL; 255 case RELATEDTO: 256 return ConceptMapRelationship.RELATEDTO; 257 case SOURCEISBROADERTHANTARGET: 258 return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 259 case SOURCEISNARROWERTHANTARGET: 260 return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 261 default: 262 return null; 263 } 264 } 265 266 public static ConceptMap collapse(String id, String url, boolean cumulative, ConceptMap src, ConceptMap... sequence) { 267 ConceptMap res = src.copy(); 268 res.setId(id); 269 res.setUrl(url); 270 271 for (ConceptMap cm : sequence) { 272 if (res.hasTargetScope() && cm.hasTargetScope()) { 273 if (!cm.getSourceScope().primitiveValue().equals(res.getTargetScope().primitiveValue())) { 274 throw new Error("Mismatch between sequential concept maps: target was "+res.getTargetScope()+" and source is "+cm.getSourceScope()); 275 } else { 276 res.setTargetScope(cm.getTargetScope()); 277 } 278 } else { 279 res.setTargetScope(null); 280 } 281 } 282 283 for (ConceptMapGroupComponent gd : res.getGroup()) { 284 for (ConceptMap cm : sequence) { 285 for (ConceptMapGroupComponent gt : cm.getGroup()) { 286 if (gt.getSource().equals(gd.getTarget())) { 287 gd.setTarget(gt.getTarget()); 288 289 List<SourceElementComponent> processed = new ArrayList<ConceptMap.SourceElementComponent>(); 290 for (SourceElementComponent ed : gd.getElement()) { 291 List<TargetElementComponent> list = new ArrayList<>(); 292 list.addAll(ed.getTarget()); 293 ed.getTarget().clear(); 294 for (TargetElementComponent ts : list) { 295 for (SourceElementComponent et : gt.getElement()) { 296 if (et.getCode().equals(ed.getCode())) { 297 processed.add(et); 298 for (TargetElementComponent tt : et.getTarget()) { 299 ed.addTarget().setCode(tt.getCode()).setRelationship(combineRelationships(ts.getRelationship(), tt.getRelationship())); 300 } 301 } 302 } 303 } 304 if (ed.getTarget().isEmpty()) { 305 if (cumulative) { 306 ed.getTarget().addAll(list); 307 } else { 308 ed.setNoMap(true); 309 } 310 } 311 } 312 if (cumulative) { 313 for (SourceElementComponent et : gt.getElement()) { 314 if (!processed.contains(et)) { 315 gd.addElement(et.copy()); 316 } 317 } 318 } 319 } 320 Collections.sort(gt.getElement(), new ConceptMapElementSorter()); 321 for (SourceElementComponent e: gt.getElement()) { 322 Collections.sort(e.getTarget(), new ConceptMapTargetElementSorter()); 323 } 324 } 325 } 326 } 327 return res; 328 } 329 330 public static ConceptMapRelationship combineRelationships(ConceptMapRelationship rel1, ConceptMapRelationship rel2) { 331 switch (rel1) { 332 case EQUIVALENT: 333 return rel2; 334 case NOTRELATEDTO: 335 return ConceptMapRelationship.NOTRELATEDTO; 336 case NULL: 337 return null; 338 case RELATEDTO: 339 return rel2; 340 case SOURCEISBROADERTHANTARGET: 341 switch (rel2) { 342 case EQUIVALENT: 343 return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 344 case NOTRELATEDTO: 345 return ConceptMapRelationship.NOTRELATEDTO; 346 case NULL: 347 return null; 348 case RELATEDTO: 349 return ConceptMapRelationship.RELATEDTO; 350 case SOURCEISBROADERTHANTARGET: 351 return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 352 case SOURCEISNARROWERTHANTARGET: 353 return ConceptMapRelationship.RELATEDTO; 354 } 355 case SOURCEISNARROWERTHANTARGET: 356 switch (rel2) { 357 case EQUIVALENT: 358 return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 359 case NOTRELATEDTO: 360 return ConceptMapRelationship.NOTRELATEDTO; 361 case NULL: 362 return null; 363 case RELATEDTO: 364 return ConceptMapRelationship.RELATEDTO; 365 case SOURCEISBROADERTHANTARGET: 366 return ConceptMapRelationship.RELATEDTO; 367 case SOURCEISNARROWERTHANTARGET: 368 return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 369 } 370 } 371 return null; 372 } 373 374 public static boolean checkReciprocal(ConceptMap left, ConceptMap right, List<String> issues, boolean makeChanges) { 375 boolean changed = false; 376 if (!Base.compareDeep(left.getTargetScope(), right.getSourceScope(), true)) { 377 issues.add("scopes are not reciprocal: "+left.getTargetScope()+" vs "+right.getSourceScope()); 378 } 379 if (!Base.compareDeep(left.getSourceScope(), right.getTargetScope(), true)) { 380 issues.add("scopes are not reciprocal: "+left.getSourceScope()+" vs "+right.getTargetScope()); 381 } 382 for (ConceptMapGroupComponent gl : left.getGroup()) { 383 ConceptMapGroupComponent gr = findMatchingGroup(right.getGroup(), gl.getTarget(), gl.getSource()); 384 if (gr == null) { 385 for (SourceElementComponent e : gl.getElement()) { 386 for (TargetElementComponent t : e.getTarget()) { 387 if (t.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 388 if (makeChanges) { 389 changed = true; 390 right.forceGroup(gl.getTarget(), gl.getSource()).getOrAddElement(t.getCode()).addTarget(e.getCode(), inverse(t.getRelationship())); 391 } else { 392 issues.add("left maps from "+gl.getSource()+"#"+e.getCode()+" to "+gl.getTarget()+"#"+t.getCode()+" but right has no matching reverse map"); 393 } 394 } 395 } 396 } 397 } else { 398 for (SourceElementComponent srcL : gl.getElement()) { 399 if (!srcL.getNoMap()) { 400 for (TargetElementComponent tgtL : srcL.getTarget()) { 401 List<ElementMappingPair> pairs = getMappings(gr, tgtL.getCode(), srcL.getCode()); 402 if (tgtL.getRelationship() == null) { 403 issues.add("Left map has relationship "+srcL.getCode()+" with no relationship"); 404 } else switch (tgtL.getRelationship()) { 405 case EQUIVALENT: 406 if (pairs.isEmpty()) { 407 if (makeChanges) { 408 changed = true; 409 gr.getOrAddElement(tgtL.getCode()).addTarget(srcL.getCode(), ConceptMapRelationship.EQUIVALENT); 410 } else { 411 issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but there's no reverse relationship"); 412 } 413 } else for (ElementMappingPair pair : pairs) { 414 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) { 415 issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 416 } 417 } 418 break; 419 case RELATEDTO: 420 if (pairs.isEmpty()) { 421 issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but there's no reverse relationship"); 422 } else for (ElementMappingPair pair : pairs) { 423 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) { 424 issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 425 } 426 } 427 break; 428 case SOURCEISBROADERTHANTARGET: 429 if (pairs.isEmpty()) { 430 issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but there's no reverse relationship"); 431 } else for (ElementMappingPair pair : pairs) { 432 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 433 issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 434 } 435 } 436 break; 437 case SOURCEISNARROWERTHANTARGET: 438 if (pairs.isEmpty()) { 439 issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but there's no reverse relationship"); 440 } else for (ElementMappingPair pair : pairs) { 441 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) { 442 issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 443 } 444 } 445 break; 446 case NOTRELATEDTO: 447 for (ElementMappingPair pair : pairs) { 448 if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 449 issues.add("Left map says that "+srcL.getCode()+" is not related to "+tgtL.getCode()+" but a reverse relationship exists with type "+pair.tgt.getRelationship().toCode()); 450 } 451 } 452 break; 453 } 454 } 455 } else { 456 for (SourceElementComponent srcR : gr.getElement()) { 457 for (TargetElementComponent tgtR : srcR.getTarget()) { 458 if (srcL.getCode().equals(tgtR.getCode())) { 459 issues.add("Left map says that there is no relationship for "+srcL.getCode()+" but right map has a "+tgtR.getRelationship().toCode()+" mapping to it from "+srcR.getCode()); 460 } 461 } 462 } 463 } 464 } 465 } 466 } 467 for (ConceptMapGroupComponent gr : right.getGroup()) { 468 ConceptMapGroupComponent gl = findMatchingGroup(left.getGroup(), gr.getTarget(), gr.getSource()); 469 if (gl == null) { 470 for (SourceElementComponent e : gr.getElement()) { 471 for (TargetElementComponent t : e.getTarget()) { 472 if (t.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 473 if (makeChanges) { 474 changed = true; 475 left.forceGroup(gr.getTarget(), gr.getSource()).getOrAddElement(t.getCode()).addTarget(e.getCode(), inverse(t.getRelationship())); 476 } else { 477 issues.add("left maps from "+gr.getSource()+"#"+e.getCode()+" to "+gr.getTarget()+"#"+t.getCode()+" but right has no matching reverse map"); 478 } 479 } 480 } 481 } 482 } else { 483 for (SourceElementComponent srcR : gr.getElement()) { 484 if (!"CHECK!".equals(srcR.getCode())) { 485 if (!srcR.getNoMap()) { 486 for (TargetElementComponent tgtR : srcR.getTarget()) { 487 List<ElementMappingPair> pairs = getMappings(gl, tgtR.getCode(), srcR.getCode()); 488 if (tgtR.getRelationship() == null) { 489 issues.add("Right map has relationship "+srcR.getCode()+" with no relationship"); 490 } else switch (tgtR.getRelationship()) { 491 case EQUIVALENT: 492 if (pairs.isEmpty()) { 493 if (makeChanges) { 494 changed = true; 495 gl.getOrAddElement(tgtR.getCode()).addTarget(srcR.getCode(), ConceptMapRelationship.EQUIVALENT); 496 } else { 497 issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but there's no reverse relationship"); 498 } 499 } else for (ElementMappingPair pair : pairs) { 500 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) { 501 issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 502 } 503 } 504 break; 505 case RELATEDTO: 506 if (pairs.isEmpty()) { 507 issues.add("Right map says that "+srcR.getCode()+" is related to "+tgtR.getCode()+" but there's no reverse relationship"); 508 } else for (ElementMappingPair pair : pairs) { 509 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) { 510 issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 511 } 512 } 513 break; 514 case SOURCEISBROADERTHANTARGET: 515 if (pairs.isEmpty()) { 516 issues.add("Right map says that "+srcR.getCode()+" is broader than "+tgtR.getCode()+" but there's no reverse relationship"); 517 } else for (ElementMappingPair pair : pairs) { 518 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 519 issues.add("Right map says that "+srcR.getCode()+" is broader than "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 520 } 521 } 522 break; 523 case SOURCEISNARROWERTHANTARGET: 524 if (pairs.isEmpty()) { 525 issues.add("Right map says that "+srcR.getCode()+" is narrower than "+tgtR.getCode()+" but there's no reverse relationship"); 526 } else for (ElementMappingPair pair : pairs) { 527 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) { 528 issues.add("Right map says that "+srcR.getCode()+" is narrower than "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 529 } 530 } 531 break; 532 case NOTRELATEDTO: 533 for (ElementMappingPair pair : pairs) { 534 if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 535 issues.add("Right map says that "+srcR.getCode()+" is not related to "+tgtR.getCode()+" but a reverse relationship exists with type "+pair.tgt.getRelationship().toCode()); 536 } 537 } 538 break; 539 } 540 } 541 } else { 542 for (SourceElementComponent srcL : gr.getElement()) { 543 for (TargetElementComponent tgtL : srcL.getTarget()) { 544 if (srcR.getCode().equals(tgtL.getCode())) { 545 issues.add("Right map says that there is no relationship for "+srcR.getCode()+" but right map has a "+tgtL.getRelationship().toCode()+" mapping to it from "+srcL.getCode()); 546 } 547 } 548 } 549 } 550 } 551 } 552 } 553 } 554 return changed; 555 } 556 557 private static ConceptMapRelationship inverse(ConceptMapRelationship relationship) { 558 switch (relationship) { 559 case EQUIVALENT: return ConceptMapRelationship.EQUIVALENT; 560 case RELATEDTO: return ConceptMapRelationship.RELATEDTO; 561 case SOURCEISBROADERTHANTARGET: return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 562 case SOURCEISNARROWERTHANTARGET: return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 563 default: return null; 564 } 565 } 566 567 private static boolean hasActualMappings(ConceptMapGroupComponent gr) { 568 for (SourceElementComponent e : gr.getElement()) { 569 for (TargetElementComponent tgt : e.getTarget()) { 570 if (tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 571 return true; 572 } 573 } 574 } 575 return false; 576 } 577 578 private static List<ElementMappingPair> getMappings(ConceptMapGroupComponent g, String source, String target) { 579 List<ElementMappingPair> res = new ArrayList<ConceptMapUtilities.ElementMappingPair>(); 580 581 for (SourceElementComponent src : g.getElement()) { 582 for (TargetElementComponent tgt : src.getTarget()) { 583 if (source.equals(src.getCode()) && target.equals(tgt.getCode())) { 584 res.add(new ElementMappingPair(src, tgt)); 585 } 586 } 587 } 588 return res; 589 } 590 591 private static ConceptMapGroupComponent findMatchingGroup(List<ConceptMapGroupComponent> groups, String source, String target) { 592 for (ConceptMapGroupComponent g : groups) { 593 if (source.equals(g.getSource()) && target.equals(g.getTarget())) { 594 return g; 595 } 596 } 597 return null; 598 } 599 600 /** 601 * 602 * @param cmF 603 * @return true if all the maps simply map to the same code 604 */ 605 public static boolean isUnityMap(ConceptMap cm) { 606 for (ConceptMapGroupComponent grp : cm.getGroup()) { 607 for (SourceElementComponent src : grp.getElement()) { 608 if (src.hasNoMap()) { 609 return false; 610 } 611 if (src.getTarget().size() != 1) { 612 return false; 613 } 614 if (src.getTargetFirstRep().getRelationship() != ConceptMapRelationship.EQUIVALENT && src.getTargetFirstRep().getRelationship() != ConceptMapRelationship.RELATEDTO) { 615 return false; 616 } 617 if (!src.getCode().equals(src.getTargetFirstRep().getCode())) { 618 return false; 619 } 620 } 621 } 622 return true; 623 } 624 625 public static int mapCount(ConceptMap cm) { 626 int i = 0; 627 for (ConceptMapGroupComponent grp : cm.getGroup()) { 628 for (SourceElementComponent src : grp.getElement()) { 629 i = i + src.getTarget().size(); 630 } 631 } 632 return i; 633 } 634 635 public static Set<Coding> listCodesWithNoMappings(Set<Coding> codes, ConceptMap map) { 636 Set<Coding> res = new HashSet<>(); 637 for (Coding c : codes) { 638 if (c != null && c.hasCode()) { 639 boolean found = false; 640 for (ConceptMapGroupComponent grp : map.getGroup()) { 641 if (matchesCoding(grp, c)) { 642 for (SourceElementComponent src : grp.getElement()) { 643 if (c.getCode().equals(src.getCode())) { 644 for (TargetElementComponent tgt : src.getTarget()) { 645 if (tgt.getRelationship() == ConceptMapRelationship.RELATEDTO || tgt.getRelationship() == ConceptMapRelationship.EQUIVALENT || tgt.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 646 found = true; 647 } 648 } 649 } 650 } 651 } 652 } 653 if (!found) { 654 res.add(c); 655 } 656 } 657 } 658 return res; 659 } 660 661 private static boolean matchesCoding(ConceptMapGroupComponent grp, Coding code) { 662 return code.getSystem().equals(grp.getSource()) || (code.getSystem()+"|"+code.getVersion()).equals(grp.getSource()); 663 } 664 665 public static List<String> translateCode(String name, String defaultValue, ConceptMap... cmList) { 666 List<String> res = translateCode(name, cmList); 667 if (res.isEmpty()) { 668 res.add(defaultValue); 669 } 670 return res; 671 } 672 public static List<String> translateCode(String name, ConceptMap... cmList) { 673 List<String> res = new ArrayList<>(); 674 res.add(name); 675 for (ConceptMap cm : cmList) { 676 res = translateCodes(res, cm); 677 } 678 return res; 679 } 680 681 private static List<String> translateCodes(List<String> codes, ConceptMap cm) { 682 List<String> res = new ArrayList<>(); 683 for (ConceptMapGroupComponent g : cm.getGroup()) { 684 for (SourceElementComponent e : g.getElement()) { 685 if (Utilities.existsInList(e.getCode(), codes)) { 686 for (TargetElementComponent t : e.getTarget()) { 687 if (t.getRelationship() == ConceptMapRelationship.EQUIVALENT || t.getRelationship() == ConceptMapRelationship.RELATEDTO || 688 t.getRelationship() == ConceptMapRelationship.SOURCEISBROADERTHANTARGET ||t.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 689 res.add(t.getCode()); 690 } 691 } 692 } 693 } 694 } 695 return res; 696 } 697 698 public static List<Coding> translateCoding(Coding code, ConceptMap... cmList) { 699 List<Coding> res = new ArrayList<>(); 700 for (ConceptMap cm : cmList) { 701 res = translateCodings(res, cm); 702 } 703 return res; 704 } 705 706 private static List<Coding> translateCodings(List<Coding> codes, ConceptMap cm) { 707 List<Coding> res = new ArrayList<>(); 708 for (ConceptMapGroupComponent g : cm.getGroup()) { 709 for (SourceElementComponent e : g.getElement()) { 710 if (hasCode(g.getSource(), e.getCode(), codes)) { 711 for (TargetElementComponent t : e.getTarget()) { 712 if (t.getRelationship() == ConceptMapRelationship.EQUIVALENT || t.getRelationship() == ConceptMapRelationship.RELATEDTO || 713 t.getRelationship() == ConceptMapRelationship.SOURCEISBROADERTHANTARGET ||t.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 714 res.add(new Coding().setSystem(g.getTarget()).setCode((t.getCode()))); 715 } 716 } 717 } 718 } 719 } 720 return res; 721 } 722 723 private static boolean hasCode(String system, String code, List<Coding> codes) { 724 for (Coding c : codes) { 725 if (system.equals(c.getSystem()) && code.equals(c.getCode())) { 726 return true; 727 } 728 } 729 return false; 730 } 731 732 public static List<MappingTriple> getBySource(ConceptMap map, Coding c) { 733 List<MappingTriple> list = new ArrayList<>(); 734 for (ConceptMapGroupComponent g : map.getGroup()) { 735 if (CanonicalType.matches(g.getSource(), c.getSystem(), c.getVersion())) { 736 for (SourceElementComponent e : g.getElement()) { 737 if (e.getCode().equals(c.getCode())) { 738 if (e.getNoMap()) { 739 list.add(new MappingTriple(g, e, null)); 740 } else { 741 for (TargetElementComponent t : e.getTarget()) { 742 list.add(new MappingTriple(g, e, t)); 743 } 744 } 745 } 746 } 747 } 748 } 749 return list; 750 } 751 752 public static List<MappingTriple> getByTarget(ConceptMap map, Coding c) { 753 List<MappingTriple> list = new ArrayList<>(); 754 for (ConceptMapGroupComponent g : map.getGroup()) { 755 if (CanonicalType.matches(g.getTarget(), c.getSystem(), c.getVersion())) { 756 for (SourceElementComponent e : g.getElement()) { 757 for (TargetElementComponent t : e.getTarget()) { 758 if (t.getCode().equals(c.getCode())) { 759 list.add(new MappingTriple(g, e, t)); 760 } 761 } 762 } 763 } 764 } 765 return list; 766 } 767 768}