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