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}