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