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.CodeSystem;
013import org.hl7.fhir.r4.model.MetadataResource;
014import org.hl7.fhir.utilities.VersionUtilities;
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
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
050  private boolean enforceUniqueId; 
051  private List<T> list = new ArrayList<>();
052  private Map<String, T> map = new HashMap<>();
053  
054  
055  public CanonicalResourceManager(boolean enforceUniqueId) {
056    super();
057    this.enforceUniqueId = enforceUniqueId;
058  }
059
060  public void copy(CanonicalResourceManager<T> source) {
061    list.clear();
062    map.clear();
063    list.addAll(source.list);
064    map.putAll(source.map);
065  }
066  
067  public void see(T r) {
068    if (!r.hasId()) {
069      r.setId(UUID.randomUUID().toString());
070    }
071    if (enforceUniqueId && map.containsKey(r.getId())) {
072      drop(r.getId());      
073    }
074    list.add(r);
075    map.put(r.getId(), r); // we do this so we can drop by id
076
077    if (r.hasUrl()) {
078      // first, this is the correct reosurce for this version (if it has a version)
079      if (r.hasVersion()) {
080        map.put(r.getUrl()+"|"+r.getVersion(), r);
081      }
082      updateList(r.getUrl(), r.getVersion());
083    }
084  }
085
086  private void updateList(String url, String version) {
087    List<T> rl = new ArrayList<T>();
088    for (T t : list) {
089      if (url.equals(t.getUrl()) && !rl.contains(t)) {
090        rl.add(t);
091      }
092    }
093    if (rl.size() > 0) {
094      // sort by version as much as we are able
095      Collections.sort(rl, new MetadataResourceVersionComparator<T>());
096      // the current is the latest
097      map.put(url, rl.get(rl.size()-1));
098      // now, also, the latest for major/minor
099      if (version != null) {
100        T latest = null;
101        for (T t : rl) {
102          if (VersionUtilities.versionsCompatible(t.getVersion(), version)) {
103            latest = t;
104          }
105        }
106        if (latest != null) { // might be null if it's not using semver
107          String lv = VersionUtilities.getMajMin(latest.getVersion());
108          if (lv != null && !lv.equals(version))
109            map.put(url+"|"+lv, rl.get(rl.size()-1));
110        }
111      }
112    }
113  }
114 
115
116  public T get(String url) {
117    return map.get(url);
118  }
119  
120  public boolean has(String url) {
121    return map.containsKey(url);
122  }
123  
124  public T get(String system, String version) {
125    if (map.containsKey(system+"|"+version))
126      return map.get(system+"|"+version);
127    String mm = VersionUtilities.getMajMin(version);
128    if (mm != null)
129      return map.get(system+"|"+mm);
130    else
131      return null;
132  }
133  
134  public boolean has(String system, String version) {
135    if (map.containsKey(system+"|"+version))
136      return true;
137    String mm = VersionUtilities.getMajMin(version);
138    if (mm != null)
139      return map.containsKey(system+"|"+mm);
140    else
141      return false;
142  }
143  
144  public int size() {
145    return list.size();
146  }
147  
148  public void drop(String id) {
149    T res = null;
150    do {
151      res = null;
152      for (T t : list) {
153        if (t.getId().equals(id)) {
154          res = t;
155        }
156      }
157      if (res != null) {
158        list.remove(res);
159        map.remove(id);
160        map.remove(res.getUrl());
161        if (res.hasVersion()) {
162          map.remove(res.getUrl()+"|"+res.getVersion());
163          String mm = VersionUtilities.getMajMin(res.getVersion());
164          if (mm != null) {
165            map.remove(res.getUrl()+"|"+mm);
166          }
167        }
168        updateList(res.getUrl(), res.getVersion()); 
169      }
170    } while (res != null);
171  }
172  
173  
174  public void listAll(List<T> result) {
175    result.addAll(list);    
176  }
177
178  public void listAllM(List<MetadataResource> result) {
179    result.addAll(list);    
180  }
181
182  public void clear() {
183    list.clear();
184    map.clear();
185    
186  }
187
188  public List<T> getList() {
189    List<T> res = new ArrayList<>();
190    for (T t : list) {
191      if (!res.contains(t)) {
192        res.add(t);
193      }
194    }
195    return res;
196  }
197
198  public Set<String> keys() {
199    return map.keySet();
200  }
201
202  public boolean isEnforceUniqueId() {
203    return enforceUniqueId;
204  }
205  
206}