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}