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