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.MarkedToMoveToAdjunctPackage;
014import org.hl7.fhir.utilities.VersionUtilities;
015
016/**
017 * This manages a cached list of resources, and provides high speed access by
018 * 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 MetadataResource> {
027
028  public class MetadataResourceVersionComparator<T extends MetadataResource> implements Comparator<T> {
029    @Override
030    public int compare(T arg1, T arg2) {
031      String v1 = arg1.getVersion();
032      String v2 = arg2.getVersion();
033      if (v1 == null && v2 == null) {
034        return Integer.compare(list.indexOf(arg1), list.indexOf(arg2)); // retain original order
035      } else if (v1 == null) {
036        return -1;
037      } else if (v2 == null) {
038        return 1;
039      } else {
040        String mm1 = VersionUtilities.getMajMin(v1);
041        String mm2 = VersionUtilities.getMajMin(v2);
042        if (mm1 == null || mm2 == null) {
043          return v1.compareTo(v2);
044        } else {
045          return mm1.compareTo(mm2);
046        }
047      }
048    }
049  }
050
051  private boolean enforceUniqueId;
052  private List<T> list = new ArrayList<>();
053  private Map<String, T> map = new HashMap<>();
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  public T get(String url) {
116    return map.get(url);
117  }
118
119  public boolean has(String url) {
120    return map.containsKey(url);
121  }
122
123  public T get(String system, String version) {
124    if (map.containsKey(system + "|" + version))
125      return map.get(system + "|" + version);
126    String mm = VersionUtilities.getMajMin(version);
127    if (mm != null)
128      return map.get(system + "|" + mm);
129    else
130      return null;
131  }
132
133  public boolean has(String system, String version) {
134    if (map.containsKey(system + "|" + version))
135      return true;
136    String mm = VersionUtilities.getMajMin(version);
137    if (mm != null)
138      return map.containsKey(system + "|" + mm);
139    else
140      return false;
141  }
142
143  public int size() {
144    return list.size();
145  }
146
147  public void drop(String id) {
148    T res = null;
149    do {
150      res = null;
151      for (T t : list) {
152        if (t.getId().equals(id)) {
153          res = t;
154        }
155      }
156      if (res != null) {
157        list.remove(res);
158        map.remove(id);
159        map.remove(res.getUrl());
160        if (res.hasVersion()) {
161          map.remove(res.getUrl() + "|" + res.getVersion());
162          String mm = VersionUtilities.getMajMin(res.getVersion());
163          if (mm != null) {
164            map.remove(res.getUrl() + "|" + mm);
165          }
166        }
167        updateList(res.getUrl(), res.getVersion());
168      }
169    } while (res != null);
170  }
171
172  public void listAll(List<T> result) {
173    result.addAll(list);
174  }
175
176  public void listAllM(List<MetadataResource> result) {
177    result.addAll(list);
178  }
179
180  public void clear() {
181    list.clear();
182    map.clear();
183
184  }
185
186  public List<T> getList() {
187    List<T> res = new ArrayList<>();
188    for (T t : list) {
189      if (!res.contains(t)) {
190        res.add(t);
191      }
192    }
193    return res;
194  }
195
196  public Set<String> keys() {
197    return map.keySet();
198  }
199
200  public boolean isEnforceUniqueId() {
201    return enforceUniqueId;
202  }
203
204}