
001package org.hl7.fhir.r4.terminologies; 002 003/* 004 Copyright (c) 2011+, HL7, Inc. 005 All rights reserved. 006 007 Redistribution and use in source and binary forms, with or without modification, 008 are permitted provided that the following conditions are met: 009 010 * Redistributions of source code must retain the above copyright notice, this 011 list of conditions and the following disclaimer. 012 * Redistributions in binary form must reproduce the above copyright notice, 013 this list of conditions and the following disclaimer in the documentation 014 and/or other materials provided with the distribution. 015 * Neither the name of HL7 nor the names of its contributors may be used to 016 endorse or promote products derived from this software without specific 017 prior written permission. 018 019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 022 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 023 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 025 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 026 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 028 POSSIBILITY OF SUCH DAMAGE. 029 030 */ 031 032import java.io.FileInputStream; 033import java.io.FileNotFoundException; 034import java.io.FileOutputStream; 035import java.io.IOException; 036 037import javax.xml.parsers.DocumentBuilder; 038import javax.xml.parsers.DocumentBuilderFactory; 039import javax.xml.parsers.ParserConfigurationException; 040 041import org.hl7.fhir.exceptions.FHIRFormatError; 042import org.hl7.fhir.r4.formats.XmlParser; 043import org.hl7.fhir.r4.model.Bundle; 044import org.hl7.fhir.r4.model.Bundle.BundleType; 045import org.hl7.fhir.r4.model.CodeableConcept; 046import org.hl7.fhir.r4.model.Coding; 047import org.hl7.fhir.r4.model.DateTimeType; 048import org.hl7.fhir.r4.model.InstantType; 049import org.hl7.fhir.r4.model.Meta; 050import org.hl7.fhir.utilities.Utilities; 051import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 052import org.hl7.fhir.utilities.xml.XMLUtil; 053import org.w3c.dom.Document; 054import org.w3c.dom.Element; 055import org.xml.sax.SAXException; 056import org.xmlpull.v1.XmlPullParserException; 057 058/** 059 * This class converts the LOINC XML representation that the FHIR build tool 060 * uses internally to a set of DataElements in an atom feed 061 * 062 * @author Grahame 063 * 064 */ 065@Deprecated 066public class LoincToDEConvertor { 067 068 public static void main(String[] args) 069 throws FHIRFormatError, IOException, XmlPullParserException, SAXException, ParserConfigurationException { 070 if (args.length == 0) { 071 System.out.println("FHIR LOINC to CDE convertor. "); 072 System.out.println(""); 073 System.out.println("This tool converts from LOINC to A set of DataElement definitions."); 074 System.out.println(""); 075 System.out.println("Usage: [jar(path?)] [dest] (-defn [definitions]) where: "); 076 System.out.println("* [dest] is a file name of the bundle to produce"); 077 System.out.println( 078 "* [definitions] is the file name of a file produced by exporting the main LOINC table from the mdb to XML"); 079 System.out.println(""); 080 } else { 081 LoincToDEConvertor exe = new LoincToDEConvertor(); 082 exe.setDest(args[0]); 083 for (int i = 1; i < args.length; i++) { 084 if (args[i].equals("-defn")) 085 exe.setDefinitions(args[i + 1]); 086 } 087 exe.process(); 088 } 089 090 } 091 092 private String dest; 093 private String definitions; 094 095 public String getDest() { 096 return dest; 097 } 098 099 public void setDest(String dest) { 100 this.dest = dest; 101 } 102 103 public String getDefinitions() { 104 return definitions; 105 } 106 107 public void setDefinitions(String definitions) { 108 this.definitions = definitions; 109 } 110 111 private Document xml; 112 private Bundle bundle; 113 private DateTimeType now; 114 115 public Bundle process(String sourceFile) 116 throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { 117 this.definitions = sourceFile; 118 log("Begin. Produce Loinc CDEs in " + dest + " from " + definitions); 119 loadLoinc(); 120 log("LOINC loaded"); 121 122 now = DateTimeType.now(); 123 124 bundle = new Bundle(); 125 bundle.setType(BundleType.COLLECTION); 126 bundle.setId("http://hl7.org/fhir/commondataelement/loinc"); 127 bundle.setMeta(new Meta().setLastUpdatedElement(InstantType.now())); 128 129 processLoincCodes(); 130 return bundle; 131 } 132 133 public void process() 134 throws FHIRFormatError, IOException, XmlPullParserException, SAXException, ParserConfigurationException { 135 log("Begin. Produce Loinc CDEs in " + dest + " from " + definitions); 136 loadLoinc(); 137 log("LOINC loaded"); 138 139 now = DateTimeType.now(); 140 141 bundle = new Bundle(); 142 bundle.setId("http://hl7.org/fhir/commondataelement/loinc"); 143 bundle.setMeta(new Meta().setLastUpdatedElement(InstantType.now())); 144 145 processLoincCodes(); 146 if (dest != null) { 147 log("Saving..."); 148 saveBundle(); 149 } 150 log("Done"); 151 152 } 153 154 private void log(String string) { 155 System.out.println(string); 156 157 } 158 159 private void loadLoinc() throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { 160 DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory(); 161 factory.setNamespaceAware(true); 162 DocumentBuilder builder = factory.newDocumentBuilder(); 163 164 xml = builder.parse(ManagedFileAccess.inStream(definitions)); 165 } 166 167 private void saveBundle() throws FHIRFormatError, IOException, XmlPullParserException { 168 XmlParser xml = new XmlParser(); 169 FileOutputStream s = ManagedFileAccess.outStream(dest); 170 xml.compose(s, bundle, true); 171 s.close(); 172 } 173 174 private String col(Element row, String name) { 175 Element e = XMLUtil.getNamedChild(row, name); 176 if (e == null) 177 return null; 178 String text = e.getTextContent(); 179 return text; 180 } 181 182 private boolean hasCol(Element row, String name) { 183 return Utilities.noString(col(row, name)); 184 } 185 186 private void processLoincCodes() { 187 Element row = XMLUtil.getFirstChild(xml.getDocumentElement()); 188 int i = 0; 189 while (row != null) { 190 i++; 191 if (i % 1000 == 0) 192 System.out.print("."); 193 String code = col(row, "LOINC_NUM"); 194 String comp = col(row, "COMPONENT"); 195// DataElement de = new DataElement(); 196// de.setId("loinc-"+code); 197// de.setMeta(new Meta().setLastUpdatedElement(InstantType.now())); 198// bundle.getEntry().add(new BundleEntryComponent().setResource(de)); 199// Identifier id = new Identifier(); 200// id.setSystem("http://hl7.org/fhir/commondataelement/loinc"); 201// id.setValue(code); 202// de.addIdentifier(id); 203// de.setPublisher("Regenstrief + FHIR Project Team"); 204// if (!col(row, "STATUS").equals("ACTIVE")) 205// de.setStatus(PublicationStatus.DRAFT); // till we get good at this 206// else 207// de.setStatus(PublicationStatus.RETIRED); 208// de.setDateElement(DateTimeType.now()); 209// de.setName(comp); 210// ElementDefinition dee = de.addElement(); 211// 212// // PROPERTY ignore 213// // TIME_ASPCT 214// // SYSTEM 215// // SCALE_TYP 216// // METHOD_TYP 217// // dee.getCategory().add(new CodeableConcept().setText(col(row, "CLASS"))); 218// // SOURCE 219// // DATE_LAST_CHANGED - should be in ? 220// // CHNG_TYPE 221// dee.setComment(col(row , "COMMENTS")); 222// if (hasCol(row, "CONSUMER_NAME")) 223// dee.addAlias(col(row, "CONSUMER_NAME")); 224// // MOLAR_MASS 225// // CLASSTYPE 226// // FORMULA 227// // SPECIES 228// // EXMPL_ANSWERS 229// // ACSSYM 230// // BASE_NAME - ? this is a relationship 231// // NAACCR_ID 232// // ---------- CODE_TABLE todo 233// // SURVEY_QUEST_TEXT 234// // SURVEY_QUEST_SRC 235// if (hasCol(row, "RELATEDNAMES2")) { 236// String n = col(row, "RELATEDNAMES2"); 237// for (String s : n.split("\\;")) { 238// if (!Utilities.noString(s)) 239// dee.addAlias(s); 240// } 241// } 242// dee.addAlias(col(row, "SHORTNAME")); 243// // ORDER_OBS 244// // CDISC Code 245// // HL7_FIELD_SUBFIELD_ID 246// // ------------------ EXTERNAL_COPYRIGHT_NOTICE todo 247// dee.setDefinition(col(row, "LONG_COMMON_NAME")); 248// // HL7_V2_DATATYPE 249// String cc = makeType(col(row, "HL7_V3_DATATYPE"), code); 250// if (cc != null) 251// dee.addType().setCode(cc); 252// // todo... CURATED_RANGE_AND_UNITS 253// // todo: DOCUMENT_SECTION 254// // STATUS_REASON 255// // STATUS_TEXT 256// // CHANGE_REASON_PUBLIC 257// // COMMON_TEST_RANK 258// // COMMON_ORDER_RANK 259// // COMMON_SI_TEST_RANK 260// // HL7_ATTACHMENT_STRUCTURE 261// 262// // units: 263// // UNITSREQUIRED 264// // SUBMITTED_UNITS 265// ToolingExtensions.setAllowableUnits(dee, makeUnits(col(row, "EXAMPLE_UNITS"), col(row, "EXAMPLE_UCUM_UNITS"))); 266// // EXAMPLE_SI_UCUM_UNITS 267 268 row = XMLUtil.getNextSibling(row); 269 } 270 System.out.println("done"); 271 } 272 273 private String makeType(String type, String id) { 274 if (Utilities.noString(type)) 275 return null; 276 if (type.equals("PQ")) 277 return "Quantity"; 278 else if (type.equals("ED")) 279 return "Attachment"; 280 else if (type.equals("TS")) 281 return "dateTime"; 282 else if (type.equals("ST")) 283 return "string"; 284 else if (type.equals("II")) 285 return "Identifier"; 286 else if (type.equals("CWE")) 287 return "CodeableConcept"; 288 else if (type.equals("CD") || type.equals("CO")) 289 return "CodeableConcept"; 290 else if (type.equals("PN")) 291 return "HumanName"; 292 else if (type.equals("EN")) 293 return "HumanName"; 294 else if (type.equals("AD")) 295 return "Address"; 296 else if (type.equals("BL")) 297 return "boolean"; 298 else if (type.equals("GTS")) 299 return "Schedule"; 300 else if (type.equals("INT")) 301 return "integer"; 302 else if (type.equals("CS")) 303 return "code"; 304 else if (type.equals("IVL_TS")) 305 return "Period"; 306 else if (type.equals("MMAT") || type.equals("PRF") || type.equals("TX") || type.equals("DT") || type.equals("FT")) 307 return null; 308 else 309 throw new Error("unmapped type " + type + " for LOINC code " + id); 310 } // 18606-4: MMAT. 18665-0: PRF. 18671-8: TX. 55400-6: DT; 8251-1: FT 311 312 private CodeableConcept makeUnits(String text, String ucum) { 313 if (Utilities.noString(text) && Utilities.noString(ucum)) 314 return null; 315 CodeableConcept cc = new CodeableConcept(); 316 cc.setText(text); 317 cc.getCoding().add(new Coding().setCode(ucum).setSystem("http://unitsofmeasure.org")); 318 return cc; 319 } 320 321 public Bundle getBundle() { 322 return bundle; 323 } 324}