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}