
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}