001package org.hl7.fhir.convertors.misc;
002
003
004
005
006
007/*
008  Copyright (c) 2011+, HL7, Inc.
009  All rights reserved.
010  
011  Redistribution and use in source and binary forms, with or without modification, 
012  are permitted provided that the following conditions are met:
013  
014   * Redistributions of source code must retain the above copyright notice, this 
015     list of conditions and the following disclaimer.
016   * Redistributions in binary form must reproduce the above copyright notice, 
017     this list of conditions and the following disclaimer in the documentation 
018     and/or other materials provided with the distribution.
019   * Neither the name of HL7 nor the names of its contributors may be used to 
020     endorse or promote products derived from this software without specific 
021     prior written permission.
022  
023  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
024  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
025  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
026  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
027  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
028  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
029  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
030  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
031  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
032  POSSIBILITY OF SUCH DAMAGE.
033  
034*/
035
036import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
037import org.hl7.fhir.utilities.xml.XMLUtil;
038import org.w3c.dom.Document;
039import org.w3c.dom.Element;
040import org.w3c.dom.Node;
041
042import javax.xml.parsers.DocumentBuilder;
043import javax.xml.parsers.DocumentBuilderFactory;
044import java.io.InputStream;
045import java.util.ArrayList;
046import java.util.List;
047
048public class CDAUtilities {
049
050  private final Document doc;
051
052  public CDAUtilities(InputStream stream) throws Exception {
053    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
054    factory.setNamespaceAware(true);
055    DocumentBuilder builder = factory.newDocumentBuilder();
056
057    doc = builder.parse(stream);
058    basicChecks();
059  }
060
061  private void basicChecks() throws Exception {
062    Element e = doc.getDocumentElement();
063    rule(e.getNamespaceURI().equals("urn:hl7-org:v3"), "CDA namespace must be ");
064    rule(e.getNodeName().equals("ClinicalDocument"), "CDA root name must be ClinicalDocument");
065
066  }
067
068  private void rule(boolean test, String message) throws Exception {
069    if (!test)
070      throw new Exception(message);
071
072  }
073
074  public Element getElement() {
075    return doc.getDocumentElement();
076  }
077
078  public void checkTemplateId(Element e, String templateId) throws Exception {
079    rule(hasTemplateId(e, templateId), "Template Id '" + templateId + "' not found");
080
081  }
082
083  public Element getChild(Element e, String[] names) throws Exception {
084    for (String n : names) {
085      if (e == null)
086        return null;
087      e = getChild(e, n);
088    }
089    return e;
090  }
091
092  public Element getChild(Element element, String name) throws Exception {
093    if (element == null)
094      return null;
095
096    Element e = null;
097    Node n = element.getFirstChild();
098    while (n != null) {
099      if (n.getNodeType() == Node.ELEMENT_NODE && n.getNodeName().equals(name)) {
100        if (e == null) {
101          e = (Element) n;
102        } else {
103          throw new Exception("multiple matches found for " + name);
104        }
105      }
106      n = n.getNextSibling();
107    }
108    return e;
109  }
110
111  public Element getChildByAttribute(Element element, String name, String attrname, String value) throws Exception {
112    if (element == null)
113      return null;
114
115    Element e = null;
116    Node n = element.getFirstChild();
117    while (n != null) {
118      if (n.getNodeType() == Node.ELEMENT_NODE && n.getNodeName().equals(name) && value.equals(((Element) n).getAttribute(attrname))) {
119        if (e == null) {
120          e = (Element) n;
121        } else {
122          throw new Exception("multiple matches found for " + name);
123        }
124      }
125      n = n.getNextSibling();
126    }
127    return e;
128  }
129
130
131  public List<Element> getChildren(Element element, String name) {
132    List<Element> l = new ArrayList<Element>();
133    if (element != null) {
134      Node n = element.getFirstChild();
135      while (n != null) {
136        if (n.getNodeType() == Node.ELEMENT_NODE && n.getNodeName().equals(name)) {
137          l.add((Element) n);
138        }
139        n = n.getNextSibling();
140      }
141    }
142    return l;
143  }
144
145  public Element getDescendent(Element element, String path) throws Exception {
146    String[] p = path.split("\\/");
147    return getDescendent(element, p);
148  }
149
150  public Element getDescendent(Element e, String[] path) throws Exception {
151    for (String n : path) {
152      if (e == null)
153        return e;
154      e = getChild(e, n);
155    }
156    return e;
157  }
158
159  public boolean hasTemplateId(Element e, String tid) {
160    if (e == null)
161      return false;
162    boolean found = false;
163    Node n = e.getFirstChild();
164    while (n != null && !found) {
165      if (n.getNodeType() == Node.ELEMENT_NODE && n.getNodeName().equals("templateId") && tid.equals(((Element) n).getAttribute("root")))
166        found = true;
167      n = n.getNextSibling();
168    }
169    return found;
170  }
171
172  public String getStatus(Element act) throws Exception {
173    if (act == null)
174      return null;
175    Element sc = getChild(act, "statusCode");
176    if (sc == null)
177      return null;
178    else
179      return sc.getAttribute("code");
180  }
181
182  public String getSeverity(Element observation) throws Exception {
183    for (Element e : getChildren(observation, "entryRelationship")) {
184      Element child = getChild(e, "observation");
185      if (hasTemplateId(child, "2.16.840.1.113883.10.20.22.4.8"))
186        return getChild(child, "value").getAttribute("code");
187    }
188    return null;
189  }
190
191  public String showTemplateIds(Element element) {
192    List<Element> list = getChildren(element, "templateId");
193    CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
194    for (Element e : list) {
195      if (e.hasAttribute("extension"))
196        b.append(e.getAttribute("root") + "::" + e.getAttribute("extension"));
197      else
198        b.append(e.getAttribute("root"));
199    }
200    return b.toString();
201  }
202
203  public Element getlastChild(Element e) {
204    Node n = e.getLastChild();
205    while (n != null && n.getNodeType() != Node.ELEMENT_NODE)
206      n = n.getPreviousSibling();
207    return n == null ? null : (Element) n;
208  }
209
210  /**
211   * This method looks up an object by it's id, and only returns it if has a child by the given name
212   * (resolving identifier based cross references)
213   *
214   * @param id
215   * @param childName
216   * @return
217   * @throws Exception
218   */
219  public Element getById(Element id, String childName) throws Exception {
220    return getById(doc.getDocumentElement(), id, childName);
221  }
222
223  private Element getById(Element e, Element id, String childName) throws Exception {
224    Element c = XMLUtil.getFirstChild(e);
225    while (c != null) {
226      Element i = getChild(c, "id");
227      if (i != null && matchesAsId(i, id) && getChild(c, childName) != null)
228        return c;
229      Element m = getById(c, id, childName);
230      if (m != null)
231        return m;
232      c = XMLUtil.getNextSibling(c);
233    }
234    return null;
235  }
236
237  private boolean matchesAsId(Element i1, Element i2) {
238    String r1 = i1.getAttribute("root");
239    String r2 = i2.getAttribute("root");
240    String e1 = i1.getAttribute("extension");
241    String e2 = i2.getAttribute("extension");
242    return (r1 != null && r1.equals(r2)) && ((e1 == null && e2 == null) || (e1 != null && e1.equals(e2)));
243  }
244
245  public Element getByXmlId(String id) {
246    return getByXmlId(doc.getDocumentElement(), id);
247  }
248
249  private Element getByXmlId(Element e, String value) {
250    Element c = XMLUtil.getFirstChild(e);
251    while (c != null) {
252      String id = c.getAttribute("ID");
253      if (id != null && id.equals(value))
254        return c;
255      Element m = getByXmlId(c, value);
256      if (m != null)
257        return m;
258      c = XMLUtil.getNextSibling(c);
259    }
260    return null;
261  }
262
263}