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