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.instance.utilities.xml; 030 031import java.io.ByteArrayInputStream; 032import java.io.IOException; 033import java.io.InputStream; 034import java.io.InputStreamReader; 035import java.util.List; 036 037import javax.xml.parsers.DocumentBuilder; 038import javax.xml.parsers.DocumentBuilderFactory; 039 040import org.hl7.fhir.instance.utilities.Utilities; 041import org.w3c.dom.Document; 042import org.w3c.dom.Element; 043import org.w3c.dom.Node; 044import org.w3c.dom.ls.DOMImplementationLS; 045import org.w3c.dom.ls.LSSerializer; 046 047public class XMLUtil { 048 049 public static final String SPACE_CHAR = "\u00A0"; 050 051 public static boolean isNMToken(String name) { 052 if (name == null) 053 return false; 054 for (int i = 0; i < name.length(); i++) 055 if (!isNMTokenChar(name.charAt(i))) 056 return false; 057 return name.length() > 0; 058 } 059 060 public static boolean isNMTokenChar(char c) { 061 return isLetter(c) || isDigit(c) || c == '.' || c == '-' || c == '_' || c == ':' || isCombiningChar(c) || isExtender(c); 062 } 063 064 private static boolean isDigit(char c) { 065 return (c >= '\u0030' && c <= '\u0039') || (c >= '\u0660' && c <= '\u0669') || (c >= '\u06F0' && c <= '\u06F9') || 066 (c >= '\u0966' && c <= '\u096F') || (c >= '\u09E6' && c <= '\u09EF') || (c >= '\u0A66' && c <= '\u0A6F') || 067 (c >= '\u0AE6' && c <= '\u0AEF') || (c >= '\u0B66' && c <= '\u0B6F') || (c >= '\u0BE7' && c <= '\u0BEF') || 068 (c >= '\u0C66' && c <= '\u0C6F') || (c >= '\u0CE6' && c <= '\u0CEF') || (c >= '\u0D66' && c <= '\u0D6F') || 069 (c >= '\u0E50' && c <= '\u0E59') || (c >= '\u0ED0' && c <= '\u0ED9') || (c >= '\u0F20' && c <= '\u0F29'); 070 } 071 072 private static boolean isCombiningChar(char c) { 073 return (c >= '\u0300' && c <= '\u0345') || (c >= '\u0360' && c <= '\u0361') || (c >= '\u0483' && c <= '\u0486') || 074 (c >= '\u0591' && c <= '\u05A1') || (c >= '\u05A3' && c <= '\u05B9') || (c >= '\u05BB' && c <= '\u05BD') || 075 c == '\u05BF' || (c >= '\u05C1' && c <= '\u05C2') || c == '\u05C4' || (c >= '\u064B' && c <= '\u0652') || 076 c == '\u0670' || (c >= '\u06D6' && c <= '\u06DC') || (c >= '\u06DD' && c <= '\u06DF') || (c >= '\u06E0' && c <= '\u06E4') || 077 (c >= '\u06E7' && c <= '\u06E8') || (c >= '\u06EA' && c <= '\u06ED') || (c >= '\u0901' && c <= '\u0903') || c == '\u093C' || 078 (c >= '\u093E' && c <= '\u094C') || c == '\u094D' || (c >= '\u0951' && c <= '\u0954') || (c >= '\u0962' && c <= '\u0963') || 079 (c >= '\u0981' && c <= '\u0983') || c == '\u09BC' || c == '\u09BE' || c == '\u09BF' || (c >= '\u09C0' && c <= '\u09C4') || 080 (c >= '\u09C7' && c <= '\u09C8') || (c >= '\u09CB' && c <= '\u09CD') || c == '\u09D7' || (c >= '\u09E2' && c <= '\u09E3') || 081 c == '\u0A02' || c == '\u0A3C' || c == '\u0A3E' || c == '\u0A3F' || (c >= '\u0A40' && c <= '\u0A42') || 082 (c >= '\u0A47' && c <= '\u0A48') || (c >= '\u0A4B' && c <= '\u0A4D') || (c >= '\u0A70' && c <= '\u0A71') || 083 (c >= '\u0A81' && c <= '\u0A83') || c == '\u0ABC' || (c >= '\u0ABE' && c <= '\u0AC5') || (c >= '\u0AC7' && c <= '\u0AC9') || 084 (c >= '\u0ACB' && c <= '\u0ACD') || (c >= '\u0B01' && c <= '\u0B03') || c == '\u0B3C' || (c >= '\u0B3E' && c <= '\u0B43') || 085 (c >= '\u0B47' && c <= '\u0B48') || (c >= '\u0B4B' && c <= '\u0B4D') || (c >= '\u0B56' && c <= '\u0B57') || 086 (c >= '\u0B82' && c <= '\u0B83') || (c >= '\u0BBE' && c <= '\u0BC2') || (c >= '\u0BC6' && c <= '\u0BC8') || 087 (c >= '\u0BCA' && c <= '\u0BCD') || c == '\u0BD7' || (c >= '\u0C01' && c <= '\u0C03') || (c >= '\u0C3E' && c <= '\u0C44') || 088 (c >= '\u0C46' && c <= '\u0C48') || (c >= '\u0C4A' && c <= '\u0C4D') || (c >= '\u0C55' && c <= '\u0C56') || 089 (c >= '\u0C82' && c <= '\u0C83') || (c >= '\u0CBE' && c <= '\u0CC4') || (c >= '\u0CC6' && c <= '\u0CC8') || 090 (c >= '\u0CCA' && c <= '\u0CCD') || (c >= '\u0CD5' && c <= '\u0CD6') || (c >= '\u0D02' && c <= '\u0D03') || 091 (c >= '\u0D3E' && c <= '\u0D43') || (c >= '\u0D46' && c <= '\u0D48') || (c >= '\u0D4A' && c <= '\u0D4D') || c == '\u0D57' || 092 c == '\u0E31' || (c >= '\u0E34' && c <= '\u0E3A') || (c >= '\u0E47' && c <= '\u0E4E') || c == '\u0EB1' || 093 (c >= '\u0EB4' && c <= '\u0EB9') || (c >= '\u0EBB' && c <= '\u0EBC') || (c >= '\u0EC8' && c <= '\u0ECD') || 094 (c >= '\u0F18' && c <= '\u0F19') || c == '\u0F35' || c == '\u0F37' || c == '\u0F39' || c == '\u0F3E' || c == '\u0F3F' || 095 (c >= '\u0F71' && c <= '\u0F84') || (c >= '\u0F86' && c <= '\u0F8B') || (c >= '\u0F90' && c <= '\u0F95') || c == '\u0F97' || 096 (c >= '\u0F99' && c <= '\u0FAD') || (c >= '\u0FB1' && c <= '\u0FB7') || c == '\u0FB9' || (c >= '\u20D0' && c <= '\u20DC') || 097 c == '\u20E1' || (c >= '\u302A' && c <= '\u302F') || c == '\u3099' || c == '\u309A'; 098 } 099 100 private static boolean isExtender(char c) { 101 return c == '\u00B7' || c == '\u02D0' || c == '\u02D1' || c == '\u0387' || c == '\u0640' || c == '\u0E46' || 102 c == '\u0EC6' || c == '\u3005' || (c >= '\u3031' && c <= '\u3035') || (c >= '\u309D' && c <= '\u309E') || 103 (c >= '\u30FC' && c <= '\u30FE'); 104 } 105 106 private static boolean isLetter(char c) { 107 return isBaseChar(c) || isIdeographic(c); 108 } 109 110 private static boolean isBaseChar(char c) { 111 return (c >= '\u0041' && c <= '\u005A') || (c >= '\u0061' && c <= '\u007A') || (c >= '\u00C0' && c <= '\u00D6') || 112 (c >= '\u00D8' && c <= '\u00F6') || (c >= '\u00F8' && c <= '\u00FF') || (c >= '\u0100' && c <= '\u0131') || 113 (c >= '\u0134' && c <= '\u013E') || (c >= '\u0141' && c <= '\u0148') || (c >= '\u014A' && c <= '\u017E') || 114 (c >= '\u0180' && c <= '\u01C3') || (c >= '\u01CD' && c <= '\u01F0') || (c >= '\u01F4' && c <= '\u01F5') || 115 (c >= '\u01FA' && c <= '\u0217') || (c >= '\u0250' && c <= '\u02A8') || (c >= '\u02BB' && c <= '\u02C1') || 116 c == '\u0386' || (c >= '\u0388' && c <= '\u038A') || c == '\u038C' || (c >= '\u038E' && c <= '\u03A1') || 117 (c >= '\u03A3' && c <= '\u03CE') || (c >= '\u03D0' && c <= '\u03D6') || c == '\u03DA' || c == '\u03DC' || c == '\u03DE' || 118 c == '\u03E0' || (c >= '\u03E2' && c <= '\u03F3') || (c >= '\u0401' && c <= '\u040C') || (c >= '\u040E' && c <= '\u044F') || 119 (c >= '\u0451' && c <= '\u045C') || (c >= '\u045E' && c <= '\u0481') || (c >= '\u0490' && c <= '\u04C4') || 120 (c >= '\u04C7' && c <= '\u04C8') || (c >= '\u04CB' && c <= '\u04CC') || (c >= '\u04D0' && c <= '\u04EB') || 121 (c >= '\u04EE' && c <= '\u04F5') || (c >= '\u04F8' && c <= '\u04F9') || (c >= '\u0531' && c <= '\u0556') || 122 c == '\u0559' || (c >= '\u0561' && c <= '\u0586') || (c >= '\u05D0' && c <= '\u05EA') || (c >= '\u05F0' && c <= '\u05F2') || 123 (c >= '\u0621' && c <= '\u063A') || (c >= '\u0641' && c <= '\u064A') || (c >= '\u0671' && c <= '\u06B7') || 124 (c >= '\u06BA' && c <= '\u06BE') || (c >= '\u06C0' && c <= '\u06CE') || (c >= '\u06D0' && c <= '\u06D3') || 125 c == '\u06D5' || (c >= '\u06E5' && c <= '\u06E6') || (c >= '\u0905' && c <= '\u0939') || c == '\u093D' || 126 (c >= '\u0958' && c <= '\u0961') || (c >= '\u0985' && c <= '\u098C') || (c >= '\u098F' && c <= '\u0990') || 127 (c >= '\u0993' && c <= '\u09A8') || (c >= '\u09AA' && c <= '\u09B0') || c == '\u09B2' || 128 (c >= '\u09B6' && c <= '\u09B9') || (c >= '\u09DC' && c <= '\u09DD') || (c >= '\u09DF' && c <= '\u09E1') || 129 (c >= '\u09F0' && c <= '\u09F1') || (c >= '\u0A05' && c <= '\u0A0A') || (c >= '\u0A0F' && c <= '\u0A10') || 130 (c >= '\u0A13' && c <= '\u0A28') || (c >= '\u0A2A' && c <= '\u0A30') || (c >= '\u0A32' && c <= '\u0A33') || 131 (c >= '\u0A35' && c <= '\u0A36') || (c >= '\u0A38' && c <= '\u0A39') || (c >= '\u0A59' && c <= '\u0A5C') || 132 c == '\u0A5E' || (c >= '\u0A72' && c <= '\u0A74') || (c >= '\u0A85' && c <= '\u0A8B') || c == '\u0A8D' || 133 (c >= '\u0A8F' && c <= '\u0A91') || (c >= '\u0A93' && c <= '\u0AA8') || (c >= '\u0AAA' && c <= '\u0AB0') || 134 (c >= '\u0AB2' && c <= '\u0AB3') || (c >= '\u0AB5' && c <= '\u0AB9') || c == '\u0ABD' || c == '\u0AE0' || 135 (c >= '\u0B05' && c <= '\u0B0C') || (c >= '\u0B0F' && c <= '\u0B10') || (c >= '\u0B13' && c <= '\u0B28') || 136 (c >= '\u0B2A' && c <= '\u0B30') || (c >= '\u0B32' && c <= '\u0B33') || (c >= '\u0B36' && c <= '\u0B39') || 137 c == '\u0B3D' || (c >= '\u0B5C' && c <= '\u0B5D') || (c >= '\u0B5F' && c <= '\u0B61') || 138 (c >= '\u0B85' && c <= '\u0B8A') || (c >= '\u0B8E' && c <= '\u0B90') || (c >= '\u0B92' && c <= '\u0B95') || 139 (c >= '\u0B99' && c <= '\u0B9A') || c == '\u0B9C' || (c >= '\u0B9E' && c <= '\u0B9F') || 140 (c >= '\u0BA3' && c <= '\u0BA4') || (c >= '\u0BA8' && c <= '\u0BAA') || (c >= '\u0BAE' && c <= '\u0BB5') || 141 (c >= '\u0BB7' && c <= '\u0BB9') || (c >= '\u0C05' && c <= '\u0C0C') || (c >= '\u0C0E' && c <= '\u0C10') || 142 (c >= '\u0C12' && c <= '\u0C28') || (c >= '\u0C2A' && c <= '\u0C33') || (c >= '\u0C35' && c <= '\u0C39') || 143 (c >= '\u0C60' && c <= '\u0C61') || (c >= '\u0C85' && c <= '\u0C8C') || (c >= '\u0C8E' && c <= '\u0C90') || 144 (c >= '\u0C92' && c <= '\u0CA8') || (c >= '\u0CAA' && c <= '\u0CB3') || (c >= '\u0CB5' && c <= '\u0CB9') || 145 c == '\u0CDE' || (c >= '\u0CE0' && c <= '\u0CE1') || (c >= '\u0D05' && c <= '\u0D0C') || 146 (c >= '\u0D0E' && c <= '\u0D10') || (c >= '\u0D12' && c <= '\u0D28') || (c >= '\u0D2A' && c <= '\u0D39') || 147 (c >= '\u0D60' && c <= '\u0D61') || (c >= '\u0E01' && c <= '\u0E2E') || c == '\u0E30' || 148 (c >= '\u0E32' && c <= '\u0E33') || (c >= '\u0E40' && c <= '\u0E45') || (c >= '\u0E81' && c <= '\u0E82') || 149 c == '\u0E84' || (c >= '\u0E87' && c <= '\u0E88') || c == '\u0E8A' || c == '\u0E8D' || (c >= '\u0E94' && c <= '\u0E97') || 150 (c >= '\u0E99' && c <= '\u0E9F') || (c >= '\u0EA1' && c <= '\u0EA3') || c == '\u0EA5' || c == '\u0EA7' || 151 (c >= '\u0EAA' && c <= '\u0EAB') || (c >= '\u0EAD' && c <= '\u0EAE') || c == '\u0EB0' || 152 (c >= '\u0EB2' && c <= '\u0EB3') || c == '\u0EBD' || (c >= '\u0EC0' && c <= '\u0EC4') || 153 (c >= '\u0F40' && c <= '\u0F47') || (c >= '\u0F49' && c <= '\u0F69') || (c >= '\u10A0' && c <= '\u10C5') || 154 (c >= '\u10D0' && c <= '\u10F6') || c == '\u1100' || (c >= '\u1102' && c <= '\u1103') || 155 (c >= '\u1105' && c <= '\u1107') || c == '\u1109' || (c >= '\u110B' && c <= '\u110C') || 156 (c >= '\u110E' && c <= '\u1112') || c == '\u113C' || c == '\u113E' || c == '\u1140' || c == '\u114C' || 157 c == '\u114E' || c == '\u1150' || (c >= '\u1154' && c <= '\u1155') || c == '\u1159' || 158 (c >= '\u115F' && c <= '\u1161') || c == '\u1163' || c == '\u1165' || c == '\u1167' || c == '\u1169' || 159 (c >= '\u116D' && c <= '\u116E') || (c >= '\u1172' && c <= '\u1173') || c == '\u1175' || 160 c == '\u119E' || c == '\u11A8' || c == '\u11AB' || (c >= '\u11AE' && c <= '\u11AF') || 161 (c >= '\u11B7' && c <= '\u11B8') || c == '\u11BA' || (c >= '\u11BC' && c <= '\u11C2') || 162 c == '\u11EB' || c == '\u11F0' || c == '\u11F9' || (c >= '\u1E00' && c <= '\u1E9B') || (c >= '\u1EA0' && c <= '\u1EF9') || 163 (c >= '\u1F00' && c <= '\u1F15') || (c >= '\u1F18' && c <= '\u1F1D') || (c >= '\u1F20' && c <= '\u1F45') || 164 (c >= '\u1F48' && c <= '\u1F4D') || (c >= '\u1F50' && c <= '\u1F57') || c == '\u1F59' || c == '\u1F5B' || c == '\u1F5D' || 165 (c >= '\u1F5F' && c <= '\u1F7D') || (c >= '\u1F80' && c <= '\u1FB4') || (c >= '\u1FB6' && c <= '\u1FBC') || 166 c == '\u1FBE' || (c >= '\u1FC2' && c <= '\u1FC4') || (c >= '\u1FC6' && c <= '\u1FCC') || 167 (c >= '\u1FD0' && c <= '\u1FD3') || (c >= '\u1FD6' && c <= '\u1FDB') || (c >= '\u1FE0' && c <= '\u1FEC') || 168 (c >= '\u1FF2' && c <= '\u1FF4') || (c >= '\u1FF6' && c <= '\u1FFC') || c == '\u2126' || 169 (c >= '\u212A' && c <= '\u212B') || c == '\u212E' || (c >= '\u2180' && c <= '\u2182') || 170 (c >= '\u3041' && c <= '\u3094') || (c >= '\u30A1' && c <= '\u30FA') || (c >= '\u3105' && c <= '\u312C') || 171 (c >= '\uAC00' && c <= '\uD7A3'); 172 } 173 174 private static boolean isIdeographic(char c) { 175 return (c >= '\u4E00' && c <= '\u9FA5') || c == '\u3007' || (c >= '\u3021' && c <= '\u3029'); 176 } 177 178 public static String determineEncoding(InputStream stream) throws IOException { 179 stream.mark(20000); 180 try { 181 int b0 = stream.read(); 182 int b1 = stream.read(); 183 int b2 = stream.read(); 184 int b3 = stream.read(); 185 186 if (b0 == 0xFE && b1 == 0xFF) 187 return "UTF-16BE"; 188 else if (b0 == 0xFF && b1 == 0xFE) 189 return "UTF-16LE"; 190 else if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF ) 191 return "UTF-8"; 192 else if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) 193 return "UTF-16BE"; 194 else if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) 195 return "UTF-16LE"; 196 else if (b0 == 0x3C && b1 == 0x3F && b2 == 0x78 && b3 == 0x6D) { 197// UTF-8, ISO 646, ASCII, some part of ISO 8859, Shift-JIS, EUC, or any other 7-bit, 8-bit, or mixed-width encoding 198// which ensures that the characters of ASCII have their normal positions, width, and values; the actual encoding 199// declaration must be read to detect which of these applies, but since all of these encodings use the same bit patterns 200// for the relevant ASCII characters, the encoding declaration itself may be read reliably 201 InputStreamReader rdr = new InputStreamReader(stream, "US-ASCII"); 202 String hdr = readFirstLine(rdr); 203 return extractEncoding(hdr); 204 } else 205 return null; 206 } finally { 207 stream.reset(); 208 } 209 } 210 211 private static String extractEncoding(String hdr) { 212 int i = hdr.indexOf("encoding="); 213 if (i == -1) 214 return null; 215 hdr = hdr.substring(i+9); 216 char sep = hdr.charAt(0); 217 hdr = hdr.substring(1); 218 i = hdr.indexOf(sep); 219 if (i == -1) 220 return null; 221 return hdr.substring(0, i); 222 } 223 224 private static String readFirstLine(InputStreamReader rdr) throws IOException { 225 char[] buf = new char[1]; 226 StringBuffer bldr = new StringBuffer(); 227 rdr.read(buf); 228 while (buf[0] != '>') { 229 bldr.append(buf[0]); 230 rdr.read(buf); 231 } 232 return bldr.toString(); 233 } 234 235 236 public static boolean charSetImpliesAscii(String charset) { 237 return charset.equals("ISO-8859-1") || charset.equals("US-ASCII"); 238 } 239 240 241 /** 242 * Converts the raw characters to XML escapeUrlParam characters. 243 * 244 * @param rawContent 245 * @param charset Null when charset is not known, so we assume it's unicode 246 * @param isNoLines 247 * @return escapeUrlParam string 248 */ 249 public static String escapeXML(String rawContent, String charset, boolean isNoLines) { 250 if (rawContent == null) 251 return ""; 252 else { 253 StringBuffer sb = new StringBuffer(); 254 255 for (int i = 0; i < rawContent.length(); i++) { 256 char ch = rawContent.charAt(i); 257 if (ch == '\'') 258 sb.append("'"); 259 else if (ch == '&') 260 sb.append("&"); 261 else if (ch == '"') 262 sb.append("""); 263 else if (ch == '<') 264 sb.append("<"); 265 else if (ch == '>') 266 sb.append(">"); 267 else if (ch > '~' && charset != null && charSetImpliesAscii(charset)) 268 // TODO - why is hashcode the only way to get the unicode number for the character 269 // in jre 5.0? 270 sb.append("&#x"+Integer.toHexString(new Character(ch).hashCode()).toUpperCase()+";"); 271 else if (isNoLines) { 272 if (ch == '\r') 273 sb.append("
"); 274 else if (ch != '\n') 275 sb.append(ch); 276 } 277 else 278 sb.append(ch); 279 } 280 return sb.toString(); 281 } 282 } 283 284 public static Element getFirstChild(Element e) { 285 if (e == null) 286 return null; 287 Node n = e.getFirstChild(); 288 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 289 n = n.getNextSibling(); 290 return (Element) n; 291 } 292 293 public static Element getNamedChild(Element e, String name) { 294 Element c = getFirstChild(e); 295 while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) 296 c = getNextSibling(c); 297 return c; 298 } 299 300 public static Element getNextSibling(Element e) { 301 Node n = e.getNextSibling(); 302 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 303 n = n.getNextSibling(); 304 return (Element) n; 305 } 306 307 public static void getNamedChildren(Element e, String name, List<Element> set) { 308 Element c = getFirstChild(e); 309 while (c != null) { 310 if (name.equals(c.getLocalName()) || name.equals(c.getNodeName()) ) 311 set.add(c); 312 c = getNextSibling(c); 313 } 314 } 315 316 public static String htmlToXmlEscapedPlainText(Element r) { 317 StringBuilder s = new StringBuilder(); 318 Node n = r.getFirstChild(); 319 boolean ws = false; 320 while (n != null) { 321 if (n.getNodeType() == Node.TEXT_NODE) { 322 String t = n.getTextContent().trim(); 323 if (Utilities.noString(t)) 324 ws = true; 325 else { 326 if (ws) 327 s.append(" "); 328 ws = false; 329 s.append(t); 330 } 331 } 332 if (n.getNodeType() == Node.ELEMENT_NODE) { 333 if (ws) 334 s.append(" "); 335 ws = false; 336 s.append(htmlToXmlEscapedPlainText((Element) n)); 337 if (r.getNodeName().equals("br") || r.getNodeName().equals("p")) 338 s.append("\r\n"); 339 } 340 n = n.getNextSibling(); 341 } 342 return s.toString(); 343 } 344 345 public static String htmlToXmlEscapedPlainText(String definition) throws Exception { 346 return htmlToXmlEscapedPlainText(parseToDom("<div>"+definition+"</div>").getDocumentElement()); 347 } 348 349 public static String elementToString(Element el) { 350 if (el == null) 351 return ""; 352 Document document = el.getOwnerDocument(); 353 DOMImplementationLS domImplLS = (DOMImplementationLS) document 354 .getImplementation(); 355 LSSerializer serializer = domImplLS.createLSSerializer(); 356 return serializer.writeToString(el); 357 } 358 359 public static String getNamedChildValue(Element element, String name) { 360 Element e = getNamedChild(element, name); 361 return e == null ? null : e.getAttribute("value"); 362 } 363 364 public static void getNamedChildrenWithWildcard(Element focus, String name, List<Element> children) { 365 Element c = getFirstChild(focus); 366 while (c != null) { 367 String n = c.getLocalName() != null ? c.getLocalName() : c.getNodeName(); 368 if (name.equals(n) || (name.endsWith("[x]") && n.startsWith(name.substring(0, name.length()-3)))) 369 children.add(c); 370 c = getNextSibling(c); 371 } 372 373 } 374 375 public static boolean hasNamedChild(Element e, String name) { 376 Element c = getFirstChild(e); 377 while (c != null && !name.equals(c.getLocalName()) && !name.equals(c.getNodeName())) 378 c = getNextSibling(c); 379 return c != null; 380 } 381 382 public static Document parseToDom(String content) throws Exception { 383 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 384 factory.setNamespaceAware(false); 385 DocumentBuilder builder = factory.newDocumentBuilder(); 386 return builder.parse(new ByteArrayInputStream(content.getBytes())); 387 } 388 389 public static Element getLastChild(Element e) { 390 if (e == null) 391 return null; 392 Node n = e.getLastChild(); 393 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 394 n = n.getPreviousSibling(); 395 return (Element) n; 396 } 397 398 public static Element getPrevSibling(Element e) { 399 Node n = e.getPreviousSibling(); 400 while (n != null && n.getNodeType() != Node.ELEMENT_NODE) 401 n = n.getPreviousSibling(); 402 return (Element) n; 403 } 404 405 406}