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}