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