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