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}