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