
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 066@SuppressWarnings("checkstyle:systemout") 067public class LoincToDEConvertor { 068 069 public static void main(String[] args) 070 throws FHIRFormatError, IOException, XmlPullParserException, SAXException, ParserConfigurationException { 071 if (args.length == 0) { 072 System.out.println("FHIR LOINC to CDE convertor. "); 073 System.out.println(""); 074 System.out.println("This tool converts from LOINC to A set of DataElement definitions."); 075 System.out.println(""); 076 System.out.println("Usage: [jar(path?)] [dest] (-defn [definitions]) where: "); 077 System.out.println("* [dest] is a file name of the bundle to produce"); 078 System.out.println( 079 "* [definitions] is the file name of a file produced by exporting the main LOINC table from the mdb to XML"); 080 System.out.println(""); 081 } else { 082 LoincToDEConvertor exe = new LoincToDEConvertor(); 083 exe.setDest(args[0]); 084 for (int i = 1; i < args.length; i++) { 085 if (args[i].equals("-defn")) 086 exe.setDefinitions(args[i + 1]); 087 } 088 exe.process(); 089 } 090 091 } 092 093 private String dest; 094 private String definitions; 095 096 public String getDest() { 097 return dest; 098 } 099 100 public void setDest(String dest) { 101 this.dest = dest; 102 } 103 104 public String getDefinitions() { 105 return definitions; 106 } 107 108 public void setDefinitions(String definitions) { 109 this.definitions = definitions; 110 } 111 112 private Document xml; 113 private Bundle bundle; 114 private DateTimeType now; 115 116 public Bundle process(String sourceFile) 117 throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { 118 this.definitions = sourceFile; 119 log("Begin. Produce Loinc CDEs in " + dest + " from " + definitions); 120 loadLoinc(); 121 log("LOINC loaded"); 122 123 now = DateTimeType.now(); 124 125 bundle = new Bundle(); 126 bundle.setType(BundleType.COLLECTION); 127 bundle.setId("http://hl7.org/fhir/commondataelement/loinc"); 128 bundle.setMeta(new Meta().setLastUpdatedElement(InstantType.now())); 129 130 processLoincCodes(); 131 return bundle; 132 } 133 134 public void process() 135 throws FHIRFormatError, IOException, XmlPullParserException, SAXException, ParserConfigurationException { 136 log("Begin. Produce Loinc CDEs in " + dest + " from " + definitions); 137 loadLoinc(); 138 log("LOINC loaded"); 139 140 now = DateTimeType.now(); 141 142 bundle = new Bundle(); 143 bundle.setId("http://hl7.org/fhir/commondataelement/loinc"); 144 bundle.setMeta(new Meta().setLastUpdatedElement(InstantType.now())); 145 146 processLoincCodes(); 147 if (dest != null) { 148 log("Saving..."); 149 saveBundle(); 150 } 151 log("Done"); 152 153 } 154 155 private void log(String string) { 156 System.out.println(string); 157 158 } 159 160 private void loadLoinc() throws FileNotFoundException, SAXException, IOException, ParserConfigurationException { 161 DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory(); 162 factory.setNamespaceAware(true); 163 DocumentBuilder builder = factory.newDocumentBuilder(); 164 165 xml = builder.parse(ManagedFileAccess.inStream(definitions)); 166 } 167 168 private void saveBundle() throws FHIRFormatError, IOException, XmlPullParserException { 169 XmlParser xml = new XmlParser(); 170 FileOutputStream s = ManagedFileAccess.outStream(dest); 171 xml.compose(s, bundle, true); 172 s.close(); 173 } 174 175 private String col(Element row, String name) { 176 Element e = XMLUtil.getNamedChild(row, name); 177 if (e == null) 178 return null; 179 String text = e.getTextContent(); 180 return text; 181 } 182 183 private boolean hasCol(Element row, String name) { 184 return Utilities.noString(col(row, name)); 185 } 186 187 private void processLoincCodes() { 188 Element row = XMLUtil.getFirstChild(xml.getDocumentElement()); 189 int i = 0; 190 while (row != null) { 191 i++; 192 if (i % 1000 == 0) 193 System.out.print("."); 194 String code = col(row, "LOINC_NUM"); 195 String comp = col(row, "COMPONENT"); 196// DataElement de = new DataElement(); 197// de.setId("loinc-"+code); 198// de.setMeta(new Meta().setLastUpdatedElement(InstantType.now())); 199// bundle.getEntry().add(new BundleEntryComponent().setResource(de)); 200// Identifier id = new Identifier(); 201// id.setSystem("http://hl7.org/fhir/commondataelement/loinc"); 202// id.setValue(code); 203// de.addIdentifier(id); 204// de.setPublisher("Regenstrief + FHIR Project Team"); 205// if (!col(row, "STATUS").equals("ACTIVE")) 206// de.setStatus(PublicationStatus.DRAFT); // till we get good at this 207// else 208// de.setStatus(PublicationStatus.RETIRED); 209// de.setDateElement(DateTimeType.now()); 210// de.setName(comp); 211// ElementDefinition dee = de.addElement(); 212// 213// // PROPERTY ignore 214// // TIME_ASPCT 215// // SYSTEM 216// // SCALE_TYP 217// // METHOD_TYP 218// // dee.getCategory().add(new CodeableConcept().setText(col(row, "CLASS"))); 219// // SOURCE 220// // DATE_LAST_CHANGED - should be in ? 221// // CHNG_TYPE 222// dee.setComment(col(row , "COMMENTS")); 223// if (hasCol(row, "CONSUMER_NAME")) 224// dee.addAlias(col(row, "CONSUMER_NAME")); 225// // MOLAR_MASS 226// // CLASSTYPE 227// // FORMULA 228// // SPECIES 229// // EXMPL_ANSWERS 230// // ACSSYM 231// // BASE_NAME - ? this is a relationship 232// // NAACCR_ID 233// // ---------- CODE_TABLE todo 234// // SURVEY_QUEST_TEXT 235// // SURVEY_QUEST_SRC 236// if (hasCol(row, "RELATEDNAMES2")) { 237// String n = col(row, "RELATEDNAMES2"); 238// for (String s : n.split("\\;")) { 239// if (!Utilities.noString(s)) 240// dee.addAlias(s); 241// } 242// } 243// dee.addAlias(col(row, "SHORTNAME")); 244// // ORDER_OBS 245// // CDISC Code 246// // HL7_FIELD_SUBFIELD_ID 247// // ------------------ EXTERNAL_COPYRIGHT_NOTICE todo 248// dee.setDefinition(col(row, "LONG_COMMON_NAME")); 249// // HL7_V2_DATATYPE 250// String cc = makeType(col(row, "HL7_V3_DATATYPE"), code); 251// if (cc != null) 252// dee.addType().setCode(cc); 253// // todo... CURATED_RANGE_AND_UNITS 254// // todo: DOCUMENT_SECTION 255// // STATUS_REASON 256// // STATUS_TEXT 257// // CHANGE_REASON_PUBLIC 258// // COMMON_TEST_RANK 259// // COMMON_ORDER_RANK 260// // COMMON_SI_TEST_RANK 261// // HL7_ATTACHMENT_STRUCTURE 262// 263// // units: 264// // UNITSREQUIRED 265// // SUBMITTED_UNITS 266// ToolingExtensions.setAllowableUnits(dee, makeUnits(col(row, "EXAMPLE_UNITS"), col(row, "EXAMPLE_UCUM_UNITS"))); 267// // EXAMPLE_SI_UCUM_UNITS 268 269 row = XMLUtil.getNextSibling(row); 270 } 271 System.out.println("done"); 272 } 273 274 private String makeType(String type, String id) { 275 if (Utilities.noString(type)) 276 return null; 277 if (type.equals("PQ")) 278 return "Quantity"; 279 else if (type.equals("ED")) 280 return "Attachment"; 281 else if (type.equals("TS")) 282 return "dateTime"; 283 else if (type.equals("ST")) 284 return "string"; 285 else if (type.equals("II")) 286 return "Identifier"; 287 else if (type.equals("CWE")) 288 return "CodeableConcept"; 289 else if (type.equals("CD") || type.equals("CO")) 290 return "CodeableConcept"; 291 else if (type.equals("PN")) 292 return "HumanName"; 293 else if (type.equals("EN")) 294 return "HumanName"; 295 else if (type.equals("AD")) 296 return "Address"; 297 else if (type.equals("BL")) 298 return "boolean"; 299 else if (type.equals("GTS")) 300 return "Schedule"; 301 else if (type.equals("INT")) 302 return "integer"; 303 else if (type.equals("CS")) 304 return "code"; 305 else if (type.equals("IVL_TS")) 306 return "Period"; 307 else if (type.equals("MMAT") || type.equals("PRF") || type.equals("TX") || type.equals("DT") || type.equals("FT")) 308 return null; 309 else 310 throw new Error("unmapped type " + type + " for LOINC code " + id); 311 } // 18606-4: MMAT. 18665-0: PRF. 18671-8: TX. 55400-6: DT; 8251-1: FT 312 313 private CodeableConcept makeUnits(String text, String ucum) { 314 if (Utilities.noString(text) && Utilities.noString(ucum)) 315 return null; 316 CodeableConcept cc = new CodeableConcept(); 317 cc.setText(text); 318 cc.getCoding().add(new Coding().setCode(ucum).setSystem("http://unitsofmeasure.org")); 319 return cc; 320 } 321 322 public Bundle getBundle() { 323 return bundle; 324 } 325}