001package org.hl7.fhir.r4.context; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009import java.util.Set; 010import java.util.UUID; 011 012import org.hl7.fhir.r4.model.MetadataResource; 013import org.hl7.fhir.utilities.VersionUtilities; 014 015/** 016 * This manages a cached list of resources, and provides high speed access by 017 * 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 024public class CanonicalResourceManager<T extends MetadataResource> { 025 026 public class MetadataResourceVersionComparator<T extends MetadataResource> implements Comparator<T> { 027 @Override 028 public int compare(T arg1, T arg2) { 029 String v1 = arg1.getVersion(); 030 String v2 = arg2.getVersion(); 031 if (v1 == null && v2 == null) { 032 return Integer.compare(list.indexOf(arg1), list.indexOf(arg2)); // retain original order 033 } else if (v1 == null) { 034 return -1; 035 } else if (v2 == null) { 036 return 1; 037 } else { 038 String mm1 = VersionUtilities.getMajMin(v1); 039 String mm2 = VersionUtilities.getMajMin(v2); 040 if (mm1 == null || mm2 == null) { 041 return v1.compareTo(v2); 042 } else { 043 return mm1.compareTo(mm2); 044 } 045 } 046 } 047 } 048 049 private boolean enforceUniqueId; 050 private List<T> list = new ArrayList<>(); 051 private Map<String, T> map = new HashMap<>(); 052 053 public CanonicalResourceManager(boolean enforceUniqueId) { 054 super(); 055 this.enforceUniqueId = enforceUniqueId; 056 } 057 058 public void copy(CanonicalResourceManager<T> source) { 059 list.clear(); 060 map.clear(); 061 list.addAll(source.list); 062 map.putAll(source.map); 063 } 064 065 public void see(T r) { 066 if (!r.hasId()) { 067 r.setId(UUID.randomUUID().toString()); 068 } 069 if (enforceUniqueId && map.containsKey(r.getId())) { 070 drop(r.getId()); 071 } 072 list.add(r); 073 map.put(r.getId(), r); // we do this so we can drop by id 074 075 if (r.hasUrl()) { 076 // first, this is the correct reosurce for this version (if it has a version) 077 if (r.hasVersion()) { 078 map.put(r.getUrl() + "|" + r.getVersion(), r); 079 } 080 updateList(r.getUrl(), r.getVersion()); 081 } 082 } 083 084 private void updateList(String url, String version) { 085 List<T> rl = new ArrayList<T>(); 086 for (T t : list) { 087 if (url.equals(t.getUrl()) && !rl.contains(t)) { 088 rl.add(t); 089 } 090 } 091 if (rl.size() > 0) { 092 // sort by version as much as we are able 093 Collections.sort(rl, new MetadataResourceVersionComparator<T>()); 094 // the current is the latest 095 map.put(url, rl.get(rl.size() - 1)); 096 // now, also, the latest for major/minor 097 if (version != null) { 098 T latest = null; 099 for (T t : rl) { 100 if (VersionUtilities.versionsCompatible(t.getVersion(), version)) { 101 latest = t; 102 } 103 } 104 if (latest != null) { // might be null if it's not using semver 105 String lv = VersionUtilities.getMajMin(latest.getVersion()); 106 if (lv != null && !lv.equals(version)) 107 map.put(url + "|" + lv, rl.get(rl.size() - 1)); 108 } 109 } 110 } 111 } 112 113 public T get(String url) { 114 return map.get(url); 115 } 116 117 public boolean has(String url) { 118 return map.containsKey(url); 119 } 120 121 public T get(String system, String version) { 122 if (map.containsKey(system + "|" + version)) 123 return map.get(system + "|" + version); 124 String mm = VersionUtilities.getMajMin(version); 125 if (mm != null) 126 return map.get(system + "|" + mm); 127 else 128 return null; 129 } 130 131 public boolean has(String system, String version) { 132 if (map.containsKey(system + "|" + version)) 133 return true; 134 String mm = VersionUtilities.getMajMin(version); 135 if (mm != null) 136 return map.containsKey(system + "|" + mm); 137 else 138 return false; 139 } 140 141 public int size() { 142 return list.size(); 143 } 144 145 public void drop(String id) { 146 T res = null; 147 do { 148 res = null; 149 for (T t : list) { 150 if (t.getId().equals(id)) { 151 res = t; 152 } 153 } 154 if (res != null) { 155 list.remove(res); 156 map.remove(id); 157 map.remove(res.getUrl()); 158 if (res.hasVersion()) { 159 map.remove(res.getUrl() + "|" + res.getVersion()); 160 String mm = VersionUtilities.getMajMin(res.getVersion()); 161 if (mm != null) { 162 map.remove(res.getUrl() + "|" + mm); 163 } 164 } 165 updateList(res.getUrl(), res.getVersion()); 166 } 167 } while (res != null); 168 } 169 170 public void listAll(List<T> result) { 171 result.addAll(list); 172 } 173 174 public void listAllM(List<MetadataResource> result) { 175 result.addAll(list); 176 } 177 178 public void clear() { 179 list.clear(); 180 map.clear(); 181 182 } 183 184 public List<T> getList() { 185 List<T> res = new ArrayList<>(); 186 for (T t : list) { 187 if (!res.contains(t)) { 188 res.add(t); 189 } 190 } 191 return res; 192 } 193 194 public Set<String> keys() { 195 return map.keySet(); 196 } 197 198 public boolean isEnforceUniqueId() { 199 return enforceUniqueId; 200 } 201 202}