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