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