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
036public class DicomPackageBuilder {
037  
038  private String version;
039  private String dest;
040  private String source;
041  private String pattern = "fhir.dicom#{version}.tgz";
042
043  public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
044    if (args.length == 0 || args[0].equals("-help")) {
045      System.out.println("Dicom Package Builder");
046      System.out.println("");
047      System.out.println("Before running, use FTP get the latest copy of ftp://medical.nema.org/MEDICAL/Dicom/Resources");
048      System.out.println("Then run with the following parameters (in order):");
049      System.out.println("1. the local folder that contains the copy of the FTP folder");
050      System.out.println("2. the local folder to put the package when it's created");
051      System.out.println("");
052      System.out.println("For help, see ");
053      return;      
054    }
055    String source = args[0];
056    String dest = args[1];
057    
058    DicomPackageBuilder self = new DicomPackageBuilder();
059    self.setSource(source);
060    self.setDest(dest);
061    self.setPattern("{version}/package.tgz");
062    self.execute();
063  }
064
065  public void execute() throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
066    CodeSystem cs = buildCodeSystem();
067    String fn = Utilities.path(dest, pattern.replace("{version}", version));
068    FileUtilities.createDirectory(FileUtilities.getDirectoryForFile(fn));
069    
070    NPMPackageGenerator gen = new NPMPackageGenerator(fn, buildPackage());
071    int i = 2;
072    gen.addFile(Category.RESOURCE, "CodeSystem-"+cs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(cs));
073    ValueSet vs = buildAllValueSet();
074    gen.addFile(Category.RESOURCE, "ValueSet-"+vs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(vs));
075    Set<String> ids = new HashSet<>();
076    ids.add(vs.getId());
077    
078    for (File f : ManagedFileAccess.file(Utilities.path(source, "Resources", "valuesets", "fhir", "json")).listFiles()) {
079      vs = (ValueSet) JsonParser.loadFile(ManagedFileAccess.inStream(f));
080      vs.setVersion(version);
081      if (vs.getId().length() > 64) {
082        vs.setId(vs.getId().substring(0, 64));
083      }
084      if (ids.contains(vs.getId())) {
085        throw new Error("Duplicate Id (note Ids cut off at 64 char): "+vs.getId());
086      }
087      ids.add(vs.getId());
088      gen.addFile(Category.RESOURCE, "ValueSet-"+vs.getId()+".json", new JsonParser().setOutputStyle(OutputStyle.NORMAL).composeBytes(vs));
089      i++;
090    }
091    gen.finish();
092    System.out.println("Finished - "+i+" resources");
093    
094  }
095
096  private ValueSet buildAllValueSet() {
097    ValueSet vs = new ValueSet();
098    vs.setId("all");
099    vs.setUrl("http://dicom.nema.org/resources/ValueSet/all");
100    vs.setVersion(version);
101    vs.setName("AllDICOMTerminologyDefinitions");
102    vs.setTitle("All DICOM Controlled Terminology Definitions");
103    vs.setStatus(PublicationStatus.ACTIVE);
104    vs.setExperimental(false);
105    vs.setPublisher("FHIR Project on behalf of DICOM");
106    vs.setDate(new Date());
107    vs.setDescription("All DICOM Code Definitions (Coding Scheme Designator \"DCM\" Coding Scheme Version "+version);
108    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");
109    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.");
110
111    vs.getCompose().getIncludeFirstRep().setSystem("http://dicom.nema.org/resources/ontology/DCM");
112    return vs;
113  }
114
115  private JsonObject buildPackage() {
116    JsonObject npm = new JsonObject();
117    npm.addProperty("tools-version", 3);
118    npm.addProperty("type", "Conformance");
119    npm.addProperty("license", "free");
120    npm.addProperty("author", "FHIR Project for DICOM");
121    npm.addProperty("name", "fhir.dicom");
122    npm.addProperty("url", "http://fhir.org/packages/fhir.dicom");
123    npm.addProperty("canonical", "http://fhir.org/packages/fhir.dicom");
124    npm.addProperty("version", version);
125    JsonArray fv = new JsonArray();
126    npm.add("fhirVersions", fv);
127    fv.add("4.0.1");
128    JsonObject dep = new JsonObject();
129    npm.add("dependencies", dep);
130    dep.addProperty("hl7.fhir.r4.core", "4.0.1");
131    return npm;
132  }
133
134  private CodeSystem buildCodeSystem() throws ParserConfigurationException, FileNotFoundException, SAXException, IOException {
135    DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory();
136    factory.setNamespaceAware(true);
137    DocumentBuilder builder = factory.newDocumentBuilder();
138    Document doc = builder.parse(ManagedFileAccess.inStream(Utilities.path(source, "Resources", "Ontology", "DCM", "dcm.owl")));
139    Element rdf = doc.getDocumentElement();
140    Element d = XMLUtil.getFirstChild(rdf);
141    version = processVersion(XMLUtil.getNamedChildText(d, "versionInfo"));
142    
143    CodeSystem cs = new CodeSystem();
144    cs.setId("DCM");
145    cs.setUrl("http://dicom.nema.org/resources/ontology/DCM");
146    cs.setVersion(version);
147    cs.setName("DICOMTerminologyDefinitions");
148    cs.setTitle("DICOM Controlled Terminology Definitions");
149    cs.getIdentifier().add(new Identifier().setSystem("urn:ietf:rfc:3986").setValue("urn:oid:1.2.840.10008.2.16.4"));
150    cs.setStatus(PublicationStatus.ACTIVE);
151    cs.setExperimental(false);
152    cs.setPublisher("FHIR Project on behalf of DICOM");
153    cs.setDate(new Date());
154    cs.setDescription("DICOM Code Definitions (Coding Scheme Designator \"DCM\" Coding Scheme Version "+version);
155    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");
156    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.");
157    cs.setCaseSensitive(true);
158    cs.setValueSet("http://dicom.nema.org/resources/ValueSet/all");
159    cs.setContent(CodeSystemContentMode.COMPLETE);
160    
161    d = XMLUtil.getNextSibling(d);
162    while (d != null) {
163      String code = XMLUtil.getNamedChildText(d, "notation");
164      String display = XMLUtil.getNamedChildText(d, "prefLabel");
165      String definition = XMLUtil.getNamedChildText(d, "definition");
166      ConceptDefinitionComponent cc = new ConceptDefinitionComponent();
167      cs.getConcept().add(cc);
168      cc.setCode(code);
169      cc.setDisplay(display);
170      cc.setDefinition(definition);
171      d = XMLUtil.getNextSibling(d);
172    }
173    return cs;
174  }
175
176  private String processVersion(String s) {
177    String[] p = s.split("\\_");
178    return p[0].substring(0, 4)+"."+(1+(p[0].charAt(4) - 'a'))+"."+p[1];
179  }
180
181  public void setDest(String dest) {
182    this.dest = dest;    
183  }
184
185  public void setSource(String source) {
186    this.source = source;
187  }
188
189  public void setPattern(String pattern) {
190    this.pattern  = pattern;
191    
192  }
193}