001package org.hl7.fhir.convertors.misc;
002
003import java.io.BufferedOutputStream;
004import java.io.ByteArrayOutputStream;
005import java.io.File;
006import java.io.FileInputStream;
007import java.io.IOException;
008import java.io.InputStream;
009import java.nio.charset.StandardCharsets;
010import java.util.ArrayList;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Map;
014import java.util.UUID;
015import java.util.Map.Entry;
016
017import javax.annotation.Nonnull;
018
019import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
020import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
021import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
022import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
023import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
024import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40;
025import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50;
026import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_30;
027import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_40;
028import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
029import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_30;
030import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_40;
031import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50;
032import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
033import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
034import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
035import org.hl7.fhir.r5.model.Enumeration;
036import org.hl7.fhir.r5.model.Enumerations.FHIRVersion;
037import org.hl7.fhir.r5.model.Enumerations.FHIRVersionEnumFactory;
038import org.hl7.fhir.r5.model.ImplementationGuide;
039import org.hl7.fhir.r5.model.Resource;
040import org.hl7.fhir.r5.model.StructureDefinition;
041import org.hl7.fhir.utilities.TextFile;
042import org.hl7.fhir.utilities.Utilities;
043import org.hl7.fhir.utilities.VersionUtilities;
044import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
045import org.hl7.fhir.utilities.json.model.JsonArray;
046import org.hl7.fhir.utilities.json.model.JsonObject;
047import org.hl7.fhir.utilities.json.parser.JsonParser;
048import org.hl7.fhir.utilities.npm.NpmPackageIndexBuilder;
049
050public class NpmPackageVersionConverter {
051
052  private static final int BUFFER_SIZE = 1024;
053
054  private final String source;
055  private final String dest;
056  private final String version;
057  private final String vCode;
058  private final List<String> errors = new ArrayList<>();
059  private String currentVersion;
060
061  private String packageId;
062
063  public NpmPackageVersionConverter(String source, String dest, String version, String packageId) {
064    super();
065    this.source = source;
066    this.dest = dest;
067    this.vCode = version;
068    this.packageId = packageId;
069    this.version = VersionUtilities.versionFromCode(version);
070  }
071
072  public static void main(String[] args) throws IOException {
073    NpmPackageVersionConverter self = new NpmPackageVersionConverter(args[0], args[1], args[2], args[3]);
074    self.execute();
075    System.out.println("Finished");
076    for (String s : self.errors) {
077      System.out.println(s);
078    }
079  }
080
081  public List<String> getErrors() {
082    return errors;
083  }
084
085  public void execute() throws IOException {
086    Map<String, byte[]> content = loadContentMap(ManagedFileAccess.inStream(source));
087
088    Map<String, byte[]> output = new HashMap<>();
089    output.put("package/package.json", convertPackage(content.get("package/package.json")));
090    output.put("package/other/spec.internals", convertSpec(content.get("package/other/spec.internals")));
091
092    for (Entry<String, byte[]> e : content.entrySet()) {
093      if (!e.getKey().equals("package/package.json") && !e.getKey().equals("package/other/spec.internals") && !e.getKey().endsWith("ig-r4.json") && !e.getKey().endsWith("ig-r4.jsonX")) {
094        byte[] cnv = e.getValue();
095        try {
096          JsonObject json = JsonParser.parseObject(e.getValue());
097          if (json.has("resourceType")) {
098            cnv = convertResource(e.getKey(), e.getValue());
099          }
100        } catch (Exception ex) {
101        }
102        if (cnv != null && cnv.length > 0) {
103          output.put(e.getKey(), cnv);
104        }
105      }
106    }
107
108    TarArchiveOutputStream tar;
109    ByteArrayOutputStream OutputStream;
110    BufferedOutputStream bufferedOutputStream;
111    GzipCompressorOutputStream gzipOutputStream;
112
113    OutputStream = new ByteArrayOutputStream();
114    bufferedOutputStream = new BufferedOutputStream(OutputStream);
115    gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
116    tar = new TarArchiveOutputStream(gzipOutputStream);
117
118
119    Map<String, NpmPackageIndexBuilder> indexers = new HashMap<>();
120    for (Entry<String, byte[]> e : output.entrySet()) {
121      String n = e.getKey().substring(0, e.getKey().lastIndexOf("/"));
122      String s = e.getKey().substring(n.length() + 1);
123      byte[] b = e.getValue();
124      NpmPackageIndexBuilder indexer = indexers.get(n);
125      if (indexer == null) {
126        indexer = new NpmPackageIndexBuilder();
127        indexer.start(Utilities.path("[tmp]", "tmp-"+UUID.randomUUID().toString()+".db"));
128        indexers.put(n, indexer);
129      }
130      indexer.seeFile(s, b);
131      if (!s.equals(".index.json") && !s.equals("package.json") && !s.equals(".index.db")) {
132        TarArchiveEntry entry = new TarArchiveEntry(e.getKey());
133        entry.setSize(b.length);
134        tar.putArchiveEntry(entry);
135        tar.write(b);
136        tar.closeArchiveEntry();
137      }
138    }
139    for (Entry<String, NpmPackageIndexBuilder> e : indexers.entrySet()) {
140      byte[] cnt = e.getValue().build().getBytes(StandardCharsets.UTF_8);
141      TarArchiveEntry entry = new TarArchiveEntry(e.getKey() + "/.index.json");
142      entry.setSize(cnt.length);
143      tar.putArchiveEntry(entry);
144      tar.write(cnt);
145      tar.closeArchiveEntry();
146      cnt = TextFile.fileToBytes(e.getValue().getDbFilename());
147      ManagedFileAccess.file(e.getValue().getDbFilename()).delete();
148      entry = new TarArchiveEntry(e.getKey() + "/.index.db");
149      entry.setSize(cnt.length);
150      tar.putArchiveEntry(entry);
151      tar.write(cnt);
152      tar.closeArchiveEntry();
153    }
154
155    byte[] cnt = output.get("package/package.json");
156    TarArchiveEntry entry = new TarArchiveEntry("package/package.json");
157    entry.setSize(cnt.length);
158    tar.putArchiveEntry(entry);
159    tar.write(cnt);
160    tar.closeArchiveEntry();
161
162    tar.finish();
163    tar.close();
164    gzipOutputStream.close();
165    bufferedOutputStream.close();
166    OutputStream.close();
167    byte[] b = OutputStream.toByteArray();
168    TextFile.bytesToFile(b, dest);
169  }
170
171  @Nonnull
172  protected Map<String, byte[]> loadContentMap(InputStream inputStream) throws IOException {
173    GzipCompressorInputStream gzipIn;
174    try {
175      gzipIn = new GzipCompressorInputStream(inputStream);
176    } catch (Exception e) {
177      throw new IOException("Error reading " + source + ": " + e.getMessage(), e);
178    }
179    Map<String, byte[]> content = new HashMap<>();
180
181    try (TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn)) {
182      TarArchiveEntry entry;
183
184      while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) {
185        String n = entry.getName();
186        if (n.contains("..")) {
187          throw new RuntimeException("Entry with an illegal name: " + n);
188        }
189        if (!entry.isDirectory()) {
190          int count;
191          byte[] data = new byte[BUFFER_SIZE];
192          ByteArrayOutputStream fos = new ByteArrayOutputStream();
193          try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
194            while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) {
195              dest.write(data, 0, count);
196            }
197          }
198          fos.close();
199          content.put(n, fos.toByteArray());
200        }
201      }
202    }
203    return content;
204  }
205
206  private byte[] convertPackage(byte[] cnt) throws IOException {
207    JsonObject json = JsonParser.parseObject(cnt);
208    currentVersion = json.getJsonArray("fhirVersions").get(0).asString();
209    String name = json.asString("name");
210    assert(packageId.equals(name + "." + vCode));
211    json.remove("name");
212    json.add("name", name + "." + vCode);
213    json.remove("fhirVersions");
214    json.remove("dependencies");
215    JsonArray fv = new JsonArray();
216    json.add("fhirVersions", fv);
217    fv.add(version);
218    JsonObject dep = new JsonObject();
219    json.add("dependencies", dep);
220    dep.add(VersionUtilities.packageForVersion(version), version);
221    return JsonParser.composeBytes(json);
222  }
223
224  private byte[] convertSpec(byte[] cnt) throws IOException {
225    JsonObject json = JsonParser.parseObject(cnt);
226    json.set("ig-version", version);
227    json.set("npm-name", packageId);
228    return JsonParser.composeBytes(json, true);
229  }
230
231  private byte[] convertResource(String n, byte[] cnt) {
232    try {
233      if (VersionUtilities.isR2Ver(currentVersion)) {
234        org.hl7.fhir.dstu2.model.Resource res = new org.hl7.fhir.dstu2.formats.JsonParser().parse(cnt);
235        convertResourceR2(res);
236        if (VersionUtilities.isR2Ver(version)) {
237          return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(res);
238        } else if (VersionUtilities.isR2BVer(version)) {
239          return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_30.convertResource(VersionConvertorFactory_10_30.convertResource(res)));
240        } else if (VersionUtilities.isR3Ver(version)) {
241          return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_10_30.convertResource(res));
242        } else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
243          return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_10_40.convertResource(res));
244        } else if (VersionUtilities.isR5Plus(version)) {
245          return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_10_50.convertResource(res));
246        }
247      } else if (VersionUtilities.isR2BVer(currentVersion)) {
248        org.hl7.fhir.dstu2016may.model.Resource res = new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(cnt);
249        convertResourceR2B(res);
250        if (VersionUtilities.isR2Ver(version)) {
251          return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(VersionConvertorFactory_10_30.convertResource(VersionConvertorFactory_14_30.convertResource(res)));
252        } else if (VersionUtilities.isR2BVer(version)) {
253          return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(res);
254        } else if (VersionUtilities.isR3Ver(version)) {
255          return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_14_30.convertResource(res));
256        } else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
257          return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_14_40.convertResource(res));
258        } else if (VersionUtilities.isR5Plus(version)) {
259          return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_14_50.convertResource(res));
260        }
261      } else if (VersionUtilities.isR3Ver(currentVersion)) {
262        org.hl7.fhir.dstu3.model.Resource res = new org.hl7.fhir.dstu3.formats.JsonParser().parse(cnt);
263        convertResourceR3(res);
264        if (VersionUtilities.isR2Ver(version)) {
265          return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(VersionConvertorFactory_10_30.convertResource(res));
266        } else if (VersionUtilities.isR2BVer(version)) {
267          return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_30.convertResource(res));
268        } else if (VersionUtilities.isR3Ver(version)) {
269          return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(res);
270        } else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
271          return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_30_40.convertResource(res));
272        } else if (VersionUtilities.isR5Plus(version)) {
273          return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_30_50.convertResource(res));
274        }
275      } else if (VersionUtilities.isR4Ver(currentVersion)) {
276        org.hl7.fhir.r4.model.Resource res = new org.hl7.fhir.r4.formats.JsonParser().parse(cnt);
277        convertResourceR4(res);
278        if (VersionUtilities.isR2Ver(version)) {
279          return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(VersionConvertorFactory_10_40.convertResource(res, new PR2Handler()));
280        } else if (VersionUtilities.isR2BVer(version)) {
281          return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_40.convertResource(res));
282        } else if (VersionUtilities.isR3Ver(version)) {
283          return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_30_40.convertResource(res, new BaseAdvisor_30_40(false)));
284        } else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
285          return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(res);
286        } else if (VersionUtilities.isR5Plus(version)) {
287          return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_40_50.convertResource(res));
288        }
289      } else if (VersionUtilities.isR4BVer(currentVersion)) {
290        org.hl7.fhir.r4.model.Resource res = new org.hl7.fhir.r4.formats.JsonParser().parse(cnt);
291        convertResourceR4B(res);
292        if (VersionUtilities.isR2Ver(version)) {
293          return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(VersionConvertorFactory_10_40.convertResource(res, new PR2Handler()));
294        } else if (VersionUtilities.isR2BVer(version)) {
295          return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_40.convertResource(res));
296        } else if (VersionUtilities.isR3Ver(version)) {
297          return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_30_40.convertResource(res, new BaseAdvisor_30_40(false)));
298        } else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
299          return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(res);
300        } else if (VersionUtilities.isR5Plus(version)) {
301          return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(VersionConvertorFactory_40_50.convertResource(res));
302        }
303      } else if (VersionUtilities.isR5Plus(currentVersion)) {
304        org.hl7.fhir.r5.model.Resource res = new org.hl7.fhir.r5.formats.JsonParser().parse(cnt);
305        convertResourceR5(res);
306        if (VersionUtilities.isR2Ver(version)) {
307          return new org.hl7.fhir.dstu2.formats.JsonParser().composeBytes(VersionConvertorFactory_10_50.convertResource(res));
308        } else if (VersionUtilities.isR2BVer(version)) {
309          return new org.hl7.fhir.dstu2016may.formats.JsonParser().composeBytes(VersionConvertorFactory_14_50.convertResource(res));
310        } else if (VersionUtilities.isR3Ver(version)) {
311          return new org.hl7.fhir.dstu3.formats.JsonParser().composeBytes(VersionConvertorFactory_30_50.convertResource(res, new BaseAdvisor_30_50(false)));
312        } else if (VersionUtilities.isR4Ver(version) || VersionUtilities.isR4BVer(version)) {
313          return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(VersionConvertorFactory_40_50.convertResource(res));
314        } else if (VersionUtilities.isR5Plus(version)) {
315          return new org.hl7.fhir.r5.formats.JsonParser().composeBytes(res);
316        }
317      }
318      throw new Error("Unknown version " + currentVersion + " -> " + version);
319    } catch (Exception ex) {
320      errors.add("Error converting " + n + ": " + ex.getMessage());
321      return null;
322    }
323  }
324
325  private void convertResourceR2(org.hl7.fhir.dstu2.model.Resource res) {
326    if (res instanceof org.hl7.fhir.dstu2.model.ImplementationGuide) {
327      org.hl7.fhir.dstu2.model.ImplementationGuide ig = (org.hl7.fhir.dstu2.model.ImplementationGuide) res;
328      ig.setFhirVersion(version);
329    } 
330    if (res instanceof org.hl7.fhir.dstu2.model.StructureDefinition) {
331      org.hl7.fhir.dstu2.model.StructureDefinition sd = (org.hl7.fhir.dstu2.model.StructureDefinition) res;
332      sd.setFhirVersion(version);
333    }
334  }
335
336  private void convertResourceR2B(org.hl7.fhir.dstu2016may.model.Resource res) {
337    if (res instanceof org.hl7.fhir.dstu2016may.model.ImplementationGuide) {
338      org.hl7.fhir.dstu2016may.model.ImplementationGuide ig = (org.hl7.fhir.dstu2016may.model.ImplementationGuide) res;
339      ig.setFhirVersion(version);
340    } 
341    if (res instanceof org.hl7.fhir.dstu2016may.model.StructureDefinition) {
342      org.hl7.fhir.dstu2016may.model.StructureDefinition sd = (org.hl7.fhir.dstu2016may.model.StructureDefinition) res;
343      sd.setFhirVersion(version);
344    }
345  }
346
347  private void convertResourceR3(org.hl7.fhir.dstu3.model.Resource res) {
348    if (res instanceof org.hl7.fhir.dstu3.model.ImplementationGuide) {
349      org.hl7.fhir.dstu3.model.ImplementationGuide ig = (org.hl7.fhir.dstu3.model.ImplementationGuide) res;
350      ig.setFhirVersion(version);
351    }    
352    if (res instanceof org.hl7.fhir.dstu3.model.StructureDefinition) {
353      org.hl7.fhir.dstu3.model.StructureDefinition sd = (org.hl7.fhir.dstu3.model.StructureDefinition) res;
354      sd.setFhirVersion(version);
355    }
356  }
357
358  private void convertResourceR4(org.hl7.fhir.r4.model.Resource res) {
359    if (res instanceof org.hl7.fhir.r4.model.ImplementationGuide) {
360      org.hl7.fhir.r4.model.ImplementationGuide ig = (org.hl7.fhir.r4.model.ImplementationGuide) res;
361      ig.getFhirVersion().clear();
362      ig.getFhirVersion().add(new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.Enumerations.FHIRVersionEnumFactory(), version));
363      ig.setPackageId(packageId);
364    }
365    if (res instanceof org.hl7.fhir.r4.model.StructureDefinition) {
366      org.hl7.fhir.r4.model.StructureDefinition sd = (org.hl7.fhir.r4.model.StructureDefinition) res;
367      sd.setFhirVersion(org.hl7.fhir.r4.model.Enumerations.FHIRVersion.fromCode(version));
368    }
369  }
370
371  private void convertResourceR4B(org.hl7.fhir.r4.model.Resource res) {
372    if (res instanceof org.hl7.fhir.r4.model.ImplementationGuide) {
373      org.hl7.fhir.r4.model.ImplementationGuide ig = (org.hl7.fhir.r4.model.ImplementationGuide) res;
374      ig.getFhirVersion().clear();
375      ig.getFhirVersion().add(new org.hl7.fhir.r4.model.Enumeration<>(new org.hl7.fhir.r4.model.Enumerations.FHIRVersionEnumFactory(), version));
376      ig.setPackageId(packageId);
377    }
378    if (res instanceof org.hl7.fhir.r4.model.StructureDefinition) {
379      org.hl7.fhir.r4.model.StructureDefinition sd = (org.hl7.fhir.r4.model.StructureDefinition) res;
380      sd.setFhirVersion(org.hl7.fhir.r4.model.Enumerations.FHIRVersion.fromCode(version));
381    }
382  }
383
384
385  private void convertResourceR5(Resource res) {
386    if (res instanceof ImplementationGuide) {
387      ImplementationGuide ig = (ImplementationGuide) res;
388      ig.getFhirVersion().clear();
389      ig.getFhirVersion().add(new Enumeration<>(new FHIRVersionEnumFactory(), version));
390      ig.setPackageId(packageId);
391    }
392    if (res instanceof StructureDefinition) {
393      StructureDefinition sd = (StructureDefinition) res;
394      sd.setFhirVersion(FHIRVersion.fromCode(version));
395    }
396  }
397
398}