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