
001package org.hl7.fhir.convertors.misc.iso21090; 002 003import java.io.FileInputStream; 004import java.io.FileOutputStream; 005import java.io.IOException; 006import java.util.Date; 007import java.util.HashMap; 008import java.util.List; 009import java.util.Map; 010 011import javax.xml.parsers.DocumentBuilder; 012import javax.xml.parsers.DocumentBuilderFactory; 013import javax.xml.parsers.ParserConfigurationException; 014 015/* 016 Copyright (c) 2011+, HL7, Inc. 017 All rights reserved. 018 019 Redistribution and use in source and binary forms, with or without modification, 020 are permitted provided that the following conditions are met: 021 022 * Redistributions of source code must retain the above copyright notice, this 023 list of conditions and the following disclaimer. 024 * Redistributions in binary form must reproduce the above copyright notice, 025 this list of conditions and the following disclaimer in the documentation 026 and/or other materials provided with the distribution. 027 * Neither the name of HL7 nor the names of its contributors may be used to 028 endorse or promote products derived from this software without specific 029 prior written permission. 030 031 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 032 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 033 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 034 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 035 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 036 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 037 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 038 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 039 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 040 POSSIBILITY OF SUCH DAMAGE. 041 042 */ 043 044 045import org.hl7.fhir.dstu3.context.IWorkerContext; 046import org.hl7.fhir.dstu3.context.SimpleWorkerContext; 047import org.hl7.fhir.dstu3.formats.IParser.OutputStyle; 048import org.hl7.fhir.dstu3.formats.XmlParser; 049import org.hl7.fhir.dstu3.model.ElementDefinition; 050import org.hl7.fhir.dstu3.model.ElementDefinition.PropertyRepresentation; 051import org.hl7.fhir.dstu3.model.Enumerations.BindingStrength; 052import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; 053import org.hl7.fhir.dstu3.model.StructureDefinition; 054import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionKind; 055import org.hl7.fhir.dstu3.model.StructureDefinition.TypeDerivationRule; 056import org.hl7.fhir.dstu3.model.UriType; 057import org.hl7.fhir.dstu3.model.ValueSet; 058import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; 059import org.hl7.fhir.dstu3.utils.ToolingExtensions; 060import org.hl7.fhir.exceptions.FHIRFormatError; 061import org.hl7.fhir.utilities.Utilities; 062import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; 063import org.hl7.fhir.utilities.xml.XMLUtil; 064import org.w3c.dom.Document; 065import org.w3c.dom.Element; 066import org.xml.sax.SAXException; 067 068@SuppressWarnings("checkstyle:systemout") 069public class ISO21090Importer { 070 071 private final Map<String, EnumValueSet> bindings = new HashMap<>(); 072 private final Map<String, DataType> types = new HashMap<>(); 073 private IWorkerContext ctxt; 074 private Element schema; 075 076 public static void main(String[] args) throws Exception { 077 new ISO21090Importer().process(); 078 } 079 080 private void process() throws Exception { 081 ctxt = SimpleWorkerContext.fromPack("C:\\work\\org.hl7.fhir\\build\\publish\\igpack.zip"); 082 load(); 083 processEnums(); 084 processDataTypes(); 085 generate(); 086 087 System.out.print("done"); 088 } 089 090 private void generate() throws Exception { 091 for (EnumValueSet evs : bindings.values()) { 092 generateValueSet(evs); 093 } 094 for (DataType dt : types.values()) { 095 generateType(dt); 096 } 097 } 098 099 private void generateType(DataType dt) throws Exception { 100 StructureDefinition sd = new StructureDefinition(); 101 sd.setId(dt.getName()); 102 sd.setUrl("http://hl7.org/fhir/iso21090/StructureDefinition/" + sd.getId()); 103 sd.setName(dt.getName() + " data type"); 104 sd.setStatus(PublicationStatus.ACTIVE); 105 sd.setExperimental(false); 106 sd.setPublisher("HL7 / ISO"); 107 sd.setDate(new Date()); 108 sd.setDescription(dt.getDoco()); 109 sd.setKind(StructureDefinitionKind.LOGICAL); 110 sd.setAbstract(Utilities.existsInList(dt.getName(), "HXIT", "QTY")); 111 sd.setType("Element"); 112 if (dt.getParent() == null) 113 sd.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/Element"); 114 else 115 sd.setBaseDefinition("http://hl7.org/fhir/iso21090/StructureDefinition/" + dt.getParent()); 116 sd.setDerivation(TypeDerivationRule.SPECIALIZATION); 117 ElementDefinition ed = sd.getDifferential().addElement(); 118 ed.setPath(dt.getName()); 119 produceProperties(sd.getDifferential().getElement(), dt.getName(), dt.getProperties(), true, false); 120 produceProperties(sd.getDifferential().getElement(), dt.getName(), dt.getProperties(), false, false); 121 ed = sd.getSnapshot().addElement(); 122 ed.setPath(dt.getName()); 123 if (dt.getParent() != null) 124 addParentProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getParent(), true, true); 125 produceProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getProperties(), true, true); 126 if (dt.getParent() != null) 127 addParentProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getParent(), false, true); 128 produceProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getProperties(), false, true); 129 ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); 130 new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "iso21090\\StructureDefinition-" + dt.getName() + ".xml")), sd); 131 } 132 133 private void addParentProperties(List<ElementDefinition> elements, String name, String parent, boolean attrMode, boolean snapshot) throws FHIRFormatError { 134 DataType dt = types.get(parent); 135 if (dt == null) 136 throw new Error("No find " + parent); 137 if (dt.getParent() != null) 138 addParentProperties(elements, name, dt.getParent(), attrMode, snapshot); 139 produceProperties(elements, name, dt.getProperties(), attrMode, snapshot); 140 } 141 142 private void produceProperties(List<ElementDefinition> elements, String name, List<Property> properties, boolean attrMode, boolean snapshot) throws FHIRFormatError { 143 for (Property p : properties) { 144 if (p.isIsattr() == attrMode) { 145 ElementDefinition ed = new ElementDefinition(); 146 elements.add(ed); 147 ed.setPath(name + "." + p.getName()); 148 if (p.getType().startsWith("xsd:")) 149 ToolingExtensions.addStringExtension(ed.addType(), ToolingExtensions.EXT_XML_TYPE, p.getType()); 150 else 151 ed.addType().setCode(p.getType()); 152 ed.setMin(p.getMin()); 153 ed.setMax(p.getMax() == Integer.MAX_VALUE ? "*" : Integer.toString(p.getMax())); 154 ed.setDefinition(p.getDoco()); 155 if (p.isIsattr()) 156 ed.addRepresentation(PropertyRepresentation.XMLATTR); 157 if (p.getBinding() != null) 158 ed.getBinding().setStrength(BindingStrength.REQUIRED).setValueSet(new UriType("http://hl7.org/fhir/iso21090/ValueSet/" + p.getBinding())); 159 if (snapshot) 160 ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); 161 } 162 } 163 } 164 165 private void generateValueSet(EnumValueSet evs) throws Exception { 166 ValueSet bvs = ctxt.fetchResource(ValueSet.class, evs.getTemplate()); 167 if (bvs == null) 168 throw new Exception("Did not find template value set " + evs.getTemplate()); 169 ValueSet vs = bvs.copy(); 170 vs.getCompose().getInclude().clear(); 171 vs.getIdentifier().clear(); 172 vs.setName("ISO 20190 " + evs.getName() + " Enumeration"); 173 vs.setId(evs.getName()); 174 vs.setUrl("http://hl7.org/fhir/iso21090/ValueSet/" + vs.getId()); 175 vs.setDate(new Date()); 176 vs.setExperimental(false); 177 ConceptSetComponent inc = vs.getCompose().addInclude().setSystem(evs.getSystem()); 178 for (String code : evs.getCodes()) { 179 inc.addConcept().setCode(code); 180 } 181 new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "iso21090\\ValueSet-" + evs.getName() + ".xml")), vs); 182 } 183 184 private void processDataTypes() { 185 Element type = XMLUtil.getFirstChild(schema); 186 while (type != null) { 187 if (type.getTagName().equals("xsd:complexType")) { 188 String n = type.getAttribute("name"); 189 if (!(n.contains(".") || n.contains("_") || n.equals("XReference"))) 190 processDataType(n, type); 191 } 192 type = XMLUtil.getNextSibling(type); 193 } 194 } 195 196 private void processDataType(String n, Element type) { 197 DataType dt = new DataType(); 198 types.put(n, dt); 199 dt.setName(n); 200 dt.setDoco(getDoco(type)); 201 Element cnt; 202 Element ext = XMLUtil.getNamedChild(XMLUtil.getNamedChild(type, "xsd:complexContent"), "xsd:extension"); 203 if (ext != null) { 204 dt.setParent(ext.getAttribute("base")); 205 cnt = XMLUtil.getFirstChild(ext); 206 } else { 207 cnt = XMLUtil.getFirstChild(type); 208 } 209 if (cnt.getTagName().equals("xsd:annotation")) 210 cnt = XMLUtil.getNextSibling(cnt); 211 System.out.println(n + " (" + dt.getParent() + ")"); 212 while (cnt != null) { 213 if (cnt.getTagName().equals("xsd:attribute")) { 214 processAttribute(dt, cnt); 215 } else if (cnt.getTagName().equals("xsd:sequence")) { 216 Element e = XMLUtil.getFirstChild(cnt); 217 while (e != null) { 218 if (e.getTagName().equals("xsd:element")) { 219 processElement(dt, e); 220 } else 221 System.out.println("2. ignore " + e.getTagName()); 222 223 e = XMLUtil.getNextSibling(e); 224 } 225 } else 226 System.out.println("ignore " + cnt.getTagName()); 227 cnt = XMLUtil.getNextSibling(cnt); 228 } 229 } 230 231 private void processElement(DataType dt, Element elem) { 232 Property prop = new Property(); 233 prop.setName(elem.getAttribute("name")); 234 prop.setMin(Integer.parseInt(elem.getAttribute("minOccurs"))); 235 prop.setMax("unbounded".equals(elem.getAttribute("maxOccurs")) ? Integer.MAX_VALUE : Integer.parseInt(elem.getAttribute("maxOccurs"))); 236 prop.setType(elem.getAttribute("type")); 237 prop.setDoco(getDoco(elem)); 238 dt.getProperties().add(prop); 239 System.out.println(" " + prop.getName() + " : " + prop.getType() + " [" + prop.getMin() + ".." + prop.getMax() + "]"); 240 } 241 242 private void processAttribute(DataType dt, Element attr) { 243 Property prop = new Property(); 244 prop.setName(attr.getAttribute("name")); 245 prop.setType(attr.getAttribute("type")); 246 if (!prop.getType().startsWith("xsd:")) { 247 if (Utilities.noString(prop.getType())) 248 prop.setType("xsd:string"); 249 else if (bindings.containsKey(prop.getType())) { 250 prop.setBinding(prop.getType()); 251 prop.setType("xsd:string"); 252 } else if (prop.getType().startsWith("set_") && bindings.containsKey(prop.getType().substring(4))) { 253 prop.setBinding(prop.getType().substring(4)); 254 prop.setType("xsd:string"); 255 prop.setMax(Integer.MAX_VALUE); 256 } else if ("Uid".equals(prop.getType())) 257 prop.setType("xsd:string"); 258 else if ("Code".equals(prop.getType())) 259 prop.setType("xsd:token"); 260 else if ("Decimal".equals(prop.getType())) 261 prop.setType("xsd:decimal"); 262 else 263 throw new Error("Unknown type " + prop.getType() + " on " + dt.getName() + "." + prop.getName()); 264 } 265 prop.setMin("optional".equals(attr.getAttribute("use")) ? 0 : 1); 266 prop.setMax(1); 267 prop.setDoco(getDoco(attr)); 268 prop.setIsattr(true); 269 dt.getProperties().add(prop); 270 System.out.println(" " + prop.getName() + " : " + prop.getType() + " [" + prop.getMin() + ".." + prop.getMax() + "]"); 271 } 272 273 private void processEnums() { 274 Element type = XMLUtil.getFirstChild(schema); 275 while (type != null) { 276 if (type.getTagName().equals("xsd:simpleType")) { 277 Element res = XMLUtil.getFirstChild(type); 278 Element en = XMLUtil.getFirstChild(res); 279 if (en != null && en.getTagName().equals("xsd:enumeration") && !type.getAttribute("name").contains(".")) 280 processEnum(type.getAttribute("name"), en); 281 } 282 type = XMLUtil.getNextSibling(type); 283 } 284 } 285 286 private void processEnum(String n, Element en) { 287 EnumValueSet vs = new EnumValueSet(); 288 bindings.put(n, vs); 289 vs.setName(n); 290 String v3n; 291 if (n.contains("EntityName")) 292 v3n = n + "R2"; 293 else if (n.equals("Compression")) 294 v3n = "CompressionAlgorithm"; 295 else if (n.equals("UpdateMode")) 296 v3n = "HL7UpdateMode"; 297 else if (n.equals("UncertaintyType")) 298 v3n = "ProbabilityDistributionType"; 299 else if (n.equals("TelecommunicationAddressUse") || n.equals("PostalAddressUse")) 300 v3n = "AddressUse"; 301 else if (n.equals("TelecommunicationCapability")) 302 v3n = "TelecommunicationCapabilities"; 303 else 304 v3n = n; 305 vs.setSystem("http://hl7.org/fhir/v3-" + v3n); 306 vs.setTemplate("http://hl7.org/fhir/ValueSet/v3-" + v3n); 307 System.out.println("Enum: " + n + " == " + vs.getSystem()); 308 while (en != null) { 309 vs.getCodes().add(en.getAttribute("value")); 310 vs.getMembers().put(en.getAttribute("value"), getDoco(en)); 311 en = XMLUtil.getNextSibling(en); 312 } 313 } 314 315 private String getDoco(Element en) { 316 Element doco = XMLUtil.getNamedChild(XMLUtil.getNamedChild(en, "xsd:annotation"), "xsd:documentation"); 317 return doco == null ? null : doco.getTextContent(); 318 } 319 320 private void load() throws ParserConfigurationException, SAXException, IOException { 321 DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory(); 322 factory.setNamespaceAware(false); 323 DocumentBuilder builder = factory.newDocumentBuilder(); 324 Document doc = builder.parse(ManagedFileAccess.inStream("C:\\work\\projects\\org.hl7.v3.dt\\iso\\iso-21090-datatypes.xsd")); 325 schema = doc.getDocumentElement(); 326 } 327 328}