
001package org.hl7.fhir.r5.context; 002 003import java.util.*; 004 005import org.hl7.fhir.exceptions.FHIRException; 006import org.hl7.fhir.r5.model.CanonicalResource; 007import org.hl7.fhir.r5.model.CodeSystem; 008import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode; 009import org.hl7.fhir.r5.model.PackageInformation; 010import org.hl7.fhir.r5.model.StructureDefinition; 011import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 012import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage; 013import org.hl7.fhir.utilities.NaturalOrderComparator; 014import org.hl7.fhir.utilities.Utilities; 015import org.hl7.fhir.utilities.VersionUtilities; 016 017/** 018 * This manages a cached list of resources, and provides high speed access by URL / URL+version, and assumes that patch version doesn't matter for access 019 * note, though, that not all resources have semver versions 020 * 021 * @author graha 022 * 023 */ 024 025@MarkedToMoveToAdjunctPackage 026public class CanonicalResourceManager<T extends CanonicalResource> { 027 028 private final String[] INVALID_TERMINOLOGY_URLS = { 029 "http://snomed.info/sct", 030 "http://dicom.nema.org/resources/ontology/DCM", 031 "http://nucc.org/provider-taxonomy" 032 }; 033 private int loadCount = 0; 034 035 public static abstract class CanonicalResourceProxy { 036 private String type; 037 private String id; 038 private String url; 039 private String version; 040 private String supplements; 041 private String derivation; 042 private CanonicalResource resource; 043 private boolean hacked; 044 private String content; 045 046 public CanonicalResourceProxy(String type, String id, String url, String version, String supplements, String derivation, String content) { 047 super(); 048 this.type = type; 049 this.id = id; 050 this.url = url; 051 this.version = version; 052 this.supplements = supplements; 053 this.content = content; 054 this.derivation = derivation; 055 } 056 057 public String getType() { 058 return type; 059 } 060 061 public String getId() { 062 return id; 063 } 064 065 public String getUrl() { 066 return url; 067 } 068 069 public String getVersion() { 070 return version; 071 } 072 073 public boolean hasId() { 074 return id != null; 075 } 076 077 public boolean hasUrl() { 078 return url != null; 079 } 080 081 public boolean hasVersion() { 082 return version != null; 083 } 084 085 public String getSupplements() { 086 return supplements; 087 } 088 089 090 public String getContent() { 091 return content; 092 } 093 094 public String getDerivation() { 095 return derivation; 096 } 097 098 public void setDerivation(String derivation) { 099 this.derivation = derivation; 100 } 101 102 public CanonicalResource getResource() throws FHIRException { 103 if (resource == null) { 104 resource = loadResource(); 105 if (hacked) { 106 resource.setUrl(url).setVersion(version); 107 } 108 if (resource instanceof CodeSystem) { 109 CodeSystemUtilities.crossLinkCodeSystem((CodeSystem) resource); 110 } 111 } 112 return resource; 113 } 114 115 public void setResource(CanonicalResource resource) { 116 this.resource = resource; 117 } 118 119 public abstract CanonicalResource loadResource() throws FHIRException; 120 121 @Override 122 public String toString() { 123 return type+"/"+id+": "+url+"|"+version; 124 } 125 126 public void hack(String url, String version) { 127 this.url = url; 128 this.version = version; 129 this.hacked = true; 130 131 } 132 133 /** 134 * used in cross version settings by the package loaders. 135 */ 136 public void updateInfo() { 137 type = resource.fhirType(); 138 id = resource.getId(); 139 url = resource.getUrl(); 140 version = resource.getVersion(); 141 if (resource instanceof CodeSystem) { 142 supplements = ((CodeSystem) resource).getSupplements(); 143 content = ((CodeSystem) resource).getContentElement().asStringValue(); 144 } 145 if (resource instanceof StructureDefinition) { 146 derivation = ((StructureDefinition) resource).getDerivationElement().asStringValue(); 147 } 148 } 149 } 150 151 public static class CanonicalListSorter implements Comparator<CanonicalResource> { 152 153 @Override 154 public int compare(CanonicalResource arg0, CanonicalResource arg1) { 155 String u0 = arg0.getUrl(); 156 String u1 = arg1.getUrl(); 157 return u0.compareTo(u1); 158 } 159 } 160 161 public class CachedCanonicalResource<T1 extends CanonicalResource> { 162 int loadingOrder = 0; 163 private T1 resource; 164 private CanonicalResourceProxy proxy; 165 private PackageInformation packageInfo; 166 167 public CachedCanonicalResource(T1 resource, PackageInformation packageInfo) { 168 super(); 169 if (resource == null) { 170 throw new NullPointerException("Canonical resource cannot be null"); 171 } 172 this.resource = resource; 173 this.packageInfo = packageInfo; 174 } 175 176 public CachedCanonicalResource(CanonicalResourceProxy proxy, PackageInformation packageInfo) { 177 super(); 178 if (proxy == null) { 179 throw new NullPointerException("Canonical resource proxy cannot be null"); 180 } 181 this.proxy = proxy; 182 this.packageInfo = packageInfo; 183 } 184 185 public T1 getResource() { 186 if (resource == null) { 187 @SuppressWarnings("unchecked") 188 T1 res = (T1) proxy.getResource(); 189 if (res == null) { 190 throw new Error("Proxy loading a resource from "+packageInfo+" failed and returned null"); 191 } 192 synchronized (this) { 193 resource = res; 194 } 195 resource.setSourcePackage(packageInfo); 196 proxy = null; 197 } 198 return resource; 199 } 200 201 public PackageInformation getPackageInfo() { 202 return packageInfo; 203 } 204 public String getUrl() { 205 return resource != null ? resource.getUrl() : proxy.getUrl(); 206 } 207 public String getId() { 208 return resource != null ? resource.getId() : proxy.getId(); 209 } 210 public String getVersion() { 211 return resource != null ? resource.getVersion() : proxy.getVersion(); 212 } 213 public boolean hasVersion() { 214 return resource != null ? resource.hasVersion() : proxy.getVersion() != null; 215 } 216 public String getContent() { 217 if (this.resource instanceof CodeSystem) { 218 CodeSystemContentMode cnt = ((CodeSystem) resource).getContent(); 219 return cnt == null ? null : cnt.toCode(); 220 } else if (proxy != null) { 221 return proxy.getContent(); 222 } else { 223 return null; 224 } 225 } 226 227 @Override 228 public String toString() { 229 return resource != null ? resource.fhirType()+"/"+resource.getId()+"["+resource.getUrl()+"|"+resource.getVersion()+"]" : proxy.toString(); 230 } 231 232 public String supplements() { 233 if (resource == null) { 234 return proxy.getSupplements(); 235 } else { 236 return resource instanceof CodeSystem ? ((CodeSystem) resource).getSupplements() : null; 237 } 238 } 239 240 public Object getDerivation() { 241 if (resource == null) { 242 return proxy.getDerivation(); 243 } else { 244 return resource instanceof StructureDefinition ? ((StructureDefinition) resource).getDerivationElement().primitiveValue() : null; 245 } 246 } 247 248 public void unload() { 249 if (proxy != null) { 250 resource = null; 251 } 252 } 253 254 public int getLoadingOrder() { 255 return loadingOrder; 256 } 257 258 public void setLoadingOrder(int loadingOrder) { 259 this.loadingOrder = loadingOrder; 260 } 261 } 262 263 public class MetadataResourceVersionComparator<T1 extends CachedCanonicalResource<T>> implements Comparator<T1> { 264 @Override 265 public int compare(T1 arg1, T1 arg2) { 266 return compareResources(arg1, arg2); 267 } 268 269 } 270 271 private boolean minimalMemory; 272 private boolean enforceUniqueId; 273 private List<CachedCanonicalResource<T>> list = new ArrayList<>(); 274 private Map<String, List<CachedCanonicalResource<T>>> listForId; 275 private Map<String, List<CachedCanonicalResource<T>>> listForUrl; 276 private Map<String, CachedCanonicalResource<T>> map; 277 private Map<String, List<CachedCanonicalResource<T>>> supplements; // general index based on CodeSystem.supplements 278 private String version; // for debugging purposes 279 280 281 public CanonicalResourceManager(boolean enforceUniqueId, boolean minimalMemory) { 282 super(); 283 this.enforceUniqueId = enforceUniqueId; 284 this.minimalMemory = minimalMemory; 285 list = new ArrayList<>(); 286 listForId = new HashMap<>(); 287 listForUrl = new HashMap<>(); 288 map = new HashMap<>(); 289 supplements = new HashMap<>(); // general index based on CodeSystem.supplements 290 } 291 292 293 public String getVersion() { 294 return version; 295 } 296 297 298 public void setVersion(String version) { 299 this.version = version; 300 } 301 302 303 public void copy(CanonicalResourceManager<T> source) { 304 list.clear(); 305 map.clear(); 306 list.addAll(source.list); 307 map.putAll(source.map); 308 } 309 310 public void register(CanonicalResourceProxy r, PackageInformation packgeInfo) { 311 if (!r.hasId()) { 312 throw new FHIRException("An id is required for a deferred load resource"); 313 } 314 CanonicalResourceManager<T>.CachedCanonicalResource<T> cr = new CachedCanonicalResource<T>(r, packgeInfo); 315 see(cr); 316 } 317 318 public void see(T r, PackageInformation packgeInfo) { 319 if (r != null) { 320 if (!r.hasId()) { 321 r.setId(UUID.randomUUID().toString()); 322 } 323 CanonicalResourceManager<T>.CachedCanonicalResource<T> cr = new CachedCanonicalResource<T>(r, packgeInfo); 324 see(cr); 325 } 326 } 327 328 public void see(CachedCanonicalResource<T> cr) { 329 // -- 1. exit conditions ----------------------------------------------------------------------------- 330 331 // ignore UTG NUCC erroneous code system 332 if (cr.getPackageInfo() != null 333 && cr.getPackageInfo().getId() != null 334 && cr.getPackageInfo().getId().startsWith("hl7.terminology") 335 && Arrays.stream(INVALID_TERMINOLOGY_URLS).anyMatch((it)->it.equals(cr.getUrl()))) { 336 return; 337 } 338 if (map.get(cr.getUrl()) != null && (cr.getPackageInfo() != null && cr.getPackageInfo().isExamplesPackage())) { 339 return; 340 } 341 342 // -- 2. preparation ----------------------------------------------------------------------------- 343 if (cr.resource != null && cr.getPackageInfo() != null) { 344 cr.resource.setSourcePackage(cr.getPackageInfo()); 345 } 346 347 // -- 3. deleting existing content --------------------------------------------------------------- 348 if (enforceUniqueId && map.containsKey(cr.getId())) { 349 drop(cr.getId()); 350 } 351 352 // special case logic for UTG support prior to version 5 353 if (cr.getPackageInfo() != null && cr.getPackageInfo().getId().startsWith("hl7.terminology")) { 354 List<CachedCanonicalResource<T>> toDrop = new ArrayList<>(); 355 for (CachedCanonicalResource<T> n : list) { 356 if (n.getUrl() != null && n.getUrl().equals(cr.getUrl()) && isBasePackage(n.getPackageInfo())) { 357 toDrop.add(n); 358 } 359 } 360 for (CachedCanonicalResource<T> n : toDrop) { 361 drop(n); 362 } 363 } 364// CachedCanonicalResource<T> existing = cr.hasVersion() ? map.get(cr.getUrl()+"|"+cr.getVersion()) : map.get(cr.getUrl()+"|#0"); 365// if (existing != null) { 366// drop(existing); // was list.remove(existing) 367// } 368 369 // -- 4. ok we add it to the list --------------------------------------------------------------- 370 if (!enforceUniqueId) { 371 if (!listForId.containsKey(cr.getId())) { 372 listForId.put(cr.getId(), new ArrayList<>()); 373 } 374 List<CachedCanonicalResource<T>> set = listForId.get(cr.getId()); 375 set.add(cr); 376 } 377 list.add(cr); 378 if (!listForUrl.containsKey(cr.getUrl())) { 379 listForUrl.put(cr.getUrl(), new ArrayList<>()); 380 } 381 addToSupplements(cr); 382 List<CachedCanonicalResource<T>> set = listForUrl.get(cr.getUrl()); 383 set.add(cr); 384 if (set.size() > 1) { 385 Collections.sort(set, new MetadataResourceVersionComparator<CachedCanonicalResource<T>>()); 386 } 387 388 // -- 4. add to the map all the ways --------------------------------------------------------------- 389 String pv = cr.getPackageInfo() != null ? cr.getPackageInfo().getVID() : null; 390 addToMap(cr.getId(), cr); // we do this so we can drop by id - if not enforcing id, it's just the most recent resource with this id 391 addToMap(cr.hasVersion() ? cr.getUrl()+"|"+cr.getVersion() : cr.getUrl()+"|#0", cr); 392 if (pv != null) { 393 addToMap(pv+":"+(cr.hasVersion() ? cr.getUrl()+"|"+cr.getVersion() : cr.getUrl()+"|#0"), cr); 394 } 395 int ndx = set.indexOf(cr); 396 if (ndx == set.size()-1) { 397 addToMap(cr.getUrl(), cr); 398 if (pv != null) { 399 addToMap(pv+":"+cr.getUrl(), cr); 400 } 401 } 402 String mmp = VersionUtilities.getMajMinPatch(cr.getVersion()); 403 if (mmp != null && !mmp.equals(cr.getVersion())) { 404 if (pv != null) { 405 addToMap(pv+":"+cr.getUrl()+"|"+mmp, cr); 406 } 407 if (set.size() - 1 == ndx) { 408 addToMap(cr.getUrl()+"|"+mmp, cr); 409 } else { 410 for (int i = set.size() - 1; i > ndx; i--) { 411 if (mmp.equals(VersionUtilities.getMajMinPatch(set.get(i).getVersion()))) { 412 return; 413 } 414 addToMap(cr.getUrl()+"|"+mmp, cr); 415 } 416 } 417 } 418 419 String mm = VersionUtilities.getMajMin(cr.getVersion()); 420 if (mm != null) { 421 if (pv != null) { 422 addToMap(pv+":"+cr.getUrl()+"|"+mm, cr); 423 } 424 if (set.size() - 1 == ndx) { 425 addToMap(cr.getUrl()+"|"+mm, cr); 426 } else { 427 for (int i = set.size() - 1; i > ndx; i--) { 428 if (mm.equals(VersionUtilities.getMajMin(set.get(i).getVersion()))) { 429 return; 430 } 431 addToMap(cr.getUrl()+"|"+mm, cr); 432 } 433 } 434 } 435 cr.setLoadingOrder(++loadCount); 436 } 437 438 private int compareResources (CachedCanonicalResource<T> c1, CachedCanonicalResource<T> c2) { 439 // the order of comparison is 440 // by resource version 441 // by content type (ignore content=not-present code systems) 442 // by package version (e.g for THO) 443 // by whether we've seen it or not 444 int res = compareByResourceVersion(c1.getVersion(), c2.getVersion()); 445 if (res == 0) { 446 res = compareByResourceContent(c1.getContent(), c2.getContent()); 447 } 448 if (res == 0) { 449 res = compareByPackageVersion(c1.getPackageInfo(), c2.getPackageInfo()); 450 } 451 if (res == 0) { 452 res = compareByLoadCount(c1.getLoadingOrder(), c2.getLoadingOrder()); 453 } 454 return res; 455 } 456 457 private int compareByLoadCount(int o1, int o2) { 458 if (01 == 0 && o2 == 0) { 459 return 0; 460 } else if (o1 == 0) { 461 return 1; 462 } else if (o2 == 0) { 463 return -1; 464 } else { 465 return Integer.compare(o1, o2); 466 } 467 } 468 469 private int compareByResourceVersion(String v1, String v2) { 470 if (v1 == null && v2 == null) { 471 return 0; 472 } else if (v1 == null) { 473 return -1; // this order is deliberate 474 } else if (v2 == null) { 475 return 1; 476 } else if (VersionUtilities.isSemVer(v1) && VersionUtilities.isSemVer(v2)) { 477 return VersionUtilities.compareVersions(v1, v2); 478 } else if (Utilities.isInteger(v1) && Utilities.isInteger(v2)) { 479 return Integer.compare(Integer.parseInt(v1), Integer.parseInt(v2)); 480 } else { 481 return new NaturalOrderComparator<String>().compare(v1, v2); 482 } 483 } 484 485 private int compareByLoadCount(String v1, String v2) { 486 if (v1 == null && v2 == null) { 487 return 0; 488 } else if (v1 == null) { 489 return 1; 490 } else if (v2 == null) { 491 return -1; 492 } else if (VersionUtilities.isSemVer(v1) && VersionUtilities.isSemVer(v2)) { 493 return VersionUtilities.compareVersions(v1, v2); 494 } else if (Utilities.isInteger(v1) && Utilities.isInteger(v2)) { 495 return Integer.compare(Integer.parseInt(v1), Integer.parseInt(v2)); 496 } else { 497 return new NaturalOrderComparator<String>().compare(v1, v2); 498 } 499 } 500 501 private int compareByPackageVersion(PackageInformation p1, PackageInformation p2) { 502 if (p1 == null && p2 == null) { 503 return 0; 504 } else if (p1 == null) { 505 return 1; // this order is deliberate. 506 } else if (p2 == null) { 507 return -1; 508 } else if (p1.getId().equals(p2.getId())) { 509 int res = compareByResourceVersion(p1.getVersion(), p2.getVersion()); 510 if (res == 0) { 511 res = compareByDate(p1.getDate(), p2.getDate()); 512 } 513 return res; 514 } else { 515 // comes from a mismatch package - presumable some repackaging has gone on - the most recent by date wins 516 return compareByDate(p1.getDate(), p2.getDate()); 517 } 518 } 519 520 private int compareByDate(Date d1, Date d2) { 521 if (d1 == null && d2 == null) { 522 return 0; 523 } else if (d1 == null) { 524 return 1; 525 } else if (d2 == null) { 526 return -1; 527 } else { 528 return d1.compareTo(d2); 529 } 530 } 531 532 private int compareByResourceContent(String c1, String c2) { 533 if (c1 == null && c2 == null) { 534 return 0; 535 } else if (c1 == null) { 536 return -1; 537 } else if (c2 == null) { 538 return 1; 539 } else if (c1.equals(c2)) { 540 return 0; 541 } else { 542 int i1 = orderOfContent(c1); 543 int i2 = orderOfContent(c2); 544 return Integer.compare(i1, i2); 545 } 546 } 547 548 private int orderOfContent(String c) { 549 switch (c) { 550 case "not-present": return 1; 551 case "example": return 2; 552 case "fragment": return 3; 553 case "complete": return 5; 554 case "supplement": return 4; 555 } 556 return 0; 557 } 558 559 private void addToMap(String key, CachedCanonicalResource<T> cr) { 560 boolean newIsOlder = false; 561 CachedCanonicalResource<T> existing = map.get(key); 562 if (existing != null) { 563 // at every level of adding something to the map, the most recent (by versioning) wins. 564 int comp = compareResources(existing, cr); 565 newIsOlder = comp == 1; 566 } 567 if (!newIsOlder) { 568 map.put(key, cr); 569 } 570 } 571 572 private void addToSupplements(CanonicalResourceManager<T>.CachedCanonicalResource<T> cr) { 573 String surl = cr.supplements(); 574 if (surl != null) { 575 List<CanonicalResourceManager<T>.CachedCanonicalResource<T>> list = supplements.get(surl); 576 if (list == null) { 577 list = new ArrayList<>(); 578 supplements.put(surl, list); 579 } 580 list.add(cr); 581 } 582 } 583 584 585 public void drop(CachedCanonicalResource<T> cr) { 586 while (map.values().remove(cr)); 587 while (listForId.values().remove(cr)); 588 while (listForUrl.values().remove(cr)); 589 String surl = cr.supplements(); 590 if (surl != null) { 591 supplements.get(surl).remove(cr); 592 } 593 list.remove(cr); 594 List<CachedCanonicalResource<T>> set = listForUrl.get(cr.getUrl()); 595 if (set != null) { // it really should be 596 boolean last = set.indexOf(cr) == set.size()-1; 597 set.remove(cr); 598 if (!set.isEmpty()) { 599 CachedCanonicalResource<T> crl = set.get(set.size()-1); 600 if (last) { 601 map.put(crl.getUrl(), crl); 602 } 603 String mm = VersionUtilities.getMajMin(cr.getVersion()); 604 if (mm != null) { 605 for (int i = set.size()-1; i >= 0; i--) { 606 if (mm.equals(VersionUtilities.getMajMin(set.get(i).getVersion()))) { 607 map.put(cr.getUrl()+"|"+mm, set.get(i)); 608 break; 609 } 610 } 611 } 612 } 613 } 614 } 615 616 public void drop(String id) { 617 if (enforceUniqueId) { 618 CachedCanonicalResource<T> cr = map.get(id); 619 if (cr != null) { 620 drop(cr); 621 } 622 } else { 623 List<CachedCanonicalResource<T>> set = listForId.get(id); 624 if (set != null) { // it really should be 625 for (CachedCanonicalResource<T> i : set) { 626 drop(i); 627 } 628 } 629 } 630 } 631 632 private boolean isBasePackage(PackageInformation packageInfo) { 633 return packageInfo == null ? false : VersionUtilities.isCorePackage(packageInfo.getId()); 634 } 635 636 private void updateList(String url, String version) { 637 List<CachedCanonicalResource<T>> rl = new ArrayList<>(); 638 for (CachedCanonicalResource<T> t : list) { 639 if (url.equals(t.getUrl()) && !rl.contains(t)) { 640 rl.add(t); 641 } 642 } 643 if (rl.size() > 0) { 644 // sort by version as much as we are able 645 // the current is the latest 646 map.put(url, rl.get(rl.size()-1)); 647 // now, also, the latest for major/minor 648 if (version != null) { 649 CachedCanonicalResource<T> latest = null; 650 for (CachedCanonicalResource<T> t : rl) { 651 if (VersionUtilities.versionMatches(t.getVersion(), version)) { 652 latest = t; 653 } 654 } 655 if (latest != null) { // might be null if it's not using semver 656 String lv = VersionUtilities.getMajMin(latest.getVersion()); 657 if (lv != null && !lv.equals(version)) 658 map.put(url+"|"+lv, rl.get(rl.size()-1)); 659 } 660 } 661 } 662 } 663 664 665 public boolean has(String url) { 666 return map.containsKey(url); 667 } 668 669 public boolean has(String system, String version) { 670 if (map.containsKey(system+"|"+version)) 671 return true; 672 String mm = VersionUtilities.getMajMin(version); 673 if (mm != null) 674 return map.containsKey(system+"|"+mm); 675 else 676 return false; 677 } 678 679 public T get(String url) { 680 return map.containsKey(url) ? map.get(url).getResource() : null; 681 } 682 683 public T get(String system, String version) { 684 if (version == null) { 685 return get(system); 686 } else { 687 if (map.containsKey(system+"|"+version)) 688 return map.get(system+"|"+version).getResource(); 689 if (VersionUtilities.isSemVer(version) && !Utilities.containsInList(version, "+", "-")) { 690 String mm = VersionUtilities.getMajMin(version); 691 if (mm != null && map.containsKey(system + "|" + mm)) 692 return map.get(system + "|" + mm).getResource(); 693 694 } 695 return null; 696 } 697 } 698 699 public List<T> getForUrl(String url) { 700 List<T> res = new ArrayList<>(); 701 List<CanonicalResourceManager<T>.CachedCanonicalResource<T>> list = listForUrl.get(url); 702 if (list != null) { 703 for (CanonicalResourceManager<T>.CachedCanonicalResource<T> t : list) { 704 res.add(t.getResource()); 705 } 706 } 707 return res; 708 } 709 710 /** 711 * This is asking for a packaged version aware resolution 712 * 713 * if we can resolve the reference in the package dependencies, we will. if we can't 714 * then we fall back to the non-package approach 715 * 716 * The context has to prepare the pvlist based on the original package 717 * @param url 718 * @param srcInfo 719 * @return 720 */ 721 public T getByPackage(String url, List<String> pvlist) { 722 for (String pv : pvlist) { 723 if (map.containsKey(pv+":"+url)) { 724 return map.get(pv+":"+url).getResource(); 725 } 726 } 727 return map.containsKey(url) ? map.get(url).getResource() : null; 728 } 729 730 public T getByPackage(String system, String version, List<String> pvlist) { 731 if (version == null) { 732 return getByPackage(system, pvlist); 733 } else { 734 for (String pv : pvlist) { 735 if (map.containsKey(pv+":"+system+"|"+version)) 736 return map.get(pv+":"+system+"|"+version).getResource(); 737 } 738 String mm = VersionUtilities.getMajMin(version); 739 if (mm != null && map.containsKey(system+"|"+mm)) 740 for (String pv : pvlist) { 741 if (map.containsKey(pv+":"+system+"|"+mm)) 742 return map.get(pv+":"+system+"|"+mm).getResource(); 743 } 744 745 if (map.containsKey(system+"|"+version)) 746 return map.get(system+"|"+version).getResource(); 747 if (mm != null && map.containsKey(system+"|"+mm)) 748 return map.get(system+"|"+mm).getResource(); 749 else 750 return null; 751 } 752 } 753 754 755 756 public PackageInformation getPackageInfo(String system, String version) { 757 if (version == null) { 758 return map.containsKey(system) ? map.get(system).getPackageInfo() : null; 759 } else { 760 if (map.containsKey(system+"|"+version)) 761 return map.get(system+"|"+version).getPackageInfo(); 762 String mm = VersionUtilities.getMajMin(version); 763 if (mm != null && map.containsKey(system+"|"+mm)) 764 return map.get(system+"|"+mm).getPackageInfo(); 765 else 766 return null; 767 } 768 } 769 770 771 772 773 public int size() { 774 return list.size(); 775 } 776 777 778 779 public void listAll(List<T> result) { 780 for (CachedCanonicalResource<T> t : list) { 781 result.add(t.getResource()); 782 } 783 } 784 785 public void listAllM(List<CanonicalResource> result) { 786 for (CachedCanonicalResource<T> t : list) { 787 result.add(t.getResource()); 788 } 789 } 790 791 public List<T> getSupplements(T cr) { 792 if (cr == null) { 793 return new ArrayList<T>(); 794 } 795 if (cr.hasSourcePackage()) { 796 List<String> pvl = new ArrayList<>(); 797 pvl.add(cr.getSourcePackage().getVID()); 798 return getSupplements(cr.getUrl(), cr.getVersion(), pvl); 799 } else { 800 return getSupplements(cr.getUrl(), cr.getVersion(), null); 801 } 802 } 803 804 public List<T> getSupplements(String url) { 805 return getSupplements(url, null, null); 806 } 807 808 public List<T> getSupplements(String url, String version) { 809 return getSupplements(url, version, null); 810 } 811 812 public List<T> getSupplements(String url, String version, List<String> pvlist) { 813 boolean possibleMatches = false; 814 List<T> res = new ArrayList<>(); 815 if (version != null) { 816 List<CanonicalResourceManager<T>.CachedCanonicalResource<T>> list = supplements.get(url+"|"+version); 817 if (list != null) { 818 for (CanonicalResourceManager<T>.CachedCanonicalResource<T> t : list) { 819 possibleMatches = true; 820 if (pvlist == null || pvlist.contains(t.getPackageInfo().getVID())) { 821 res.add(t.getResource()); 822 } 823 } 824 } 825 } 826 List<CanonicalResourceManager<T>.CachedCanonicalResource<T>> list = supplements.get(url); 827 if (list != null) { 828 for (CanonicalResourceManager<T>.CachedCanonicalResource<T> t : list) { 829 possibleMatches = true; 830 if (pvlist == null || t.getPackageInfo() == null || pvlist.contains(t.getPackageInfo().getVID())) { 831 res.add(t.getResource()); 832 } 833 } 834 } 835 if (res.isEmpty() && pvlist != null && possibleMatches) { 836 return getSupplements(url, version, null); 837 } else { 838 return res; 839 } 840 } 841 842 public void clear() { 843 list.clear(); 844 map.clear(); 845 846 } 847 848 public List<CachedCanonicalResource<T>> getCachedList() { 849 return list; 850 } 851 852 public List<T> getList() { 853 List<T> res = new ArrayList<>(); 854 for (CachedCanonicalResource<T> t : list) { 855 if (!res.contains(t.getResource())) { 856 res.add(t.getResource()); 857 } 858 } 859 return res; 860 } 861 862 public List<T> getVersionList(String url) { 863 List<T> res = new ArrayList<>(); 864 for (CachedCanonicalResource<T> t : list) { 865 if (url.equals(t.getUrl())) { 866 if (!res.contains(t.getResource())) { 867 res.add(t.getResource()); 868 } 869 870 } 871 } 872 return res; 873 } 874 875 public List<T> getSortedList() { 876 List<T> res = getList(); 877 Collections.sort(res, new CanonicalListSorter()); 878 return res; 879 } 880 881 public Set<String> keys() { 882 return map.keySet(); 883 } 884 885 public boolean isEnforceUniqueId() { 886 return enforceUniqueId; 887 } 888 889 890 public void unload() { 891 for (CachedCanonicalResource<T> t : list) { 892 t.unload(); 893 } 894 895 } 896 897 898}