001package org.hl7.fhir.utilities.npm;
002
003import java.io.File;
004import java.io.IOException;
005import java.text.ParseException;
006import java.time.Instant;
007import java.util.ArrayList;
008import java.util.List;
009
010import org.hl7.fhir.utilities.FhirPublication;
011import org.hl7.fhir.utilities.TextFile;
012import org.hl7.fhir.utilities.Utilities;
013import org.hl7.fhir.utilities.json.JsonException;
014import org.hl7.fhir.utilities.json.model.JsonArray;
015import org.hl7.fhir.utilities.json.model.JsonObject;
016import org.hl7.fhir.utilities.json.parser.JsonParser;
017import org.hl7.fhir.utilities.npm.PackageList.PackageListEntry;
018
019public class PackageList {
020
021  public static class PackageListEntry {
022    private JsonObject json;
023
024    private PackageListEntry(JsonObject json) {
025      super();
026      this.json = json;
027    }
028    
029    public String version() {
030      return json.asString("version");
031    }
032
033    public String path() {
034      return json.asString("path");
035    }
036
037    public boolean current() {
038      return json.has("current") && json.asBoolean("current");
039    }
040
041    public boolean cibuild() {
042      return "ci-build".equals(json.asString("status"));
043    }
044
045    public String sequence() {
046      return json.asString("sequence");
047   }
048
049    public String fhirVersion() {
050      if (json.has("fhir-version")) // legacy
051        return json.asString("fhir-version");
052      return json.asString("fhirversion");
053    }
054
055    public String status() {
056      return json.asString("status");
057    }
058
059    public String desc() {
060      return json.asString("desc");
061    }
062
063    public String date() {
064      return json.asString("date");
065    }
066
067    public void setDate(String date) {
068      json.set("date", date);      
069    }
070
071    public boolean isPartofMainSpec() {
072      return Utilities.startsWithInList(path(), "http://hl7.org/fhir/DSTU2", "http://hl7.org/fhir/2015Sep", "http://hl7.org/fhir/2015May");
073    }
074    
075    /**
076     * For a variety of reasons in the past, the path is not always under the canonical
077     * this method determines the path *if it exists*
078     * 
079     * @param root
080     * @param v
081     * @throws IOException 
082     */
083    public String determineLocalPath(String url, String root) throws IOException {
084      if (!isPartofMainSpec() && path().startsWith(url+"/")) {
085        String tail = path().substring(url.length()+1);
086        return Utilities.path(root, tail);
087      } else {
088        return null;
089      }
090    }
091    
092    public void setCurrent(boolean b) {
093      if (b) {
094        json.set("current", true);
095      } else {
096        if (json.has("current")) {
097          json.remove("current");
098        }
099      }
100    }
101
102    public void init(String version, String path, String status, String sequence, FhirPublication fhirVersion) {
103      json.set("version", version);      
104      json.set("path", path);
105      json.set("status", status);
106      json.set("sequence", sequence);
107      if (fhirVersion != null) {
108        json.set("fhirversion", fhirVersion.toCode());
109      }
110    }
111    
112    public void describe(String desc, String descMD, String changes) {
113      if (!Utilities.noString(desc)) {
114        json.set("desc", desc);
115      }
116      if (!Utilities.noString(descMD)) {
117        json.set("descmd", descMD);
118      }
119      if (!Utilities.noString(changes)) {
120        json.set("changes", changes);
121      }
122    }
123
124    public JsonObject json() {
125      return json;
126    }
127
128    public Instant instant() throws ParseException {
129      return json.asInstant("date");
130    }
131
132    /**
133     * only used for a technical correction. tcPath is the name of archive file (web reference) containing the content before correction
134     * 
135     * @param asString
136     * @param webpath
137     * @param asString2
138     * @param asString3
139     * @param fhirVersion
140     * @param tcName
141     */
142    public void update(String version, String path, String status, String sequence, FhirPublication fhirVersion, String tcPath, String date) {
143      JsonObject tc = new JsonObject();
144      json.forceArray("corrections").add(tc);
145      tc.set("version", json.asString("version"));
146      tc.set("path", tcPath);
147      tc.set("date", date);
148
149      json.set("version", version);      
150      json.set("path", path);
151      json.set("status", status);
152      json.set("sequence", sequence);
153      if (fhirVersion != null) {
154        json.set("fhirversion", fhirVersion.toCode());
155      }
156      setDate(date);
157    }
158  }
159  
160  private String source;
161  private JsonObject json;
162  private List<PackageListEntry> list = new ArrayList<>();
163  private List<PackageListEntry> versions = new ArrayList<>();
164  private PackageListEntry cibuild;
165  
166  public PackageList() {
167    super();
168    json = new JsonObject();
169    json.add("list", new JsonArray());
170  }
171
172  public PackageList(JsonObject json) {
173    this.json = json;
174    for (JsonObject o : json.getJsonObjects("list")) {
175      PackageListEntry e = new PackageListEntry(o);
176      list.add(e);
177      if ("current".equals(o.asString("version"))) {
178        cibuild = e;
179      } else {
180        versions.add(e);
181      }
182    }
183  }
184
185  public static PackageList fromFile(File f) throws JsonException, IOException {
186    return new PackageList(JsonParser.parseObject(f)).setSource(f.getAbsolutePath());
187  }
188  
189  public PackageList setSource(String src) {
190    this.source = src;
191    return this;
192  }
193
194  public static PackageList fromFile(String f) throws JsonException, IOException {
195    return new PackageList(JsonParser.parseObjectFromFile(f)).setSource(f);    
196  }
197  
198  public static PackageList fromContent(byte[] cnt) throws JsonException, IOException {
199    return new PackageList(JsonParser.parseObject(cnt)).setSource("unknown");
200  }
201  
202  public static PackageList fromUrl(String url) throws JsonException, IOException {
203    return new PackageList(JsonParser.parseObjectFromUrl(url)).setSource(url);
204  }
205  
206  public String source() {
207    return source;
208  }
209
210  public String canonical() {
211    return json.asString("canonical");
212  }
213
214  public String pid() {
215    return json.asString("package-id");
216  }
217  
218  public boolean hasPid() {
219    return json.has("package-id");
220  }
221
222  public String category() {
223    return json.asString("category");
224  }
225  
226  public List<PackageListEntry> list() {
227    return list;
228  }
229  
230  public List<PackageListEntry> versions() {
231    return versions;
232  }
233
234  public PackageListEntry ciBuild() {
235    return cibuild;
236  }
237
238  public String toJson() {
239    return JsonParser.compose(json, true);
240  }
241
242  public PackageListEntry newVersion(String version, String path, String status, String sequence, FhirPublication fhirVersion) {
243    JsonObject o = new JsonObject();
244    PackageListEntry e = new PackageListEntry(o);
245    versions.add(0, e);
246    if (cibuild != null) {
247      json.getJsonArray("list").add(1, o);
248      list.add(1, e);
249    } else {
250      json.getJsonArray("list").add(0, o);     
251      list.add(0, e);
252    }
253    e.init(version, path, status, sequence, fhirVersion);
254    return e;
255  }
256
257  public void init(String name, String canonical, String title, String category, String introduction) {
258    json.add("package-id", name);
259    json.add("canonical", canonical);
260    json.add("title", title);
261    json.add("category", category);
262    json.add("introduction", introduction);
263
264  }
265
266  public void addCIBuild(String version, String path, String desc, String status) {
267    if (cibuild != null) {
268      json.getJsonArray("list").remove(cibuild.json);
269    }
270    cibuild = new PackageListEntry(new JsonObject());
271    cibuild.init(version, path, status, status, null);
272    json.getJsonArray("list").add(0, cibuild.json);    
273  }
274
275  public PackageListEntry findByVersion(String version) {
276    for (PackageListEntry p : versions) {
277      if (version.equals(p.version())) {
278        return p;
279      }
280    }
281    return null;
282  }
283
284  public void save(String filepath) throws IOException {
285    TextFile.stringToFile(toJson(), filepath);
286  }
287
288  public String determineLocalPath(String url, String root) throws IOException {
289    if (canonical().startsWith(url+"/")) {
290      String tail = canonical().substring(url.length()+1);
291      return Utilities.path(root, tail);
292    } else {
293      return null;
294    }
295  }
296
297  public String title() {
298    return json.asString("title");
299  }
300
301  public PackageListEntry current() {
302    for (PackageListEntry e : list) {
303      if (e.current() && !"ci-build".equals(e.status())) {
304        return e;
305      }
306    }
307    return null;
308  }
309
310  public String intro() {
311    return json.asString("introduction");
312  }
313}