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}