001/* 002Copyright (c) 2011+, HL7, Inc 003All rights reserved. 004 005Redistribution and use in source and binary forms, with or without modification, 006are permitted provided that the following conditions are met: 007 008 * Redistributions of source code must retain the above copyright notice, this 009 list of conditions and the following disclaimer. 010 * Redistributions in binary form must reproduce the above copyright notice, 011 this list of conditions and the following disclaimer in the documentation 012 and/or other materials provided with the distribution. 013 * Neither the name of HL7 nor the names of its contributors may be used to 014 endorse or promote products derived from this software without specific 015 prior written permission. 016 017THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 018ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 019WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 020IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 021INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 022NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 023PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 024WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 025ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 026POSSIBILITY OF SUCH DAMAGE. 027 028*/ 029package org.hl7.fhir.utilities.xml; 030 031/*- 032 * #%L 033 * org.hl7.fhir.utilities 034 * %% 035 * Copyright (C) 2014 - 2019 Health Level 7 036 * %% 037 * Licensed under the Apache License, Version 2.0 (the "License"); 038 * you may not use this file except in compliance with the License. 039 * You may obtain a copy of the License at 040 * 041 * http://www.apache.org/licenses/LICENSE-2.0 042 * 043 * Unless required by applicable law or agreed to in writing, software 044 * distributed under the License is distributed on an "AS IS" BASIS, 045 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 046 * See the License for the specific language governing permissions and 047 * limitations under the License. 048 * #L% 049 */ 050 051 052import java.io.ByteArrayInputStream; 053import java.io.File; 054import java.io.FileInputStream; 055import java.io.IOException; 056import java.io.InputStream; 057import java.io.InputStreamReader; 058import java.io.OutputStream; 059import java.util.ArrayList; 060import java.util.List; 061import java.util.Set; 062 063import javax.xml.parsers.DocumentBuilder; 064import javax.xml.parsers.DocumentBuilderFactory; 065import javax.xml.parsers.ParserConfigurationException; 066import javax.xml.transform.Result; 067import javax.xml.transform.Source; 068import javax.xml.transform.Transformer; 069import javax.xml.transform.TransformerException; 070import javax.xml.transform.TransformerFactory; 071import javax.xml.transform.dom.DOMSource; 072import javax.xml.transform.stream.StreamResult; 073 074import org.hl7.fhir.exceptions.FHIRException; 075import org.hl7.fhir.utilities.Utilities; 076import org.w3c.dom.Attr; 077import org.w3c.dom.Document; 078import org.w3c.dom.Element; 079import org.w3c.dom.Node; 080import org.w3c.dom.ls.DOMImplementationLS; 081import org.w3c.dom.ls.LSSerializer; 082import org.xml.sax.SAXException; 083 084public class XMLUtil { 085 086 public static final String SPACE_CHAR = "\u00A0"; 087 088 public static boolean isNMToken(String name) { 089 if (name == null) 090 return false; 091 for (int i = 0; i < name.length(); i++) 092 if (!isNMTokenChar(name.charAt(i))) 093 return false; 094 return name.length() > 0; 095 } 096 097 public static boolean isNMTokenChar(char c) { 098 return isLetter(c) || isDigit(c) || c == '.' || c == '-' || c == '_' || c == ':' || isCombiningChar(c) || isExtender(c); 099 } 100 101 private static boolean isDigit(char c) { 102 return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9') || 103 (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F') || 104 (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF') || 105 (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F') || 106 (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29'); 107 } 108 109 private static boolean isCombiningChar(char c) { 110 return (c >= '\u0300' && c <= '\u0345') || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || 111 (c >= '\u0591' && c <= '\u05A1') || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') || 112 c == '\u05BF' || (c >= '\u05C1' && c <= '\u05C2') || c == '\u05C4' || (c >= '\u064B' && c <= '\u0652') || 113 c == '\u0670' || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') || 114 (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') || c == '\u093C' || 115 (c >= '\u093E' && c <= '\u094C') || c == '\u094D' || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || 116 (c >= '\u0981' && c <= '\u0983') || c == '\u09BC' || c == '\u09BE' || c == '\u09BF' || (c >= '\u09C0' && c <= '\u09C4') || 117 (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') || c == '\u09D7' || (c >= '\u09E2' && c <= '\u09E3') || 118 c == '\u0A02' || c == '\u0A3C' || c == '\u0A3E' || c == '\u0A3F' || (c >= '\u0A40' && c <= '\u0A42') || 119 (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') || (c >= '\u0A70' && c <= '\u0A71') || 120 (c >= '\u0A81' && c <= '\u0A83') || c == '\u0ABC' || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || 121 (c >= '\u0ACB' && c <= '\u0ACD') || (c >= '\u0B01' && c <= '\u0B03') || c == '\u0B3C' || (c >= '\u0B3E' && c <= '\u0B43') || 122 (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') || (c >= '\u0B56' && c <= '\u0B57') || 123 (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') || (c >= '\u0BC6' && c <= '\u0BC8') || 124 (c >= '\u0BCA' && c <= '\u0BCD') || c == '\u0BD7' || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || 125 (c >= '\u0C46' && c <= '\u0C48') || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || 126 (c >= '\u0C82' && c <= '\u0C83') || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || 127 (c >= '\u0CCA' && c <= '\u0CCD') || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || 128 (c >= '\u0D3E' && c <= '\u0D43') || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') || c == '\u0D57' || 129 c == '\u0E31' || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') || c == '\u0EB1' || 130 (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') || 131 (c >= '\u0F18' && c <= '\u0F19') || c == '\u0F35' || c == '\u0F37' || c == '\u0F39' || c == '\u0F3E' || c == '\u0F3F' || 132 (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') || c == '\u0F97' || 133 (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') || c == '\u0FB9' || (c >= '\u20D0' && c <= '\u20DC') || 134 c == '\u20E1' || (c >= '\u302A' && c <= '\u302F') || c == '\u3099' || c == '\u309A'; 135 } 136 137 private static boolean isExtender(char c) { 138 return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387' || c == '\u0640' || c == '\u0E46' || 139 c == '\u0EC6' || c == '\u3005' || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || 140 (c >= '\u30FC' && c <= '\u30FE'); 141 } 142 143 private static boolean isLetter(char c) { 144 return isBaseChar(c) || isIdeographic(c); 145 } 146 147 private static boolean isBaseChar(char c) { 148 return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A') || (c >= '\u00C0' && c <= '\u00D6') || 149 (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131') || 150 (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E') || 151 (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5') || 152 (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1') || 153 c == '\u0386' || (c >= '\u0388' && c <= '\u038A') || c == '\u038C' || (c >= '\u038E' && c <= '\u03A1') || 154 (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6') || c == '\u03DA' || c == '\u03DC' || c == '\u03DE' || 155 c == '\u03E0' || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F') || 156 (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4') || 157 (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB') || 158 (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556') || 159 c == '\u0559' || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2') || 160 (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7') || 161 (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3') || 162 c == '\u06D5' || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939') || c == '\u093D' || 163 (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990') || 164 (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0') || c == '\u09B2' || 165 (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DC' && c <= '\u09DD') || (c >= '\u09DF' && c <= '\u09E1') || 166 (c >= '\u09F0' && c <= '\u09F1') || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || 167 (c >= '\u0A13' && c <= '\u0A28') || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || 168 (c >= '\u0A35' && c <= '\u0A36') || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C') || 169 c == '\u0A5E' || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B') || c == '\u0A8D' || 170 (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') || 171 (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') || c == '\u0ABD' || c == '\u0AE0' || 172 (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') || 173 (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') || 174 c == '\u0B3D' || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || 175 (c >= '\u0B85' && c <= '\u0B8A') || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || 176 (c >= '\u0B99' && c <= '\u0B9A') || c == '\u0B9C' || (c >= '\u0B9E' && c <= '\u0B9F') || 177 (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') || (c >= '\u0BAE' && c <= '\u0BB5') || 178 (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') || (c >= '\u0C0E' && c <= '\u0C10') || 179 (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') || (c >= '\u0C35' && c <= '\u0C39') || 180 (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') || (c >= '\u0C8E' && c <= '\u0C90') || 181 (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') || (c >= '\u0CB5' && c <= '\u0CB9') || 182 c == '\u0CDE' || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || 183 (c >= '\u0D0E' && c <= '\u0D10') || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || 184 (c >= '\u0D60' && c <= '\u0D61') || (c >= '\u0E01' && c <= '\u0E2E') || c == '\u0E30' || 185 (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') || 186 c == '\u0E84' || (c >= '\u0E87' && c <= '\u0E88') || c == '\u0E8A' || c == '\u0E8D' || (c >= '\u0E94' && c <= '\u0E97') || 187 (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') || c == '\u0EA5' || c == '\u0EA7' || 188 (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') || c == '\u0EB0' || 189 (c >= '\u0EB2' && c <= '\u0EB3') || c == '\u0EBD' || (c >= '\u0EC0' && c <= '\u0EC4') || 190 (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') || (c >= '\u10A0' && c <= '\u10C5') || 191 (c >= '\u10D0' && c <= '\u10F6') || c == '\u1100' || (c >= '\u1102' && c <= '\u1103') || 192 (c >= '\u1105' && c <= '\u1107') || c == '\u1109' || (c >= '\u110B' && c <= '\u110C') || 193 (c >= '\u110E' && c <= '\u1112') || c == '\u113C' || c == '\u113E' || c == '\u1140' || c == '\u114C' || 194 c == '\u114E' || c == '\u1150' || (c >= '\u1154' && c <= '\u1155') || c == '\u1159' || 195 (c >= '\u115F' && c <= '\u1161') || c == '\u1163' || c == '\u1165' || c == '\u1167' || c == '\u1169' || 196 (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') || c == '\u1175' || 197 c == '\u119E' || c == '\u11A8' || c == '\u11AB' || (c >= '\u11AE' && c <= '\u11AF') || 198 (c >= '\u11B7' && c <= '\u11B8') || c == '\u11BA' || (c >= '\u11BC' && c <= '\u11C2') || 199 c == '\u11EB' || c == '\u11F0' || c == '\u11F9' || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || 200 (c >= '\u1F00' && c <= '\u1F15') || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || 201 (c >= '\u1F48' && c <= '\u1F4D') || (c >= '\u1F50' && c <= '\u1F57') || c == '\u1F59' || c == '\u1F5B' || c == '\u1F5D' || 202 (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') || 203 c == '\u1FBE' || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || 204 (c >= '\u1FD0' && c <= '\u1FD3') || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || 205 (c >= '\u1FF2' && c <= '\u1FF4') || (c >= '\u1FF6' && c <= '\u1FFC') || c == '\u2126' || 206 (c >= '\u212A' && c <= '\u212B') || c == '\u212E' || (c >= '\u2180' && c <= '\u2182') || 207 (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') || (c >= '\u3105' && c <= '\u312C') || 208 (c >= '\uAC00' && c <= '\uD7A3'); 209 } 210 211 private static boolean isIdeographic(char c) { 212 return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029'); 213 } 214 215 public static String determineEncoding(InputStream stream) throws IOException { 216 stream.mark(20000); 217 try { 218 int b0 = stream.read(); 219 int b1 = stream.read(); 220 int b2 = stream.read(); 221 int b3 = stream.read(); 222 223 if (b0 == 0xFE && b1 == 0xFF) 224 return "UTF-16BE"; 225 else if (b0 == 0xFF && b1 == 0xFE) 226 return "UTF-16LE"; 227 else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF ) 228 return "UTF-8"; 229 else if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) 230 return "UTF-16BE"; 231 else if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) 232 return "UTF-16LE"; 233 else if (b0 == 0x3C && b1 == 0x3F && b2 == 0x78 && b3 == 0x6D) { 234// UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC, or any other 7-bit, 8-bit, or mixed-width encoding 235// which ensures that the characters of ASCII have their normal positions, width, and values; the actual encoding 236// declaration must be read to detect which of these applies, but since all of these encodings use the same bit patterns 237// for the relevant ASCII characters, the encoding declaration itself may be read reliably 238 InputStreamReader rdr = new InputStreamReader(stream, "US-ASCII"); 239 String hdr = readFirstLine(rdr); 240 return extractEncoding(hdr); 241 } else 242 return null; 243 } finally { 244 stream.reset(); 245 } 246 } 247 248 private static String extractEncoding(String hdr) { 249 int i = hdr.indexOf("encoding="); 250 if (i == -1) 251 return null; 252 hdr = hdr.substring(i+9); 253 char sep = hdr.charAt(0); 254 hdr = hdr.substring(1); 255 i = hdr.indexOf(sep); 256 if (i == -1) 257 return null; 258 return hdr.substring(0, i); 259 } 260 261 private static String readFirstLine(InputStreamReader rdr) throws IOException { 262 char[] buf = new char[1]; 263 StringBuffer bldr = new StringBuffer(); 264 rdr.read(buf); 265 while (buf[0] != '>') { 266 bldr.append(buf[0]); 267 rdr.read(buf); 268 } 269 return bldr.toString(); 270 } 271 272 273 public static boolean charSetImpliesAscii(String charset) { 274 return charset.equals("ISO-8859-1") || charset.equals("US-ASCII"); 275 } 276 277 278 /** 279 * Converts the raw characters to XML escape characters. 280 * 281 * @param rawContent 282 * @param charset Null when charset is not known, so we assume it's unicode 283 * @param isNoLines 284 * @return escape string 285 */ 286 public static String escapeXML(String rawContent, String charset, boolean isNoLines) { 287 if (rawContent == null) 288 return ""; 289 else { 290 StringBuffer sb = new StringBuffer(); 291 292 for (int i = 0; i < rawContent.length(); i++) { 293 char ch = rawContent.charAt(i); 294 if (ch == '\'') 295 sb.append("'"); 296 else if (ch == '&') 297 sb.append("&"); 298 else if (ch == '"') 299 sb.append("""); 300 else if (ch == '<') 301 sb.append("<"); 302 else if (ch == '>') 303 sb.append(">"); 304 else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) 305 // TODO - why is hashcode the only way to get the unicode number for the character 306 // in jre 5.0? 307 sb.append("&#x"+Integer.toHexString(ch).toUpperCase()+";"); 308 else if (isNoLines) { 309 if (ch == '\r') 310 sb.append("
"); 311 else if (ch != '\n') 312 sb.append(ch); 313 } 314 else 315 sb.append(ch); 316 } 317 return sb.toString(); 318 } 319 } 320 321 public static Element getFirstChild(Element e) { 322 if (e == null) 323 return null; 324 Node n = e.getFirstChild(); 325 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 326 n = n.getNextSibling(); 327 return (Element) n; 328 } 329 330 public static Element getNamedChild(Element e, String name) { 331 Element c = getFirstChild(e); 332 while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) 333 c = getNextSibling(c); 334 return c; 335 } 336 337 public static Element getNamedChildByAttribute(Element e, String name, String nname, String nvalue) { 338 Element c = getFirstChild(e); 339 while (c != null && !((name.equals(c.getLocalName()) || name.equals(c.getNodeName())) && nvalue.equals(c.getAttribute(nname)))) 340 c = getNextSibling(c); 341 return c; 342 } 343 344 public static Element getNextSibling(Element e) { 345 Node n = e.getNextSibling(); 346 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 347 n = n.getNextSibling(); 348 return (Element) n; 349 } 350 351 public static void getNamedChildren(Element e, String name, List<Element> set) { 352 Element c = getFirstChild(e); 353 while (c != null) { 354 if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) 355 set.add(c); 356 c = getNextSibling(c); 357 } 358 } 359 360 public static List<Element> getNamedChildren(Element e, String name) { 361 List<Element> res = new ArrayList<Element>(); 362 Element c = getFirstChild(e); 363 while (c != null) { 364 if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) 365 res.add(c); 366 c = getNextSibling(c); 367 } 368 return res; 369 } 370 371 public static String htmlToXmlEscapedPlainText(Element r) { 372 StringBuilder s = new StringBuilder(); 373 Node n = r.getFirstChild(); 374 boolean ws = false; 375 while (n != null) { 376 if (n.getNodeType() == Node.TEXT_NODE) { 377 String t = n.getTextContent().trim(); 378 if (Utilities.noString(t)) 379 ws = true; 380 else { 381 if (ws) 382 s.append(" "); 383 ws = false; 384 s.append(t); 385 } 386 } 387 if (n.getNodeType() == Node.ELEMENT_NODE) { 388 if (ws) 389 s.append(" "); 390 ws = false; 391 s.append(htmlToXmlEscapedPlainText((Element) n)); 392 if (r.getNodeName().equals("br") || r.getNodeName().equals("p")) 393 s.append("\r\n"); 394 } 395 n = n.getNextSibling(); 396 } 397 return s.toString(); 398 } 399 400 public static String htmlToXmlEscapedPlainText(String definition) throws ParserConfigurationException, SAXException, IOException { 401 return htmlToXmlEscapedPlainText(parseToDom("<div>"+definition+"</div>").getDocumentElement()); 402 } 403 404 public static String elementToString(Element el) { 405 if (el == null) 406 return ""; 407 Document document = el.getOwnerDocument(); 408 DOMImplementationLS domImplLS = (DOMImplementationLS) document 409 .getImplementation(); 410 LSSerializer serializer = domImplLS.createLSSerializer(); 411 return serializer.writeToString(el); 412 } 413 414 public static String getNamedChildValue(Element element, String name) { 415 Element e = getNamedChild(element, name); 416 return e == null ? null : e.getAttribute("value"); 417 } 418 419 public static void setNamedChildValue(Element element, String name, String value) throws FHIRException { 420 Element e = getNamedChild(element, name); 421 if (e == null) 422 throw new FHIRException("unable to find element "+name); 423 e.setAttribute("value", value); 424 } 425 426 427 public static void getNamedChildrenWithWildcard(Element focus, String name, List<Element> children) { 428 Element c = getFirstChild(focus); 429 while (c != null) { 430 String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); 431 if (name.equals(n) || (name.endsWith("[x]") && n.startsWith(name.substring(0, name.length()-3)))) 432 children.add(c); 433 c = getNextSibling(c); 434 } 435 } 436 437 public static void getNamedChildrenWithTails(Element focus, String name, List<Element> children, Set<String> typeTails) { 438 Element c = getFirstChild(focus); 439 while (c != null) { 440 String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); 441 if (n.equals(name) || (!n.equals("responseCode") && (n.startsWith(name) && typeTails.contains(n.substring(name.length()))))) 442 children.add(c); 443 c = getNextSibling(c); 444 } 445 } 446 447 public static boolean hasNamedChild(Element e, String name) { 448 Element c = getFirstChild(e); 449 while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) 450 c = getNextSibling(c); 451 return c != null; 452 } 453 454 public static Document parseToDom(String content) throws ParserConfigurationException, SAXException, IOException { 455 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 456 factory.setNamespaceAware(false); 457 DocumentBuilder builder = factory.newDocumentBuilder(); 458 return builder.parse(new ByteArrayInputStream(content.getBytes())); 459 } 460 461 public static Document parseFileToDom(String filename) throws ParserConfigurationException, SAXException, IOException { 462 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 463 factory.setNamespaceAware(false); 464 DocumentBuilder builder = factory.newDocumentBuilder(); 465 return builder.parse(new FileInputStream(filename)); 466 } 467 468 public static Element getLastChild(Element e) { 469 if (e == null) 470 return null; 471 Node n = e.getLastChild(); 472 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 473 n = n.getPreviousSibling(); 474 return (Element) n; 475 } 476 477 public static Element getPrevSibling(Element e) { 478 Node n = e.getPreviousSibling(); 479 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 480 n = n.getPreviousSibling(); 481 return (Element) n; 482 } 483 484 public static String getNamedChildAttribute(Element element, String name, String aname) { 485 Element e = getNamedChild(element, name); 486 return e == null ? null : e.getAttribute(aname); 487 } 488 489 public static void writeDomToFile(Document doc, String filename) throws TransformerException { 490 TransformerFactory transformerFactory = TransformerFactory.newInstance(); 491 Transformer transformer = transformerFactory.newTransformer(); 492 DOMSource source = new DOMSource(doc); 493 StreamResult streamResult = new StreamResult(new File(filename)); 494 transformer.transform(source, streamResult); 495 } 496 497 public static String getXsiType(org.w3c.dom.Element element) { 498 Attr a = element.getAttributeNodeNS("http://www.w3.org/2001/XMLSchema-instance", "type"); 499 return (a == null ? null : a.getTextContent()); 500 501 } 502 503 public static String getDirectText(org.w3c.dom.Element node) { 504 Node n = node.getFirstChild(); 505 StringBuilder b = new StringBuilder(); 506 while (n != null) { 507 if (n.getNodeType() == Node.TEXT_NODE) 508 b.append(n.getTextContent()); 509 n = n.getNextSibling(); 510 } 511 return b.toString().trim(); 512 } 513 514 public static void deleteByName(Element e, String name) { 515 List<Element> matches = getNamedChildren(e, name); 516 for (Element m : matches) 517 e.removeChild(m); 518 } 519 520 public static void deleteAttr(Element e, String namespaceURI, String localName) { 521 if (e.hasAttributeNS(namespaceURI, localName)) 522 e.removeAttributeNS(namespaceURI, localName); 523 524 } 525 526 public static Node[] children(Element ed) { 527 Node[] res = new Node[ed.getChildNodes().getLength()]; 528 for (int i = 0; i < ed.getChildNodes().getLength(); i++) 529 res[i] = ed.getChildNodes().item(i); 530 return res; 531 } 532 533 public static Element insertChild(Document doc, Element element, String name, String namespace, int indent) { 534 Node node = doc.createTextNode("\n"+Utilities.padLeft("", ' ', indent)); 535 Element child = doc.createElementNS(namespace, name); 536 element.insertBefore(child, element.getFirstChild()); 537 element.insertBefore(node, element.getFirstChild()); 538 return child; 539 } 540 541 public static Element insertChild(Document doc, Element element, String name, String namespace, Node before, int indent) { 542 if (before == null) { 543 Node node = doc.createTextNode("\n"+Utilities.padLeft("", ' ', indent)); 544 element.insertBefore(node, before); 545 } 546 Element child = doc.createElementNS(namespace, name); 547 element.insertBefore(child, before); 548 if (before != null) { 549 Node node = doc.createTextNode("\n"+Utilities.padLeft("", ' ', indent)); 550 element.insertBefore(node, before); 551 } 552 return child; 553 } 554 555 public static void addTextTag(Document doc, Element element, String name, String namespace, String text, int indent) { 556 Node node = doc.createTextNode("\n"+Utilities.padLeft("", ' ', indent)); 557 element.appendChild(node); 558 Element child = doc.createElementNS(namespace, name); 559 element.appendChild(child); 560 child.setAttribute("value", text); 561 } 562 563 public static void saveToFile(Element root, OutputStream stream) throws TransformerException { 564 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 565 Result output = new StreamResult(stream); 566 Source input = new DOMSource(root); 567 568 transformer.transform(input, output); 569 } 570 571 public static void spacer(Document doc, Element element, int indent) { 572 Node node = doc.createTextNode("\n"+Utilities.padLeft("", ' ', indent)); 573 element.appendChild(node); 574 575 } 576 577 public static String getNamedChildText(Element element, String name) { 578 Element e = getNamedChild(element, name); 579 return e == null ? null : e.getTextContent(); 580 } 581 582 583}