001package org.hl7.fhir.convertors.misc;
002
003import java.io.File;
004import java.io.FileInputStream;
005import java.io.FileNotFoundException;
006import java.io.IOException;
007import java.util.Date;
008import java.util.HashSet;
009import java.util.Set;
010
011import javax.xml.parsers.DocumentBuilder;
012import javax.xml.parsers.DocumentBuilderFactory;
013import javax.xml.parsers.ParserConfigurationException;
014
015import org.hl7.fhir.r4.formats.IParser.OutputStyle;
016import org.hl7.fhir.r4.formats.JsonParser;
017import org.hl7.fhir.r4.model.CodeSystem;
018import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
019import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
020import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
021import org.hl7.fhir.r4.model.Identifier;
022import org.hl7.fhir.r4.model.ValueSet;
023import org.hl7.fhir.r4.utils.NPMPackageGenerator;
024import org.hl7.fhir.r4.utils.NPMPackageGenerator.Category;
025import org.hl7.fhir.utilities.FileUtilities;
026import org.hl7.fhir.utilities.Utilities;
027import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
028import org.hl7.fhir.utilities.xml.XMLUtil;
029import org.w3c.dom.Document;
030import org.w3c.dom.Element;
031import org.xml.sax.SAXException;
032
033import com.google.gson.JsonArray;
034import com.google.gson.JsonObject;
035
036@SuppressWarnings("checkstyle:systemout")
037public class DicomPackageBuilder {
038  
039  private String version;
040  private String dest;
041  private String source;
042  private String pattern = "fhir.dicom#{version}.tgz";
043
044  public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
045    if (args.length == 0 || args[0].equals("-help")) {
046      System.out.println("Dicom Package Builder");
047      System.out.println("");
048      System.out.println("Before running, use FTP get the latest copy of ftp://medical.nema.org/MEDICAL/Dicom/Resources");
049      System.out.println("Then run with the following parameters (in order):");
050      System.out.println("1. the local folder that contains the copy of the FTP folder");
051      System.out.println("2. the local folder to put the package when it's created");
052      System.out.println("");
053      System.out.println("For help, see ");
054      return;      
055    }
056    String source = args[0];
057    String dest = args[1];
058    
059    DicomPackageBuilder self = new DicomPackageBuilder();
060    self.setSource(source);
061    self.setDest(dest);
062    self.setPattern("{version}/package.tgz");
063    self.execute();
064  }
065
066  public void execute() throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
067    CodeSystem cs = buildCodeSystem();
068    String fn = Utilities.path(dest, pattern.replace("{version}", version));
069    FileUtilities.createDirectory(FileUtilities.getDirectoryForFile(fn));
070    
071    NPMPackageGenerator gen = new NPMPackageGenerator(fn, buildPackage());
072    int i = 2;
073    gen.addFile(Category.RESOURCE, "CodeSystem-"+cs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(cs));
074    ValueSet vs = buildAllValueSet();
075    gen.addFile(Category.RESOURCE, "ValueSet-"+vs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(vs));
076    Set<String> ids = new HashSet<>();
077    ids.add(vs.getId());
078    
079    for (File f : ManagedFileAccess.file(Utilities.path(source, "valuesets", "fhir", "json")).listFiles()) {
080      vs = (ValueSet) JsonParser.loadFile(ManagedFileAccess.inStream(f));
081      vs.setVersion(version);
082      if (vs.getId().length() > 64) {
083        vs.setId(vs.getId().substring(0, 64));
084      }
085      if (ids.contains(vs.getId())) {
086        throw new Error("Duplicate Id (note Ids cut off at 64 char): "+vs.getId());
087      }
088      ids.add(vs.getId());
089      gen.addFile(Category.RESOURCE, "ValueSet-"+vs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(vs));
090      i++;
091    }
092    gen.finish();
093    System.out.println("Finished - "+i+" resources");
094    
095  }
096
097  private ValueSet buildAllValueSet() {
098    ValueSet vs = new ValueSet();
099    vs.setId("all");
100    vs.setUrl("http://dicom.nema.org/resources/ValueSet/all");
101    vs.setVersion(version);
102    vs.setName("AllDICOMTerminologyDefinitions");
103    vs.setTitle("All DICOM Controlled Terminology Definitions");
104    vs.setStatus(PublicationStatus.ACTIVE);
105    vs.setExperimental(false);
106    vs.setPublisher("FHIR Project on behalf of DICOM");
107    vs.setDate(new Date());
108    vs.setDescription("All DICOM Code Definitions (Coding Scheme Designator \"DCM\" Coding Scheme Version "+version);
109    vs.setPurpose("This value set is published as part of FHIR in order to make the codes available to FHIR terminology services and so implementers can easily leverage the codes");
110    vs.setCopyright("These codes are excerpted from Digital Imaging and Communications in Medicine (DICOM) Standard, Part 16: Content Mapping Resource, Copyright © 2011 by the National Electrical Manufacturers Association.");
111
112    vs.getCompose().getIncludeFirstRep().setSystem("http://dicom.nema.org/resources/ontology/DCM");
113    return vs;
114  }
115
116  private JsonObject buildPackage() {
117    JsonObject npm = new JsonObject();
118    npm.addProperty("tools-version", 3);
119    npm.addProperty("type", "Conformance");
120    npm.addProperty("license", "free");
121    npm.addProperty("author", "FHIR Project for DICOM");
122    npm.addProperty("name", "fhir.dicom");
123    npm.addProperty("url", "http://fhir.org/packages/fhir.dicom");
124    npm.addProperty("canonical", "http://fhir.org/packages/fhir.dicom");
125    npm.addProperty("version", version);
126    JsonArray fv = new JsonArray();
127    npm.add("fhirVersions", fv);
128    fv.add("4.0.1");
129    JsonObject dep = new JsonObject();
130    npm.add("dependencies", dep);
131    dep.addProperty("hl7.fhir.r4.core", "4.0.1");
132    return npm;
133  }
134
135  private CodeSystem buildCodeSystem() throws ParserConfigurationException, FileNotFoundException, SAXException, IOException {
136    DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory();
137    factory.setNamespaceAware(true);
138    DocumentBuilder builder = factory.newDocumentBuilder();
139    Document doc = builder.parse(ManagedFileAccess.inStream(Utilities.path(source, "Ontology", "DCM", "dcm.owl")));
140    Element rdf = doc.getDocumentElement();
141    Element d = XMLUtil.getFirstChild(rdf);
142    version = processVersion(XMLUtil.getNamedChildText(d, "versionInfo"));
143    
144    CodeSystem cs = new CodeSystem();
145    cs.setId("DCM");
146    cs.setUrl("http://dicom.nema.org/resources/ontology/DCM");
147    cs.setVersion(version);
148    cs.setName("DICOMTerminologyDefinitions");
149    cs.setTitle("DICOM Controlled Terminology Definitions");
150    cs.getIdentifier().add(new Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:1.2.840.10008.2.16.4"));
151    cs.setStatus(PublicationStatus.ACTIVE);
152    cs.setExperimental(false);
153    cs.setPublisher("FHIR Project on behalf of DICOM");
154    cs.setDate(new Date());
155    cs.setDescription("DICOM Code Definitions (Coding Scheme Designator \"DCM\" Coding Scheme Version "+version);
156    cs.setPurpose("This code system is published as part of FHIR in order to make the codes available to FHIR terminology services and so implementers can easily leverage the codes");
157    cs.setCopyright("These codes are excerpted from Digital Imaging and Communications in Medicine (DICOM) Standard, Part 16: Content Mapping Resource, Copyright © 2011 by the National Electrical Manufacturers Association.");
158    cs.setCaseSensitive(true);
159    cs.setValueSet("http://dicom.nema.org/resources/ValueSet/all");
160    cs.setContent(CodeSystemContentMode.COMPLETE);
161    
162    d = XMLUtil.getNextSibling(d);
163    while (d != null) {
164      String code = XMLUtil.getNamedChildText(d, "notation");
165      String display = XMLUtil.getNamedChildText(d, "prefLabel");
166      String definition = XMLUtil.getNamedChildText(d, "definition");
167      ConceptDefinitionComponent cc = new ConceptDefinitionComponent();
168      cs.getConcept().add(cc);
169      cc.setCode(code);
170      cc.setDisplay(display);
171      cc.setDefinition(definition);
172      d = XMLUtil.getNextSibling(d);
173    }
174    return cs;
175  }
176
177  private String processVersion(String s) {
178    String[] p = s.split("\\_");
179    return p[0].substring(0, 4)+"."+(1+(p[0].charAt(4) - 'a'))+"."+p[1];
180  }
181
182  public void setDest(String dest) {
183    this.dest = dest;    
184  }
185
186  public void setSource(String source) {
187    this.source = source;
188  }
189
190  public void setPattern(String pattern) {
191    this.pattern  = pattern;
192    
193  }
194}