
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, boolean extension) { 198 if (!cm.hasExperimental()) { 199 cm.setExperimental(false); 200 } 201 202 if (extension) { 203 if (!cm.hasMeta()) 204 cm.setMeta(new Meta()); 205 for (UriType t : cm.getMeta().getProfile()) 206 if ("http://hl7.org/fhir/StructureDefinition/shareableconceptmap".equals(t.getValue())) 207 return cm; 208 cm.getMeta().getProfile().add(new CanonicalType("http://hl7.org/fhir/StructureDefinition/shareableconceptmap")); 209 } 210 return cm; 211 } 212 213 public static ConceptMap invert(ConceptMap src, String id, String url, String name, boolean collate) { 214 ConceptMap dst = src.copy(); 215 dst.setId(id); 216 dst.setUrl(url); 217 dst.setName(name); 218 dst.getGroup().clear(); 219 dst.setSourceScope(src.getTargetScope()); 220 dst.setTargetScope(src.getSourceScope()); 221 for (ConceptMapGroupComponent gs : src.getGroup()) { 222 ConceptMapGroupComponent gd = dst.addGroup(); 223 gd.setTargetElement(gs.getSourceElement()); 224 gd.setSourceElement(gs.getTargetElement()); 225 Map<String, SourceElementComponent> dstMap = new HashMap<>(); 226 for (SourceElementComponent es : gs.getElement()) { 227 for (TargetElementComponent ts : es.getTarget()) { 228 SourceElementComponent ed = collate ? dstMap.get(ts.getCode()) : null; 229 if (ed == null) { 230 ed = gd.addElement(); 231 ed.setCodeElement(ts.getCodeElement()); 232 if (collate) { 233 dstMap.put(ed.getCode(), ed); 234 } 235 } 236 TargetElementComponent td = ed.addTarget(); 237 td.setCode(es.getCode()); 238 td.setComment(ts.getComment()); 239 td.setRelationship(invertRelationship(ts.getRelationship())); 240 } 241 } 242 } 243 return dst; 244 } 245 246 private static ConceptMapRelationship invertRelationship(ConceptMapRelationship relationship) { 247 if (relationship == null) { 248 return null; 249 } 250 switch (relationship) { 251 case EQUIVALENT: 252 return ConceptMapRelationship.EQUIVALENT; 253 case NOTRELATEDTO: 254 return ConceptMapRelationship.NOTRELATEDTO; 255 case NULL: 256 return ConceptMapRelationship.NULL; 257 case RELATEDTO: 258 return ConceptMapRelationship.RELATEDTO; 259 case SOURCEISBROADERTHANTARGET: 260 return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 261 case SOURCEISNARROWERTHANTARGET: 262 return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 263 default: 264 return null; 265 } 266 } 267 268 public static ConceptMap collapse(String id, String url, boolean cumulative, ConceptMap src, ConceptMap... sequence) { 269 ConceptMap res = src.copy(); 270 res.setId(id); 271 res.setUrl(url); 272 273 for (ConceptMap cm : sequence) { 274 if (res.hasTargetScope() && cm.hasTargetScope()) { 275 if (!cm.getSourceScope().primitiveValue().equals(res.getTargetScope().primitiveValue())) { 276 throw new Error("Mismatch between sequential concept maps: target was "+res.getTargetScope()+" and source is "+cm.getSourceScope()); 277 } else { 278 res.setTargetScope(cm.getTargetScope()); 279 } 280 } else { 281 res.setTargetScope(null); 282 } 283 } 284 285 for (ConceptMapGroupComponent gd : res.getGroup()) { 286 for (ConceptMap cm : sequence) { 287 for (ConceptMapGroupComponent gt : cm.getGroup()) { 288 if (gt.getSource().equals(gd.getTarget())) { 289 gd.setTarget(gt.getTarget()); 290 291 List<SourceElementComponent> processed = new ArrayList<ConceptMap.SourceElementComponent>(); 292 for (SourceElementComponent ed : gd.getElement()) { 293 List<TargetElementComponent> list = new ArrayList<>(); 294 list.addAll(ed.getTarget()); 295 ed.getTarget().clear(); 296 for (TargetElementComponent ts : list) { 297 for (SourceElementComponent et : gt.getElement()) { 298 if (et.getCode().equals(ed.getCode())) { 299 processed.add(et); 300 for (TargetElementComponent tt : et.getTarget()) { 301 ed.addTarget().setCode(tt.getCode()).setRelationship(combineRelationships(ts.getRelationship(), tt.getRelationship())); 302 } 303 } 304 } 305 } 306 if (ed.getTarget().isEmpty()) { 307 if (cumulative) { 308 ed.getTarget().addAll(list); 309 } else { 310 ed.setNoMap(true); 311 } 312 } 313 } 314 if (cumulative) { 315 for (SourceElementComponent et : gt.getElement()) { 316 if (!processed.contains(et)) { 317 gd.addElement(et.copy()); 318 } 319 } 320 } 321 } 322 Collections.sort(gt.getElement(), new ConceptMapElementSorter()); 323 for (SourceElementComponent e: gt.getElement()) { 324 Collections.sort(e.getTarget(), new ConceptMapTargetElementSorter()); 325 } 326 } 327 } 328 } 329 return res; 330 } 331 332 public static ConceptMapRelationship combineRelationships(ConceptMapRelationship rel1, ConceptMapRelationship rel2) { 333 switch (rel1) { 334 case EQUIVALENT: 335 return rel2; 336 case NOTRELATEDTO: 337 return ConceptMapRelationship.NOTRELATEDTO; 338 case NULL: 339 return null; 340 case RELATEDTO: 341 return rel2; 342 case SOURCEISBROADERTHANTARGET: 343 switch (rel2) { 344 case EQUIVALENT: 345 return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 346 case NOTRELATEDTO: 347 return ConceptMapRelationship.NOTRELATEDTO; 348 case NULL: 349 return null; 350 case RELATEDTO: 351 return ConceptMapRelationship.RELATEDTO; 352 case SOURCEISBROADERTHANTARGET: 353 return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 354 case SOURCEISNARROWERTHANTARGET: 355 return ConceptMapRelationship.RELATEDTO; 356 } 357 case SOURCEISNARROWERTHANTARGET: 358 switch (rel2) { 359 case EQUIVALENT: 360 return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 361 case NOTRELATEDTO: 362 return ConceptMapRelationship.NOTRELATEDTO; 363 case NULL: 364 return null; 365 case RELATEDTO: 366 return ConceptMapRelationship.RELATEDTO; 367 case SOURCEISBROADERTHANTARGET: 368 return ConceptMapRelationship.RELATEDTO; 369 case SOURCEISNARROWERTHANTARGET: 370 return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 371 } 372 } 373 return null; 374 } 375 376 public static boolean checkReciprocal(ConceptMap left, ConceptMap right, List<String> issues, boolean makeChanges) { 377 boolean changed = false; 378 if (!Base.compareDeep(left.getTargetScope(), right.getSourceScope(), true)) { 379 issues.add("scopes are not reciprocal: "+left.getTargetScope()+" vs "+right.getSourceScope()); 380 } 381 if (!Base.compareDeep(left.getSourceScope(), right.getTargetScope(), true)) { 382 issues.add("scopes are not reciprocal: "+left.getSourceScope()+" vs "+right.getTargetScope()); 383 } 384 for (ConceptMapGroupComponent gl : left.getGroup()) { 385 ConceptMapGroupComponent gr = findMatchingGroup(right.getGroup(), gl.getTarget(), gl.getSource()); 386 if (gr == null) { 387 for (SourceElementComponent e : gl.getElement()) { 388 for (TargetElementComponent t : e.getTarget()) { 389 if (t.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 390 if (makeChanges) { 391 changed = true; 392 right.forceGroup(gl.getTarget(), gl.getSource()).getOrAddElement(t.getCode()).addTarget(e.getCode(), inverse(t.getRelationship())); 393 } else { 394 issues.add("left maps from "+gl.getSource()+"#"+e.getCode()+" to "+gl.getTarget()+"#"+t.getCode()+" but right has no matching reverse map"); 395 } 396 } 397 } 398 } 399 } else { 400 for (SourceElementComponent srcL : gl.getElement()) { 401 if (!srcL.getNoMap()) { 402 for (TargetElementComponent tgtL : srcL.getTarget()) { 403 List<ElementMappingPair> pairs = getMappings(gr, tgtL.getCode(), srcL.getCode()); 404 if (tgtL.getRelationship() == null) { 405 issues.add("Left map has relationship "+srcL.getCode()+" with no relationship"); 406 } else switch (tgtL.getRelationship()) { 407 case EQUIVALENT: 408 if (pairs.isEmpty()) { 409 if (makeChanges) { 410 changed = true; 411 gr.getOrAddElement(tgtL.getCode()).addTarget(srcL.getCode(), ConceptMapRelationship.EQUIVALENT); 412 } else { 413 issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but there's no reverse relationship"); 414 } 415 } else for (ElementMappingPair pair : pairs) { 416 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) { 417 issues.add("Left map says that "+srcL.getCode()+" is equivalent to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 418 } 419 } 420 break; 421 case RELATEDTO: 422 if (pairs.isEmpty()) { 423 issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but there's no reverse relationship"); 424 } else for (ElementMappingPair pair : pairs) { 425 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) { 426 issues.add("Left map says that "+srcL.getCode()+" is related to "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 427 } 428 } 429 break; 430 case SOURCEISBROADERTHANTARGET: 431 if (pairs.isEmpty()) { 432 issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but there's no reverse relationship"); 433 } else for (ElementMappingPair pair : pairs) { 434 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 435 issues.add("Left map says that "+srcL.getCode()+" is broader than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 436 } 437 } 438 break; 439 case SOURCEISNARROWERTHANTARGET: 440 if (pairs.isEmpty()) { 441 issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but there's no reverse relationship"); 442 } else for (ElementMappingPair pair : pairs) { 443 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) { 444 issues.add("Left map says that "+srcL.getCode()+" is narrower than "+tgtL.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 445 } 446 } 447 break; 448 case NOTRELATEDTO: 449 for (ElementMappingPair pair : pairs) { 450 if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 451 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()); 452 } 453 } 454 break; 455 } 456 } 457 } else { 458 for (SourceElementComponent srcR : gr.getElement()) { 459 for (TargetElementComponent tgtR : srcR.getTarget()) { 460 if (srcL.getCode().equals(tgtR.getCode())) { 461 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()); 462 } 463 } 464 } 465 } 466 } 467 } 468 } 469 for (ConceptMapGroupComponent gr : right.getGroup()) { 470 ConceptMapGroupComponent gl = findMatchingGroup(left.getGroup(), gr.getTarget(), gr.getSource()); 471 if (gl == null) { 472 for (SourceElementComponent e : gr.getElement()) { 473 for (TargetElementComponent t : e.getTarget()) { 474 if (t.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 475 if (makeChanges) { 476 changed = true; 477 left.forceGroup(gr.getTarget(), gr.getSource()).getOrAddElement(t.getCode()).addTarget(e.getCode(), inverse(t.getRelationship())); 478 } else { 479 issues.add("left maps from "+gr.getSource()+"#"+e.getCode()+" to "+gr.getTarget()+"#"+t.getCode()+" but right has no matching reverse map"); 480 } 481 } 482 } 483 } 484 } else { 485 for (SourceElementComponent srcR : gr.getElement()) { 486 if (!"CHECK!".equals(srcR.getCode())) { 487 if (!srcR.getNoMap()) { 488 for (TargetElementComponent tgtR : srcR.getTarget()) { 489 List<ElementMappingPair> pairs = getMappings(gl, tgtR.getCode(), srcR.getCode()); 490 if (tgtR.getRelationship() == null) { 491 issues.add("Right map has relationship "+srcR.getCode()+" with no relationship"); 492 } else switch (tgtR.getRelationship()) { 493 case EQUIVALENT: 494 if (pairs.isEmpty()) { 495 if (makeChanges) { 496 changed = true; 497 gl.getOrAddElement(tgtR.getCode()).addTarget(srcR.getCode(), ConceptMapRelationship.EQUIVALENT); 498 } else { 499 issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but there's no reverse relationship"); 500 } 501 } else for (ElementMappingPair pair : pairs) { 502 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT) { 503 issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 504 } 505 } 506 break; 507 case RELATEDTO: 508 if (pairs.isEmpty()) { 509 issues.add("Right map says that "+srcR.getCode()+" is related to "+tgtR.getCode()+" but there's no reverse relationship"); 510 } else for (ElementMappingPair pair : pairs) { 511 if (pair.tgt.getRelationship() != ConceptMapRelationship.EQUIVALENT && pair.tgt.getRelationship() != ConceptMapRelationship.RELATEDTO) { 512 issues.add("Right map says that "+srcR.getCode()+" is equivalent to "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 513 } 514 } 515 break; 516 case SOURCEISBROADERTHANTARGET: 517 if (pairs.isEmpty()) { 518 issues.add("Right map says that "+srcR.getCode()+" is broader than "+tgtR.getCode()+" but there's no reverse relationship"); 519 } else for (ElementMappingPair pair : pairs) { 520 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 521 issues.add("Right map says that "+srcR.getCode()+" is broader than "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 522 } 523 } 524 break; 525 case SOURCEISNARROWERTHANTARGET: 526 if (pairs.isEmpty()) { 527 issues.add("Right map says that "+srcR.getCode()+" is narrower than "+tgtR.getCode()+" but there's no reverse relationship"); 528 } else for (ElementMappingPair pair : pairs) { 529 if (pair.tgt.getRelationship() != ConceptMapRelationship.SOURCEISBROADERTHANTARGET) { 530 issues.add("Right map says that "+srcR.getCode()+" is narrower than "+tgtR.getCode()+" but the reverse relationship has type "+pair.tgt.getRelationship().toCode()); 531 } 532 } 533 break; 534 case NOTRELATEDTO: 535 for (ElementMappingPair pair : pairs) { 536 if (pair.tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 537 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()); 538 } 539 } 540 break; 541 } 542 } 543 } else { 544 for (SourceElementComponent srcL : gr.getElement()) { 545 for (TargetElementComponent tgtL : srcL.getTarget()) { 546 if (srcR.getCode().equals(tgtL.getCode())) { 547 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()); 548 } 549 } 550 } 551 } 552 } 553 } 554 } 555 } 556 return changed; 557 } 558 559 private static ConceptMapRelationship inverse(ConceptMapRelationship relationship) { 560 switch (relationship) { 561 case EQUIVALENT: return ConceptMapRelationship.EQUIVALENT; 562 case RELATEDTO: return ConceptMapRelationship.RELATEDTO; 563 case SOURCEISBROADERTHANTARGET: return ConceptMapRelationship.SOURCEISNARROWERTHANTARGET; 564 case SOURCEISNARROWERTHANTARGET: return ConceptMapRelationship.SOURCEISBROADERTHANTARGET; 565 default: return null; 566 } 567 } 568 569 private static boolean hasActualMappings(ConceptMapGroupComponent gr) { 570 for (SourceElementComponent e : gr.getElement()) { 571 for (TargetElementComponent tgt : e.getTarget()) { 572 if (tgt.getRelationship() != ConceptMapRelationship.NOTRELATEDTO) { 573 return true; 574 } 575 } 576 } 577 return false; 578 } 579 580 private static List<ElementMappingPair> getMappings(ConceptMapGroupComponent g, String source, String target) { 581 List<ElementMappingPair> res = new ArrayList<ConceptMapUtilities.ElementMappingPair>(); 582 583 for (SourceElementComponent src : g.getElement()) { 584 for (TargetElementComponent tgt : src.getTarget()) { 585 if (source.equals(src.getCode()) && target.equals(tgt.getCode())) { 586 res.add(new ElementMappingPair(src, tgt)); 587 } 588 } 589 } 590 return res; 591 } 592 593 private static ConceptMapGroupComponent findMatchingGroup(List<ConceptMapGroupComponent> groups, String source, String target) { 594 for (ConceptMapGroupComponent g : groups) { 595 if (source.equals(g.getSource()) && target.equals(g.getTarget())) { 596 return g; 597 } 598 } 599 return null; 600 } 601 602 /** 603 * 604 * @param cmF 605 * @return true if all the maps simply map to the same code 606 */ 607 public static boolean isUnityMap(ConceptMap cm) { 608 for (ConceptMapGroupComponent grp : cm.getGroup()) { 609 for (SourceElementComponent src : grp.getElement()) { 610 if (src.hasNoMap()) { 611 return false; 612 } 613 if (src.getTarget().size() != 1) { 614 return false; 615 } 616 if (src.getTargetFirstRep().getRelationship() != ConceptMapRelationship.EQUIVALENT && src.getTargetFirstRep().getRelationship() != ConceptMapRelationship.RELATEDTO) { 617 return false; 618 } 619 if (!src.getCode().equals(src.getTargetFirstRep().getCode())) { 620 return false; 621 } 622 } 623 } 624 return true; 625 } 626 627 public static int mapCount(ConceptMap cm) { 628 int i = 0; 629 for (ConceptMapGroupComponent grp : cm.getGroup()) { 630 for (SourceElementComponent src : grp.getElement()) { 631 i = i + src.getTarget().size(); 632 } 633 } 634 return i; 635 } 636 637 public static Set<Coding> listCodesWithNoMappings(Set<Coding> codes, ConceptMap map) { 638 Set<Coding> res = new HashSet<>(); 639 for (Coding c : codes) { 640 if (c != null && c.hasCode()) { 641 boolean found = false; 642 for (ConceptMapGroupComponent grp : map.getGroup()) { 643 if (matchesCoding(grp, c)) { 644 for (SourceElementComponent src : grp.getElement()) { 645 if (c.getCode().equals(src.getCode())) { 646 for (TargetElementComponent tgt : src.getTarget()) { 647 if (tgt.getRelationship() == ConceptMapRelationship.RELATEDTO || tgt.getRelationship() == ConceptMapRelationship.EQUIVALENT || tgt.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 648 found = true; 649 } 650 } 651 } 652 } 653 } 654 } 655 if (!found) { 656 res.add(c); 657 } 658 } 659 } 660 return res; 661 } 662 663 private static boolean matchesCoding(ConceptMapGroupComponent grp, Coding code) { 664 return code.getSystem().equals(grp.getSource()) || (code.getSystem()+"|"+code.getVersion()).equals(grp.getSource()); 665 } 666 667 public static List<String> translateCode(String name, String defaultValue, ConceptMap... cmList) { 668 List<String> res = translateCode(name, cmList); 669 if (res.isEmpty()) { 670 res.add(defaultValue); 671 } 672 return res; 673 } 674 public static List<String> translateCode(String name, ConceptMap... cmList) { 675 List<String> res = new ArrayList<>(); 676 res.add(name); 677 for (ConceptMap cm : cmList) { 678 res = translateCodes(res, cm); 679 } 680 return res; 681 } 682 683 private static List<String> translateCodes(List<String> codes, ConceptMap cm) { 684 List<String> res = new ArrayList<>(); 685 for (ConceptMapGroupComponent g : cm.getGroup()) { 686 for (SourceElementComponent e : g.getElement()) { 687 if (Utilities.existsInList(e.getCode(), codes)) { 688 for (TargetElementComponent t : e.getTarget()) { 689 if (t.getRelationship() == ConceptMapRelationship.EQUIVALENT || t.getRelationship() == ConceptMapRelationship.RELATEDTO || 690 t.getRelationship() == ConceptMapRelationship.SOURCEISBROADERTHANTARGET ||t.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 691 res.add(t.getCode()); 692 } 693 } 694 } 695 } 696 } 697 return res; 698 } 699 700 public static List<Coding> translateCoding(Coding code, ConceptMap... cmList) { 701 List<Coding> res = new ArrayList<>(); 702 for (ConceptMap cm : cmList) { 703 res = translateCodings(res, cm); 704 } 705 return res; 706 } 707 708 private static List<Coding> translateCodings(List<Coding> codes, ConceptMap cm) { 709 List<Coding> res = new ArrayList<>(); 710 for (ConceptMapGroupComponent g : cm.getGroup()) { 711 for (SourceElementComponent e : g.getElement()) { 712 if (hasCode(g.getSource(), e.getCode(), codes)) { 713 for (TargetElementComponent t : e.getTarget()) { 714 if (t.getRelationship() == ConceptMapRelationship.EQUIVALENT || t.getRelationship() == ConceptMapRelationship.RELATEDTO || 715 t.getRelationship() == ConceptMapRelationship.SOURCEISBROADERTHANTARGET ||t.getRelationship() == ConceptMapRelationship.SOURCEISNARROWERTHANTARGET) { 716 res.add(new Coding().setSystem(g.getTarget()).setCode((t.getCode()))); 717 } 718 } 719 } 720 } 721 } 722 return res; 723 } 724 725 private static boolean hasCode(String system, String code, List<Coding> codes) { 726 for (Coding c : codes) { 727 if (system.equals(c.getSystem()) && code.equals(c.getCode())) { 728 return true; 729 } 730 } 731 return false; 732 } 733 734 public static List<MappingTriple> getBySource(ConceptMap map, Coding c) { 735 List<MappingTriple> list = new ArrayList<>(); 736 for (ConceptMapGroupComponent g : map.getGroup()) { 737 if (CanonicalType.matches(g.getSource(), c.getSystem(), c.getVersion())) { 738 for (SourceElementComponent e : g.getElement()) { 739 if (e.getCode().equals(c.getCode())) { 740 if (e.getNoMap()) { 741 list.add(new MappingTriple(g, e, null)); 742 } else { 743 for (TargetElementComponent t : e.getTarget()) { 744 list.add(new MappingTriple(g, e, t)); 745 } 746 } 747 } 748 } 749 } 750 } 751 return list; 752 } 753 754 public static List<MappingTriple> getByTarget(ConceptMap map, Coding c) { 755 List<MappingTriple> list = new ArrayList<>(); 756 for (ConceptMapGroupComponent g : map.getGroup()) { 757 if (CanonicalType.matches(g.getTarget(), c.getSystem(), c.getVersion())) { 758 for (SourceElementComponent e : g.getElement()) { 759 for (TargetElementComponent t : e.getTarget()) { 760 if (t.getCode().equals(c.getCode())) { 761 list.add(new MappingTriple(g, e, t)); 762 } 763 } 764 } 765 } 766 } 767 return list; 768 } 769 770}