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}