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}