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