1/* 2 * Copyright (c) 2014, 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 24package util; 25 26import java.io.ByteArrayInputStream; 27import java.io.File; 28import java.io.FileReader; 29import java.io.IOException; 30import java.io.StringReader; 31import java.io.UnsupportedEncodingException; 32import java.util.HashMap; 33 34import javax.xml.stream.XMLEventFactory; 35import javax.xml.stream.XMLInputFactory; 36import javax.xml.stream.XMLOutputFactory; 37import javax.xml.stream.XMLResolver; 38import javax.xml.stream.XMLStreamConstants; 39import javax.xml.stream.XMLStreamException; 40import javax.xml.stream.XMLStreamReader; 41import javax.xml.stream.events.XMLEvent; 42 43import org.testng.Assert; 44 45/** 46 * Base class for all StaxTest unit test classes. Contains shared 47 * functionality for many common set up tasks, as well as for 48 * outputting diagnostics. 49 * 50 */ 51public class BaseStAXUT implements XMLStreamConstants { 52 /** 53 * This is the de facto standard property that enables accurate reporting of 54 * CDATA events. 55 */ 56 final static String PROP_REPORT_CDATA = "http://java.sun.com/xml/stream/properties/report-cdata-event"; 57 58 final static HashMap mTokenTypes = new HashMap(); 59 static { 60 mTokenTypes.put(new Integer(START_ELEMENT), "START_ELEMENT"); 61 mTokenTypes.put(new Integer(END_ELEMENT), "END_ELEMENT"); 62 mTokenTypes.put(new Integer(START_DOCUMENT), "START_DOCUMENT"); 63 mTokenTypes.put(new Integer(END_DOCUMENT), "END_DOCUMENT"); 64 mTokenTypes.put(new Integer(CHARACTERS), "CHARACTERS"); 65 mTokenTypes.put(new Integer(CDATA), "CDATA"); 66 mTokenTypes.put(new Integer(COMMENT), "COMMENT"); 67 mTokenTypes.put(new Integer(PROCESSING_INSTRUCTION), "PROCESSING_INSTRUCTION"); 68 mTokenTypes.put(new Integer(DTD), "DTD"); 69 mTokenTypes.put(new Integer(SPACE), "SPACE"); 70 mTokenTypes.put(new Integer(ENTITY_REFERENCE), "ENTITY_REFERENCE"); 71 mTokenTypes.put(new Integer(NAMESPACE), "NAMESPACE_DECLARATION"); 72 mTokenTypes.put(new Integer(NOTATION_DECLARATION), "NOTATION_DECLARATION"); 73 mTokenTypes.put(new Integer(ENTITY_DECLARATION), "ENTITY_DECLARATION"); 74 } 75 76 /* 77 * /////////////////////////////////////////////////// // Consts for 78 * expected values /////////////////////////////////////////////////// 79 */ 80 81 /** 82 * Expected return value for streamReader.getNamespaceURI() in 83 * non-namespace-aware mode. 84 */ 85 protected final String DEFAULT_URI_NON_NS = ""; 86 87 protected final String DEFAULT_URI_NS = ""; 88 89 /* 90 * /////////////////////////////////////////////////// // Other consts 91 * /////////////////////////////////////////////////// 92 */ 93 94 /* 95 * /////////////////////////////////////////////////// // Cached instances 96 * /////////////////////////////////////////////////// 97 */ 98 99 XMLInputFactory mInputFactory; 100 XMLOutputFactory mOutputFactory; 101 XMLEventFactory mEventFactory; 102 103 protected XMLInputFactory getInputFactory() { 104 if (mInputFactory == null) { 105 mInputFactory = getNewInputFactory(); 106 } 107 return mInputFactory; 108 } 109 110 protected static XMLInputFactory getNewInputFactory() { 111 return XMLInputFactory.newInstance(); 112 } 113 114 protected XMLOutputFactory getOutputFactory() { 115 if (mOutputFactory == null) { 116 mOutputFactory = getNewOutputFactory(); 117 } 118 return mOutputFactory; 119 } 120 121 protected static XMLOutputFactory getNewOutputFactory() { 122 return XMLOutputFactory.newInstance(); 123 } 124 125 protected XMLEventFactory getEventFactory() { 126 if (mEventFactory == null) { 127 mEventFactory = XMLEventFactory.newInstance(); 128 } 129 return mEventFactory; 130 } 131 132 protected static XMLStreamReader constructStreamReader(XMLInputFactory f, String content) throws XMLStreamException { 133 // return f.createXMLStreamReader(new StringReader(content)); 134 try { 135 byte[] data = content.getBytes("UTF-8"); 136 return constructStreamReader(f, data); 137 } catch (UnsupportedEncodingException e) { 138 throw new RuntimeException(e); 139 } 140 } 141 142 protected static XMLStreamReader constructStreamReader(XMLInputFactory f, byte[] b) throws XMLStreamException { 143 return f.createXMLStreamReader(new ByteArrayInputStream(b)); 144 } 145 146 protected static XMLStreamReader constructStreamReaderForFile(XMLInputFactory f, String filename) throws IOException, XMLStreamException { 147 File inf = new File(filename); 148 XMLStreamReader sr = f.createXMLStreamReader(inf.toURL().toString(), new FileReader(inf)); 149 Assert.assertEquals(START_DOCUMENT, sr.getEventType()); 150 return sr; 151 } 152 153 protected XMLStreamReader constructNsStreamReader(String content) throws XMLStreamException { 154 XMLInputFactory f = getInputFactory(); 155 setNamespaceAware(f, true); 156 return f.createXMLStreamReader(new StringReader(content)); 157 } 158 159 protected XMLStreamReader constructNsStreamReader(String content, boolean coal) throws XMLStreamException { 160 XMLInputFactory f = getInputFactory(); 161 setNamespaceAware(f, true); 162 setCoalescing(f, coal); 163 return f.createXMLStreamReader(new StringReader(content)); 164 } 165 166 /* 167 * ////////////////////////////////////////////////// // Configuring input 168 * factory ////////////////////////////////////////////////// 169 */ 170 171 protected static boolean isCoalescing(XMLInputFactory f) throws XMLStreamException { 172 return ((Boolean) f.getProperty(XMLInputFactory.IS_COALESCING)).booleanValue(); 173 } 174 175 protected static void setCoalescing(XMLInputFactory f, boolean state) throws XMLStreamException { 176 Boolean b = state ? Boolean.TRUE : Boolean.FALSE; 177 f.setProperty(XMLInputFactory.IS_COALESCING, b); 178 // Let's just double-check it... 179 Assert.assertEquals(state, isCoalescing(f)); 180 } 181 182 protected static boolean isValidating(XMLInputFactory f) throws XMLStreamException { 183 return ((Boolean) f.getProperty(XMLInputFactory.IS_VALIDATING)).booleanValue(); 184 } 185 186 protected static void setValidating(XMLInputFactory f, boolean state) throws XMLStreamException { 187 try { 188 Boolean b = state ? Boolean.TRUE : Boolean.FALSE; 189 f.setProperty(XMLInputFactory.IS_VALIDATING, b); 190 } catch (IllegalArgumentException iae) { 191 Assert.fail("Could not set DTD validating mode to " + state + ": " + iae); 192 // throw new XMLStreamException(iae.getMessage(), iae); 193 } 194 Assert.assertEquals(state, isValidating(f)); 195 } 196 197 protected static boolean isNamespaceAware(XMLInputFactory f) throws XMLStreamException { 198 return ((Boolean) f.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE)).booleanValue(); 199 } 200 201 /** 202 * @return True if setting succeeded, and property supposedly was 203 * succesfully set to the value specified; false if there was a 204 * problem. 205 */ 206 protected static boolean setNamespaceAware(XMLInputFactory f, boolean state) throws XMLStreamException { 207 try { 208 f.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, state ? Boolean.TRUE : Boolean.FALSE); 209 210 /* 211 * 07-Sep-2005, TSa: Let's not assert, but instead let's see if it 212 * sticks. Some implementations might choose to silently ignore 213 * setting, at least for 'false'? 214 */ 215 return (isNamespaceAware(f) == state); 216 } catch (IllegalArgumentException e) { 217 /* 218 * Let's assume, then, that the property (or specific value for it) 219 * is NOT supported... 220 */ 221 return false; 222 } 223 } 224 225 protected static void setReplaceEntities(XMLInputFactory f, boolean state) throws XMLStreamException { 226 Boolean b = state ? Boolean.TRUE : Boolean.FALSE; 227 f.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, b); 228 Assert.assertEquals(b, f.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES)); 229 } 230 231 protected static void setSupportDTD(XMLInputFactory f, boolean state) throws XMLStreamException { 232 Boolean b = state ? Boolean.TRUE : Boolean.FALSE; 233 f.setProperty(XMLInputFactory.SUPPORT_DTD, b); 234 Assert.assertEquals(b, f.getProperty(XMLInputFactory.SUPPORT_DTD)); 235 } 236 237 protected static boolean setSupportExternalEntities(XMLInputFactory f, boolean state) throws XMLStreamException { 238 Boolean b = state ? Boolean.TRUE : Boolean.FALSE; 239 try { 240 f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, b); 241 Object act = f.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES); 242 return (act instanceof Boolean) && ((Boolean) act).booleanValue() == state; 243 } catch (IllegalArgumentException e) { 244 /* 245 * Let's assume, then, that the property (or specific value for it) 246 * is NOT supported... 247 */ 248 return false; 249 } 250 } 251 252 protected static void setResolver(XMLInputFactory f, XMLResolver resolver) throws XMLStreamException { 253 f.setProperty(XMLInputFactory.RESOLVER, resolver); 254 } 255 256 protected static boolean setReportCData(XMLInputFactory f, boolean state) throws XMLStreamException { 257 258 Boolean b = state ? Boolean.TRUE : Boolean.FALSE; 259 if (f.isPropertySupported(PROP_REPORT_CDATA)) { 260 f.setProperty(PROP_REPORT_CDATA, b); 261 return true; 262 } 263 return false; 264 } 265 266 /* 267 * ////////////////////////////////////////////////// // Stream reader 268 * accessors ////////////////////////////////////////////////// 269 */ 270 271 /** 272 * Method that not only gets currently available text from the reader, but 273 * also checks that its consistenly accessible using different StAX methods. 274 */ 275 protected static String getAndVerifyText(XMLStreamReader sr) throws XMLStreamException { 276 String text = sr.getText(); 277 278 /* 279 * 05-Apr-2006, TSa: Although getText() is available for DTD and 280 * ENTITY_REFERENCE, getTextXxx() are not. Thus, can not do more checks 281 * for those types. 282 */ 283 int type = sr.getEventType(); 284 if (type != ENTITY_REFERENCE && type != DTD) { 285 Assert.assertNotNull("getText() should never return null.", text); 286 int expLen = sr.getTextLength(); 287 /* 288 * Hmmh. Can only return empty text for CDATA (since empty blocks 289 * are legal). 290 */ 291 /* 292 * !!! 01-Sep-2004, TSa: note: theoretically, in coalescing mode, it 293 * could be possible to have empty CDATA section(s) get converted to 294 * CHARACTERS, which would be empty... may need to enhance this to 295 * check that mode is not coalescing? Or something 296 */ 297 if (sr.getEventType() == CHARACTERS) { 298 if (expLen == 0) { 299 Assert.fail("Stream reader should never return empty Strings."); 300 } 301 } 302 Assert.assertEquals(expLen, text.length(), "Expected text length of " + expLen + ", got " + text.length()); 303 char[] textChars = sr.getTextCharacters(); 304 int start = sr.getTextStart(); 305 String text2 = new String(textChars, start, expLen); 306 Assert.assertEquals("Expected getText() and getTextCharacters() to return same value for event of type (" + tokenTypeDesc(sr.getEventType()) + ")", 307 text, text2); 308 } else { // DTD or ENTITY_REFERENCE 309 // not sure if null is legal for these either, but... 310 if (text == null) { // let's prevent an NPE at caller 311 text = ""; 312 } 313 } 314 return text; 315 } 316 317 protected static String getAllText(XMLStreamReader sr) throws XMLStreamException { 318 StringBuffer sb = new StringBuffer(); 319 while (true) { 320 int tt = sr.getEventType(); 321 if (tt != CHARACTERS && tt != SPACE) { 322 break; 323 } 324 sb.append(getAndVerifyText(sr)); 325 sr.next(); 326 } 327 return sb.toString(); 328 } 329 330 protected static String getAllCData(XMLStreamReader sr) throws XMLStreamException { 331 StringBuffer sb = new StringBuffer(); 332 while (true) { 333 /* 334 * Note: CDATA sections CAN be reported as CHARACTERS, but not as 335 * SPACE 336 */ 337 int tt = sr.getEventType(); 338 if (tt != CHARACTERS && tt != CDATA) { 339 break; 340 } 341 sb.append(getAndVerifyText(sr)); 342 sr.next(); 343 } 344 return sb.toString(); 345 } 346 347 /* 348 * ////////////////////////////////////////////////// // Derived assert/fail 349 * methods ////////////////////////////////////////////////// 350 */ 351 352 protected static void assertTokenType(int expType, int actType) { 353 if (expType == actType) { 354 return; 355 } 356 Assert.fail("Expected token " + tokenTypeDesc(expType) + "; got " + tokenTypeDesc(actType) + "."); 357 } 358 359 protected static void assertTokenType(int expType, int actType, XMLStreamReader sr) { 360 if (expType == actType) { 361 return; 362 } 363 Assert.fail("Expected token " + tokenTypeDesc(expType) + "; got " + tokenTypeDesc(actType, sr) + "."); 364 } 365 366 protected static void assertTextualTokenType(int actType) { 367 if (actType != CHARACTERS && actType != SPACE && actType != CDATA) { 368 Assert.fail("Expected textual token (CHARACTERS, SPACE or CDATA)" + "; got " + tokenTypeDesc(actType) + "."); 369 } 370 } 371 372 protected static void failStrings(String msg, String exp, String act) { 373 // !!! TODO: Indicate position where Strings differ 374 Assert.fail(msg + ": expected " + quotedPrintable(exp) + ", got " + quotedPrintable(act)); 375 } 376 377 /** 378 * Specific method makes sense, since earlier it was not clear whether null 379 * or empty string (or perhaps both) would be the right answer when there is 380 * no prefix. 381 * <p> 382 * However: as per javadocs of {@link XMLStreamReader#getPrefix}, from JDK 383 * 1.6 indicate, the current understanding is that <b>null</b> is the 384 * ultimate right answer here. 385 */ 386 protected static void assertNoPrefix(XMLStreamReader sr) throws XMLStreamException { 387 String prefix = sr.getPrefix(); 388 if (prefix != null) { 389 if (prefix.length() != 0) { 390 Assert.fail("Current element should not have a prefix: got '" + prefix + "'"); 391 } else { 392 Assert.fail("Expected null to signify missing prefix (see XMLStreamReader#getPrefix() JavaDocs): got empty String"); 393 } 394 } 395 } 396 397 protected static void assertNoAttrPrefix(String attrPrefix) throws XMLStreamException { 398 if (attrPrefix != null) { 399 if (attrPrefix.length() != 0) { 400 Assert.fail("Attribute should not have a prefix: got '" + attrPrefix + "'"); 401 } else { 402 Assert.fail("Expected null to signify missing attribute prefix (see XMLStreamReader#getAttributePrefix() JavaDocs): got empty String"); 403 } 404 } 405 } 406 407 /** 408 * Similar to {@link #assertNoPrefix}, but here we do know that unbound 409 * namespace URI should be indicated as empty String. 410 */ 411 protected static void assertNoNsURI(XMLStreamReader sr) throws XMLStreamException { 412 String uri = sr.getNamespaceURI(); 413 if (uri == null) { 414 Assert.fail("Expected empty String to indicate \"no namespace\": got null"); 415 } else if (uri.length() != 0) { 416 Assert.fail("Expected empty String to indicate \"no namespace\": got '" + uri + "'"); 417 } 418 } 419 420 protected static void assertNoAttrNamespace(String attrNsURI) throws XMLStreamException { 421 if (attrNsURI == null) { 422 // refer to 6903561; accept null for now. 423 // fail("Expected empty String to indicate \"no namespace\" (for attribute): got null"); 424 } else if (attrNsURI.length() != 0) { 425 Assert.fail("Expected empty String to indicate \"no namespace\" (for attribute): got '" + attrNsURI + "'"); 426 } 427 } 428 429 protected static void assertNoPrefixOrNs(XMLStreamReader sr) throws XMLStreamException { 430 assertNoPrefix(sr); 431 assertNoNsURI(sr); 432 } 433 434 /** 435 * Helper assertion that assert that the String is either null or empty 436 * (""). 437 */ 438 protected static void assertNullOrEmpty(String str) { 439 if (str != null && str.length() > 0) { 440 Assert.fail("Expected String to be empty or null; was '" + str + "' (length " + str.length() + ")"); 441 } 442 } 443 444 /* 445 * ////////////////////////////////////////////////// // Debug/output 446 * helpers ////////////////////////////////////////////////// 447 */ 448 449 protected static String tokenTypeDesc(int tt) { 450 String desc = (String) mTokenTypes.get(new Integer(tt)); 451 if (desc == null) { 452 return "[" + tt + "]"; 453 } 454 return desc; 455 } 456 457 protected static String tokenTypeDesc(XMLEvent evt) { 458 return tokenTypeDesc(evt.getEventType()); 459 } 460 461 final static int MAX_DESC_TEXT_CHARS = 8; 462 463 protected static String tokenTypeDesc(int tt, XMLStreamReader sr) { 464 String desc = tokenTypeDesc(tt); 465 // Let's show first 8 chars or so... 466 if (tt == CHARACTERS || tt == SPACE || tt == CDATA) { 467 String str = sr.getText(); 468 if (str.length() > MAX_DESC_TEXT_CHARS) { 469 desc = "\"" + str.substring(0, MAX_DESC_TEXT_CHARS) + "\"[...]"; 470 } else { 471 desc = "\"" + desc + "\""; 472 } 473 desc = " (" + desc + ")"; 474 } 475 return desc; 476 } 477 478 protected static String valueDesc(String value) { 479 if (value == null) { 480 return "[NULL]"; 481 } 482 return "\"" + value + "\""; 483 } 484 485 protected static String printable(char ch) { 486 if (ch == '\n') { 487 return "\\n"; 488 } 489 if (ch == '\r') { 490 return "\\r"; 491 } 492 if (ch == '\t') { 493 return "\\t"; 494 } 495 if (ch == ' ') { 496 return "_"; 497 } 498 if (ch > 127 || ch < 32) { 499 StringBuffer sb = new StringBuffer(6); 500 sb.append("\\u"); 501 String hex = Integer.toHexString((int) ch); 502 for (int i = 0, len = 4 - hex.length(); i < len; i++) { 503 sb.append('0'); 504 } 505 sb.append(hex); 506 return sb.toString(); 507 } 508 return null; 509 } 510 511 protected static String printable(String str) { 512 if (str == null || str.length() == 0) { 513 return str; 514 } 515 516 int len = str.length(); 517 StringBuffer sb = new StringBuffer(len + 64); 518 for (int i = 0; i < len; ++i) { 519 char c = str.charAt(i); 520 String res = printable(c); 521 if (res == null) { 522 sb.append(c); 523 } else { 524 sb.append(res); 525 } 526 } 527 return sb.toString(); 528 } 529 530 protected static String quotedPrintable(String str) { 531 if (str == null || str.length() == 0) { 532 return "[0]''"; 533 } 534 return "[len: " + str.length() + "] '" + printable(str) + "'"; 535 } 536 537 protected void reportNADueToProperty(String method, String prop) { 538 String clsName = getClass().getName(); 539 /* 540 * 27-Sep-2005, TSa: Should probably use some other mechanism for 541 * reporting this. Does JUnit have something applicable? 542 */ 543 System.err.println("Skipping " + clsName + "#" + method + ": property '" + prop + "' (or one of its values) not supported."); 544 } 545 546 protected void reportNADueToNS(String method) { 547 reportNADueToProperty(method, "IS_NAMESPACE_AWARE"); 548 } 549 550 protected void reportNADueToExtEnt(String method) { 551 reportNADueToProperty(method, "IS_SUPPORTING_EXTERNAL_ENTITIES"); 552 } 553 554 protected void reportNADueToEntityExpansion(String method, int type) { 555 String clsName = getClass().getName(); 556 String msg = (type > 0) ? " (next event: " + tokenTypeDesc(type) + ")" : ""; 557 System.err.println("Skipping " + clsName + "#" + method + ": entity expansion does not seem to be functioning properly" + msg + "."); 558 } 559} 560