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 068public class ISO21090Importer { 069 070 private final Map<String, EnumValueSet> bindings = new HashMap<>(); 071 private final Map<String, DataType> types = new HashMap<>(); 072 private IWorkerContext ctxt; 073 private Element schema; 074 075 public static void main(String[] args) throws Exception { 076 new ISO21090Importer().process(); 077 } 078 079 private void process() throws Exception { 080 ctxt = SimpleWorkerContext.fromPack("C:\\work\\org.hl7.fhir\\build\\publish\\igpack.zip"); 081 load(); 082 processEnums(); 083 processDataTypes(); 084 generate(); 085 086 System.out.print("done"); 087 } 088 089 private void generate() throws Exception { 090 for (EnumValueSet evs : bindings.values()) { 091 generateValueSet(evs); 092 } 093 for (DataType dt : types.values()) { 094 generateType(dt); 095 } 096 } 097 098 private void generateType(DataType dt) throws Exception { 099 StructureDefinition sd = new StructureDefinition(); 100 sd.setId(dt.getName()); 101 sd.setUrl("http://hl7.org/fhir/iso21090/StructureDefinition/" + sd.getId()); 102 sd.setName(dt.getName() + " data type"); 103 sd.setStatus(PublicationStatus.ACTIVE); 104 sd.setExperimental(false); 105 sd.setPublisher("HL7 / ISO"); 106 sd.setDate(new Date()); 107 sd.setDescription(dt.getDoco()); 108 sd.setKind(StructureDefinitionKind.LOGICAL); 109 sd.setAbstract(Utilities.existsInList(dt.getName(), "HXIT", "QTY")); 110 sd.setType("Element"); 111 if (dt.getParent() == null) 112 sd.setBaseDefinition("http://hl7.org/fhir/StructureDefinition/Element"); 113 else 114 sd.setBaseDefinition("http://hl7.org/fhir/iso21090/StructureDefinition/" + dt.getParent()); 115 sd.setDerivation(TypeDerivationRule.SPECIALIZATION); 116 ElementDefinition ed = sd.getDifferential().addElement(); 117 ed.setPath(dt.getName()); 118 produceProperties(sd.getDifferential().getElement(), dt.getName(), dt.getProperties(), true, false); 119 produceProperties(sd.getDifferential().getElement(), dt.getName(), dt.getProperties(), false, false); 120 ed = sd.getSnapshot().addElement(); 121 ed.setPath(dt.getName()); 122 if (dt.getParent() != null) 123 addParentProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getParent(), true, true); 124 produceProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getProperties(), true, true); 125 if (dt.getParent() != null) 126 addParentProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getParent(), false, true); 127 produceProperties(sd.getSnapshot().getElement(), dt.getName(), dt.getProperties(), false, true); 128 ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); 129 new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "iso21090\\StructureDefinition-" + dt.getName() + ".xml")), sd); 130 } 131 132 private void addParentProperties(List<ElementDefinition> elements, String name, String parent, boolean attrMode, boolean snapshot) throws FHIRFormatError { 133 DataType dt = types.get(parent); 134 if (dt == null) 135 throw new Error("No find " + parent); 136 if (dt.getParent() != null) 137 addParentProperties(elements, name, dt.getParent(), attrMode, snapshot); 138 produceProperties(elements, name, dt.getProperties(), attrMode, snapshot); 139 } 140 141 private void produceProperties(List<ElementDefinition> elements, String name, List<Property> properties, boolean attrMode, boolean snapshot) throws FHIRFormatError { 142 for (Property p : properties) { 143 if (p.isIsattr() == attrMode) { 144 ElementDefinition ed = new ElementDefinition(); 145 elements.add(ed); 146 ed.setPath(name + "." + p.getName()); 147 if (p.getType().startsWith("xsd:")) 148 ToolingExtensions.addStringExtension(ed.addType(), ToolingExtensions.EXT_XML_TYPE, p.getType()); 149 else 150 ed.addType().setCode(p.getType()); 151 ed.setMin(p.getMin()); 152 ed.setMax(p.getMax() == Integer.MAX_VALUE ? "*" : Integer.toString(p.getMax())); 153 ed.setDefinition(p.getDoco()); 154 if (p.isIsattr()) 155 ed.addRepresentation(PropertyRepresentation.XMLATTR); 156 if (p.getBinding() != null) 157 ed.getBinding().setStrength(BindingStrength.REQUIRED).setValueSet(new UriType("http://hl7.org/fhir/iso21090/ValueSet/" + p.getBinding())); 158 if (snapshot) 159 ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax()); 160 } 161 } 162 } 163 164 private void generateValueSet(EnumValueSet evs) throws Exception { 165 ValueSet bvs = ctxt.fetchResource(ValueSet.class, evs.getTemplate()); 166 if (bvs == null) 167 throw new Exception("Did not find template value set " + evs.getTemplate()); 168 ValueSet vs = bvs.copy(); 169 vs.getCompose().getInclude().clear(); 170 vs.getIdentifier().clear(); 171 vs.setName("ISO 20190 " + evs.getName() + " Enumeration"); 172 vs.setId(evs.getName()); 173 vs.setUrl("http://hl7.org/fhir/iso21090/ValueSet/" + vs.getId()); 174 vs.setDate(new Date()); 175 vs.setExperimental(false); 176 ConceptSetComponent inc = vs.getCompose().addInclude().setSystem(evs.getSystem()); 177 for (String code : evs.getCodes()) { 178 inc.addConcept().setCode(code); 179 } 180 new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path("[tmp]", "iso21090\\ValueSet-" + evs.getName() + ".xml")), vs); 181 } 182 183 private void processDataTypes() { 184 Element type = XMLUtil.getFirstChild(schema); 185 while (type != null) { 186 if (type.getTagName().equals("xsd:complexType")) { 187 String n = type.getAttribute("name"); 188 if (!(n.contains(".") || n.contains("_") || n.equals("XReference"))) 189 processDataType(n, type); 190 } 191 type = XMLUtil.getNextSibling(type); 192 } 193 } 194 195 private void processDataType(String n, Element type) { 196 DataType dt = new DataType(); 197 types.put(n, dt); 198 dt.setName(n); 199 dt.setDoco(getDoco(type)); 200 Element cnt; 201 Element ext = XMLUtil.getNamedChild(XMLUtil.getNamedChild(type, "xsd:complexContent"), "xsd:extension"); 202 if (ext != null) { 203 dt.setParent(ext.getAttribute("base")); 204 cnt = XMLUtil.getFirstChild(ext); 205 } else { 206 cnt = XMLUtil.getFirstChild(type); 207 } 208 if (cnt.getTagName().equals("xsd:annotation")) 209 cnt = XMLUtil.getNextSibling(cnt); 210 System.out.println(n + " (" + dt.getParent() + ")"); 211 while (cnt != null) { 212 if (cnt.getTagName().equals("xsd:attribute")) { 213 processAttribute(dt, cnt); 214 } else if (cnt.getTagName().equals("xsd:sequence")) { 215 Element e = XMLUtil.getFirstChild(cnt); 216 while (e != null) { 217 if (e.getTagName().equals("xsd:element")) { 218 processElement(dt, e); 219 } else 220 System.out.println("2. ignore " + e.getTagName()); 221 222 e = XMLUtil.getNextSibling(e); 223 } 224 } else 225 System.out.println("ignore " + cnt.getTagName()); 226 cnt = XMLUtil.getNextSibling(cnt); 227 } 228 } 229 230 private void processElement(DataType dt, Element elem) { 231 Property prop = new Property(); 232 prop.setName(elem.getAttribute("name")); 233 prop.setMin(Integer.parseInt(elem.getAttribute("minOccurs"))); 234 prop.setMax("unbounded".equals(elem.getAttribute("maxOccurs")) ? Integer.MAX_VALUE : Integer.parseInt(elem.getAttribute("maxOccurs"))); 235 prop.setType(elem.getAttribute("type")); 236 prop.setDoco(getDoco(elem)); 237 dt.getProperties().add(prop); 238 System.out.println(" " + prop.getName() + " : " + prop.getType() + " [" + prop.getMin() + ".." + prop.getMax() + "]"); 239 } 240 241 private void processAttribute(DataType dt, Element attr) { 242 Property prop = new Property(); 243 prop.setName(attr.getAttribute("name")); 244 prop.setType(attr.getAttribute("type")); 245 if (!prop.getType().startsWith("xsd:")) { 246 if (Utilities.noString(prop.getType())) 247 prop.setType("xsd:string"); 248 else if (bindings.containsKey(prop.getType())) { 249 prop.setBinding(prop.getType()); 250 prop.setType("xsd:string"); 251 } else if (prop.getType().startsWith("set_") && bindings.containsKey(prop.getType().substring(4))) { 252 prop.setBinding(prop.getType().substring(4)); 253 prop.setType("xsd:string"); 254 prop.setMax(Integer.MAX_VALUE); 255 } else if ("Uid".equals(prop.getType())) 256 prop.setType("xsd:string"); 257 else if ("Code".equals(prop.getType())) 258 prop.setType("xsd:token"); 259 else if ("Decimal".equals(prop.getType())) 260 prop.setType("xsd:decimal"); 261 else 262 throw new Error("Unknown type " + prop.getType() + " on " + dt.getName() + "." + prop.getName()); 263 } 264 prop.setMin("optional".equals(attr.getAttribute("use")) ? 0 : 1); 265 prop.setMax(1); 266 prop.setDoco(getDoco(attr)); 267 prop.setIsattr(true); 268 dt.getProperties().add(prop); 269 System.out.println(" " + prop.getName() + " : " + prop.getType() + " [" + prop.getMin() + ".." + prop.getMax() + "]"); 270 } 271 272 private void processEnums() { 273 Element type = XMLUtil.getFirstChild(schema); 274 while (type != null) { 275 if (type.getTagName().equals("xsd:simpleType")) { 276 Element res = XMLUtil.getFirstChild(type); 277 Element en = XMLUtil.getFirstChild(res); 278 if (en != null && en.getTagName().equals("xsd:enumeration") && !type.getAttribute("name").contains(".")) 279 processEnum(type.getAttribute("name"), en); 280 } 281 type = XMLUtil.getNextSibling(type); 282 } 283 } 284 285 private void processEnum(String n, Element en) { 286 EnumValueSet vs = new EnumValueSet(); 287 bindings.put(n, vs); 288 vs.setName(n); 289 String v3n; 290 if (n.contains("EntityName")) 291 v3n = n + "R2"; 292 else if (n.equals("Compression")) 293 v3n = "CompressionAlgorithm"; 294 else if (n.equals("UpdateMode")) 295 v3n = "HL7UpdateMode"; 296 else if (n.equals("UncertaintyType")) 297 v3n = "ProbabilityDistributionType"; 298 else if (n.equals("TelecommunicationAddressUse") || n.equals("PostalAddressUse")) 299 v3n = "AddressUse"; 300 else if (n.equals("TelecommunicationCapability")) 301 v3n = "TelecommunicationCapabilities"; 302 else 303 v3n = n; 304 vs.setSystem("http://hl7.org/fhir/v3-" + v3n); 305 vs.setTemplate("http://hl7.org/fhir/ValueSet/v3-" + v3n); 306 System.out.println("Enum: " + n + " == " + vs.getSystem()); 307 while (en != null) { 308 vs.getCodes().add(en.getAttribute("value")); 309 vs.getMembers().put(en.getAttribute("value"), getDoco(en)); 310 en = XMLUtil.getNextSibling(en); 311 } 312 } 313 314 private String getDoco(Element en) { 315 Element doco = XMLUtil.getNamedChild(XMLUtil.getNamedChild(en, "xsd:annotation"), "xsd:documentation"); 316 return doco == null ? null : doco.getTextContent(); 317 } 318 319 private void load() throws ParserConfigurationException, SAXException, IOException { 320 DocumentBuilderFactory factory = XMLUtil.newXXEProtectedDocumentBuilderFactory(); 321 factory.setNamespaceAware(false); 322 DocumentBuilder builder = factory.newDocumentBuilder(); 323 Document doc = builder.parse(ManagedFileAccess.inStream("C:\\work\\projects\\org.hl7.v3.dt\\iso\\iso-21090-datatypes.xsd")); 324 schema = doc.getDocumentElement(); 325 } 326 327}