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}