1 /* 2 * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package test.auctionportal; 24 25 import java.io.OutputStream; 26 import java.io.OutputStreamWriter; 27 import java.io.PrintWriter; 28 import java.io.UnsupportedEncodingException; 29 import java.util.stream.Collectors; 30 31 import org.xml.sax.Attributes; 32 import org.xml.sax.SAXException; 33 import org.xml.sax.SAXParseException; 34 import org.xml.sax.ext.LexicalHandler; 35 import org.xml.sax.helpers.DefaultHandler; 36 37 /** 38 * A SAX2 event handlers. 39 * This SAX2 ContentHandler receives callback event then print whole document 40 * that is parsed. 41 */ 42 public class XInclHandler extends DefaultHandler implements LexicalHandler { 43 /** 44 * Print writer. 45 */ 46 private final PrintWriter fOut; 47 48 /** 49 * Canonical output. 50 */ 51 private volatile boolean fCanonical; 52 53 /** 54 * Element depth. 55 */ 56 private volatile int fElementDepth; 57 58 /** 59 * Sets whether output is canonical. 60 * 61 * @param canonical if the output is canonical format. 62 */ 63 public void setCanonical(boolean canonical) { 64 fCanonical = canonical; 65 } 66 67 /** 68 * Sets the output stream for printing. 69 * @param stream OutputStream for message output. 70 * @param encoding File encoding for message output. 71 * @throws UnsupportedEncodingException if given encoding is an unsupported 72 * encoding name or invalid encoding name. 73 */ 74 public XInclHandler(OutputStream stream, String encoding) 75 throws UnsupportedEncodingException { 76 // At least set one encoding. 77 if (encoding == null) { 78 encoding = "UTF8"; 79 } 80 81 fOut = new PrintWriter(new OutputStreamWriter(stream, encoding), false); 82 } 83 84 /** 85 * Receive notification of the beginning of the document. Write the start 86 * document tag if it's not canonical mode. 87 * @exception org.xml.sax.SAXException Any SAX exception, possibly 88 * wrapping another exception. 89 */ 90 @Override 91 public void startDocument() throws SAXException { 92 fElementDepth = 0; 93 94 if (!fCanonical) { 95 writeFlush("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); 96 } 97 } 98 99 /** 100 * Receive notification of a processing instruction. 101 * @param target The processing instruction target. 102 * @param data The processing instruction data, or null if 103 * none is supplied. 104 * @exception SAXException Any SAX exception, possibly wrapping another 105 * exception. 106 */ 107 @Override 108 public void processingInstruction (String target, String data) 109 throws SAXException { 110 if (fElementDepth > 0) { 111 StringBuilder instruction = new StringBuilder("<?").append(target); 112 if (data != null && data.length() > 0) { 113 instruction.append(' ').append(data); 114 } 115 instruction.append("?>"); 116 writeFlush(instruction.toString()); 117 } 118 } 119 120 /** 121 * Receive notification of the start of an element then write the normalized 122 * output to the file. 123 * @param uri The Namespace URI, or the empty string if the 124 * element has no Namespace URI or if Namespace 125 * processing is not being performed. 126 * @param local The local name (without prefix), or the 127 * empty string if Namespace processing is not being 128 * performed. 129 * @param raw The qualified name (with prefix), or the 130 * empty string if qualified names are not available. 131 * @param attrs The attributes attached to the element. If 132 * there are no attributes, it shall be an empty 133 * Attributes object. 134 * @throws SAXException Any SAX exception, possibly wrapping another 135 * exception. 136 */ 137 @Override 138 public void startElement(String uri, String local, String raw, 139 Attributes attrs) throws SAXException { 140 fElementDepth++; 141 StringBuilder start = new StringBuilder().append('<').append(raw); 142 if (attrs != null) { 143 for (int i = 0; i < attrs.getLength(); i++) { 144 start.append(' ').append(attrs.getQName(i)).append("=\""). 145 append(normalizeAndPrint(attrs.getValue(i))).append('"'); 146 } 147 } 148 start.append('>'); 149 writeFlush(start.toString()); 150 } 151 152 /** 153 * Receive notification of character data inside an element and write 154 * normalized characters to file. 155 * @param ch The characters. 156 * @param start The start position in the character array. 157 * @param length The number of characters to use from the 158 * character array. 159 * @exception org.xml.sax.SAXException Any SAX exception, possibly 160 * wrapping another exception. 161 */ 162 @Override 163 public void characters(char ch[], int start, int length) 164 throws SAXException { 165 writeFlush(normalizeAndPrint(ch, start, length)); 166 } 167 168 /** 169 * Receiving notification of ignorable whitespace in element content and 170 * writing normalized ignorable characters to file. 171 * @param ch The characters. 172 * @param start The start position in the character array. 173 * @param length The number of characters to use from the 174 * character array. 175 * @exception org.xml.sax.SAXException Any SAX exception, possibly 176 * wrapping another exception. 177 */ 178 @Override 179 public void ignorableWhitespace(char ch[], int start, int length) 180 throws SAXException { 181 characters(ch, start, length); 182 } 183 184 /** 185 * Receive notification of the end of an element and print end element. 186 * 187 * @param uri The Namespace URI, or the empty string if the 188 * element has no Namespace URI or if Namespace 189 * processing is not being performed. 190 * @param local The local name (without prefix), or the 191 * empty string if Namespace processing is not being 192 * performed. 193 * @param raw The qualified name (with prefix), or the 194 * empty string if qualified names are not available. 195 * @throws org.xml.sax.SAXException Any SAX exception, possibly 196 * wrapping another exception. 197 */ 198 @Override 199 public void endElement(String uri, String local, String raw) 200 throws SAXException { 201 fElementDepth--; 202 writeFlush("</" + raw + ">"); 203 } 204 205 /** 206 * Receive notification of a parser warning and print it out. 207 * @param ex The warning information encoded as an exception. 208 * @exception org.xml.sax.SAXException Any SAX exception, possibly 209 * wrapping another exception. 210 */ 211 @Override 212 public void warning(SAXParseException ex) throws SAXException { 213 printError("Warning", ex); 214 } 215 216 /** 217 * Receive notification of a parser error and print it out. 218 * @param ex The error information encoded as an exception. 219 * @exception org.xml.sax.SAXException Any SAX exception, possibly 220 * wrapping another exception. 221 */ 222 @Override 223 public void error(SAXParseException ex) throws SAXException { 224 printError("Error", ex); 225 } 226 227 /** 228 * Receive notification of a parser fatal error. Throw out fatal error 229 * following print fatal error message. 230 * @param ex The fatal error information encoded as an exception. 231 * @exception org.xml.sax.SAXException Any SAX exception, possibly 232 * wrapping another exception. 233 234 */ 235 @Override 236 public void fatalError(SAXParseException ex) throws SAXException { 237 printError("Fatal Error", ex); 238 throw ex; 239 } 240 241 /** 242 * Do nothing on start DTD. 243 * @param name The document type name. 244 * @param publicId The declared public identifier for the 245 * external DTD subset, or null if none was declared. 246 * @param systemId The declared system identifier for the 247 * external DTD subset, or null if none was declared. 248 * (Note that this is not resolved against the document 249 * base URI.) 250 * @exception SAXException The application may raise an 251 * exception. 252 */ 253 @Override 254 public void startDTD(String name, String publicId, String systemId) 255 throws SAXException { 256 } 257 258 /** 259 * Do nothing on end DTD. 260 * @exception SAXException The application may raise an exception. 261 */ 262 @Override 263 public void endDTD() throws SAXException { 264 } 265 266 /** 267 * Do nothing on start entity. 268 * @param name The name of the entity. If it is a parameter 269 * entity, the name will begin with '%', and if it is the 270 * external DTD subset, it will be "[dtd]". 271 * @exception SAXException The application may raise an exception. 272 */ 273 @Override 274 public void startEntity(String name) throws SAXException { 275 } 276 277 /** 278 * Do nothing on end entity. 279 * @param name The name of the entity. If it is a parameter 280 * entity, the name will begin with '%', and if it is the 281 * external DTD subset, it will be "[dtd]". 282 * @exception SAXException The application may raise an exception. 283 */ 284 @Override 285 public void endEntity(String name) throws SAXException { 286 } 287 288 /** 289 * Do nothing on start CDATA section. 290 * @exception SAXException The application may raise an exception. 291 */ 292 @Override 293 public void startCDATA() throws SAXException { 294 } 295 296 /** 297 * Do nothing on end CDATA section. 298 * @exception SAXException The application may raise an exception. 299 */ 300 @Override 301 public void endCDATA() throws SAXException { 302 } 303 304 /** 305 * Report an normalized XML comment when receive a comment in the document. 306 * 307 * @param ch An array holding the characters in the comment. 308 * @param start The starting position in the array. 309 * @param length The number of characters to use from the array. 310 * @exception SAXException The application may raise an exception. 311 */ 312 @Override 313 public void comment(char ch[], int start, int length) throws SAXException { 314 if (!fCanonical && fElementDepth > 0) { 315 writeFlush("<!--" + normalizeAndPrint(ch, start, length) + "-->"); 316 } 317 } 318 319 /** 320 * Normalizes and prints the given string. 321 * @param s String to be normalized 322 */ 323 private String normalizeAndPrint(String s) { 324 return s.chars().mapToObj(c -> normalizeAndPrint((char)c)). 325 collect(Collectors.joining()); 326 } 327 328 /** 329 * Normalizes and prints the given array of characters. 330 * @param ch The characters to be normalized. 331 * @param start The start position in the character array. 332 * @param length The number of characters to use from the 333 * character array. 334 */ 335 private String normalizeAndPrint(char[] ch, int offset, int length) { 336 return normalizeAndPrint(new String(ch, offset, length)); 337 } 338 339 /** 340 * Normalizes given character. 341 * @param c char to be normalized. 342 */ 343 private String normalizeAndPrint(char c) { 344 switch (c) { 345 case '<': 346 return "<"; 347 case '>': 348 return ">"; 349 case '&': 350 return "&"; 351 case '"': 352 return """; 353 case '\r': 354 case '\n': 355 return fCanonical ? "&#" + Integer.toString(c) + ";" : String.valueOf(c); 356 default: 357 return String.valueOf(c); 358 } 359 } 360 361 /** 362 * Prints the error message. 363 * @param type error type 364 * @param ex exception that need to be printed 365 */ 366 private void printError(String type, SAXParseException ex) { 367 System.err.print("[" + type + "] "); 368 String systemId = ex.getSystemId(); 369 if (systemId != null) { 370 int index = systemId.lastIndexOf('/'); 371 if (index != -1) 372 systemId = systemId.substring(index + 1); 373 System.err.print(systemId); 374 } 375 System.err.print(':' + ex.getLineNumber()); 376 System.err.print(':' + ex.getColumnNumber()); 377 System.err.println(": " + ex.getMessage()); 378 System.err.flush(); 379 } 380 381 /** 382 * Write out and flush. 383 * @param out string to be written. 384 */ 385 private void writeFlush(String out) { 386 fOut.print(out); 387 fOut.flush(); 388 } 389 }