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