8043084: XML JAXP unittest co-location
Reviewed-by: joehw, dfuchs
Contributed-by: frank.yuan@oracle.com
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import org.testng.Assert;
/**
* Base class for all StaxTest unit test classes. Contains shared
* functionality for many common set up tasks, as well as for
* outputting diagnostics.
*
*/
public class BaseStAXUT implements XMLStreamConstants {
/**
* This is the de facto standard property that enables accurate reporting of
* CDATA events.
*/
final static String PROP_REPORT_CDATA = "http://java.sun.com/xml/stream/properties/report-cdata-event";
final static HashMap mTokenTypes = new HashMap();
static {
mTokenTypes.put(new Integer(START_ELEMENT), "START_ELEMENT");
mTokenTypes.put(new Integer(END_ELEMENT), "END_ELEMENT");
mTokenTypes.put(new Integer(START_DOCUMENT), "START_DOCUMENT");
mTokenTypes.put(new Integer(END_DOCUMENT), "END_DOCUMENT");
mTokenTypes.put(new Integer(CHARACTERS), "CHARACTERS");
mTokenTypes.put(new Integer(CDATA), "CDATA");
mTokenTypes.put(new Integer(COMMENT), "COMMENT");
mTokenTypes.put(new Integer(PROCESSING_INSTRUCTION), "PROCESSING_INSTRUCTION");
mTokenTypes.put(new Integer(DTD), "DTD");
mTokenTypes.put(new Integer(SPACE), "SPACE");
mTokenTypes.put(new Integer(ENTITY_REFERENCE), "ENTITY_REFERENCE");
mTokenTypes.put(new Integer(NAMESPACE), "NAMESPACE_DECLARATION");
mTokenTypes.put(new Integer(NOTATION_DECLARATION), "NOTATION_DECLARATION");
mTokenTypes.put(new Integer(ENTITY_DECLARATION), "ENTITY_DECLARATION");
}
/*
* /////////////////////////////////////////////////// // Consts for
* expected values ///////////////////////////////////////////////////
*/
/**
* Expected return value for streamReader.getNamespaceURI() in
* non-namespace-aware mode.
*/
protected final String DEFAULT_URI_NON_NS = "";
protected final String DEFAULT_URI_NS = "";
/*
* /////////////////////////////////////////////////// // Other consts
* ///////////////////////////////////////////////////
*/
/*
* /////////////////////////////////////////////////// // Cached instances
* ///////////////////////////////////////////////////
*/
XMLInputFactory mInputFactory;
XMLOutputFactory mOutputFactory;
XMLEventFactory mEventFactory;
protected XMLInputFactory getInputFactory() {
if (mInputFactory == null) {
mInputFactory = getNewInputFactory();
}
return mInputFactory;
}
protected static XMLInputFactory getNewInputFactory() {
return XMLInputFactory.newInstance();
}
protected XMLOutputFactory getOutputFactory() {
if (mOutputFactory == null) {
mOutputFactory = getNewOutputFactory();
}
return mOutputFactory;
}
protected static XMLOutputFactory getNewOutputFactory() {
return XMLOutputFactory.newInstance();
}
protected XMLEventFactory getEventFactory() {
if (mEventFactory == null) {
mEventFactory = XMLEventFactory.newInstance();
}
return mEventFactory;
}
protected static XMLStreamReader constructStreamReader(XMLInputFactory f, String content) throws XMLStreamException {
// return f.createXMLStreamReader(new StringReader(content));
try {
byte[] data = content.getBytes("UTF-8");
return constructStreamReader(f, data);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected static XMLStreamReader constructStreamReader(XMLInputFactory f, byte[] b) throws XMLStreamException {
return f.createXMLStreamReader(new ByteArrayInputStream(b));
}
protected static XMLStreamReader constructStreamReaderForFile(XMLInputFactory f, String filename) throws IOException, XMLStreamException {
File inf = new File(filename);
XMLStreamReader sr = f.createXMLStreamReader(inf.toURL().toString(), new FileReader(inf));
Assert.assertEquals(START_DOCUMENT, sr.getEventType());
return sr;
}
protected XMLStreamReader constructNsStreamReader(String content) throws XMLStreamException {
XMLInputFactory f = getInputFactory();
setNamespaceAware(f, true);
return f.createXMLStreamReader(new StringReader(content));
}
protected XMLStreamReader constructNsStreamReader(String content, boolean coal) throws XMLStreamException {
XMLInputFactory f = getInputFactory();
setNamespaceAware(f, true);
setCoalescing(f, coal);
return f.createXMLStreamReader(new StringReader(content));
}
/*
* ////////////////////////////////////////////////// // Configuring input
* factory //////////////////////////////////////////////////
*/
protected static boolean isCoalescing(XMLInputFactory f) throws XMLStreamException {
return ((Boolean) f.getProperty(XMLInputFactory.IS_COALESCING)).booleanValue();
}
protected static void setCoalescing(XMLInputFactory f, boolean state) throws XMLStreamException {
Boolean b = state ? Boolean.TRUE : Boolean.FALSE;
f.setProperty(XMLInputFactory.IS_COALESCING, b);
// Let's just double-check it...
Assert.assertEquals(state, isCoalescing(f));
}
protected static boolean isValidating(XMLInputFactory f) throws XMLStreamException {
return ((Boolean) f.getProperty(XMLInputFactory.IS_VALIDATING)).booleanValue();
}
protected static void setValidating(XMLInputFactory f, boolean state) throws XMLStreamException {
try {
Boolean b = state ? Boolean.TRUE : Boolean.FALSE;
f.setProperty(XMLInputFactory.IS_VALIDATING, b);
} catch (IllegalArgumentException iae) {
Assert.fail("Could not set DTD validating mode to " + state + ": " + iae);
// throw new XMLStreamException(iae.getMessage(), iae);
}
Assert.assertEquals(state, isValidating(f));
}
protected static boolean isNamespaceAware(XMLInputFactory f) throws XMLStreamException {
return ((Boolean) f.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE)).booleanValue();
}
/**
* @return True if setting succeeded, and property supposedly was
* succesfully set to the value specified; false if there was a
* problem.
*/
protected static boolean setNamespaceAware(XMLInputFactory f, boolean state) throws XMLStreamException {
try {
f.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, state ? Boolean.TRUE : Boolean.FALSE);
/*
* 07-Sep-2005, TSa: Let's not assert, but instead let's see if it
* sticks. Some implementations might choose to silently ignore
* setting, at least for 'false'?
*/
return (isNamespaceAware(f) == state);
} catch (IllegalArgumentException e) {
/*
* Let's assume, then, that the property (or specific value for it)
* is NOT supported...
*/
return false;
}
}
protected static void setReplaceEntities(XMLInputFactory f, boolean state) throws XMLStreamException {
Boolean b = state ? Boolean.TRUE : Boolean.FALSE;
f.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, b);
Assert.assertEquals(b, f.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES));
}
protected static void setSupportDTD(XMLInputFactory f, boolean state) throws XMLStreamException {
Boolean b = state ? Boolean.TRUE : Boolean.FALSE;
f.setProperty(XMLInputFactory.SUPPORT_DTD, b);
Assert.assertEquals(b, f.getProperty(XMLInputFactory.SUPPORT_DTD));
}
protected static boolean setSupportExternalEntities(XMLInputFactory f, boolean state) throws XMLStreamException {
Boolean b = state ? Boolean.TRUE : Boolean.FALSE;
try {
f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, b);
Object act = f.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES);
return (act instanceof Boolean) && ((Boolean) act).booleanValue() == state;
} catch (IllegalArgumentException e) {
/*
* Let's assume, then, that the property (or specific value for it)
* is NOT supported...
*/
return false;
}
}
protected static void setResolver(XMLInputFactory f, XMLResolver resolver) throws XMLStreamException {
f.setProperty(XMLInputFactory.RESOLVER, resolver);
}
protected static boolean setReportCData(XMLInputFactory f, boolean state) throws XMLStreamException {
Boolean b = state ? Boolean.TRUE : Boolean.FALSE;
if (f.isPropertySupported(PROP_REPORT_CDATA)) {
f.setProperty(PROP_REPORT_CDATA, b);
return true;
}
return false;
}
/*
* ////////////////////////////////////////////////// // Stream reader
* accessors //////////////////////////////////////////////////
*/
/**
* Method that not only gets currently available text from the reader, but
* also checks that its consistenly accessible using different StAX methods.
*/
protected static String getAndVerifyText(XMLStreamReader sr) throws XMLStreamException {
String text = sr.getText();
/*
* 05-Apr-2006, TSa: Although getText() is available for DTD and
* ENTITY_REFERENCE, getTextXxx() are not. Thus, can not do more checks
* for those types.
*/
int type = sr.getEventType();
if (type != ENTITY_REFERENCE && type != DTD) {
Assert.assertNotNull("getText() should never return null.", text);
int expLen = sr.getTextLength();
/*
* Hmmh. Can only return empty text for CDATA (since empty blocks
* are legal).
*/
/*
* !!! 01-Sep-2004, TSa: note: theoretically, in coalescing mode, it
* could be possible to have empty CDATA section(s) get converted to
* CHARACTERS, which would be empty... may need to enhance this to
* check that mode is not coalescing? Or something
*/
if (sr.getEventType() == CHARACTERS) {
if (expLen == 0) {
Assert.fail("Stream reader should never return empty Strings.");
}
}
Assert.assertEquals(expLen, text.length(), "Expected text length of " + expLen + ", got " + text.length());
char[] textChars = sr.getTextCharacters();
int start = sr.getTextStart();
String text2 = new String(textChars, start, expLen);
Assert.assertEquals("Expected getText() and getTextCharacters() to return same value for event of type (" + tokenTypeDesc(sr.getEventType()) + ")",
text, text2);
} else { // DTD or ENTITY_REFERENCE
// not sure if null is legal for these either, but...
if (text == null) { // let's prevent an NPE at caller
text = "";
}
}
return text;
}
protected static String getAllText(XMLStreamReader sr) throws XMLStreamException {
StringBuffer sb = new StringBuffer();
while (true) {
int tt = sr.getEventType();
if (tt != CHARACTERS && tt != SPACE) {
break;
}
sb.append(getAndVerifyText(sr));
sr.next();
}
return sb.toString();
}
protected static String getAllCData(XMLStreamReader sr) throws XMLStreamException {
StringBuffer sb = new StringBuffer();
while (true) {
/*
* Note: CDATA sections CAN be reported as CHARACTERS, but not as
* SPACE
*/
int tt = sr.getEventType();
if (tt != CHARACTERS && tt != CDATA) {
break;
}
sb.append(getAndVerifyText(sr));
sr.next();
}
return sb.toString();
}
/*
* ////////////////////////////////////////////////// // Derived assert/fail
* methods //////////////////////////////////////////////////
*/
protected static void assertTokenType(int expType, int actType) {
if (expType == actType) {
return;
}
Assert.fail("Expected token " + tokenTypeDesc(expType) + "; got " + tokenTypeDesc(actType) + ".");
}
protected static void assertTokenType(int expType, int actType, XMLStreamReader sr) {
if (expType == actType) {
return;
}
Assert.fail("Expected token " + tokenTypeDesc(expType) + "; got " + tokenTypeDesc(actType, sr) + ".");
}
protected static void assertTextualTokenType(int actType) {
if (actType != CHARACTERS && actType != SPACE && actType != CDATA) {
Assert.fail("Expected textual token (CHARACTERS, SPACE or CDATA)" + "; got " + tokenTypeDesc(actType) + ".");
}
}
protected static void failStrings(String msg, String exp, String act) {
// !!! TODO: Indicate position where Strings differ
Assert.fail(msg + ": expected " + quotedPrintable(exp) + ", got " + quotedPrintable(act));
}
/**
* Specific method makes sense, since earlier it was not clear whether null
* or empty string (or perhaps both) would be the right answer when there is
* no prefix.
* <p>
* However: as per javadocs of {@link XMLStreamReader#getPrefix}, from JDK
* 1.6 indicate, the current understanding is that <b>null</b> is the
* ultimate right answer here.
*/
protected static void assertNoPrefix(XMLStreamReader sr) throws XMLStreamException {
String prefix = sr.getPrefix();
if (prefix != null) {
if (prefix.length() != 0) {
Assert.fail("Current element should not have a prefix: got '" + prefix + "'");
} else {
Assert.fail("Expected null to signify missing prefix (see XMLStreamReader#getPrefix() JavaDocs): got empty String");
}
}
}
protected static void assertNoAttrPrefix(String attrPrefix) throws XMLStreamException {
if (attrPrefix != null) {
if (attrPrefix.length() != 0) {
Assert.fail("Attribute should not have a prefix: got '" + attrPrefix + "'");
} else {
Assert.fail("Expected null to signify missing attribute prefix (see XMLStreamReader#getAttributePrefix() JavaDocs): got empty String");
}
}
}
/**
* Similar to {@link #assertNoPrefix}, but here we do know that unbound
* namespace URI should be indicated as empty String.
*/
protected static void assertNoNsURI(XMLStreamReader sr) throws XMLStreamException {
String uri = sr.getNamespaceURI();
if (uri == null) {
Assert.fail("Expected empty String to indicate \"no namespace\": got null");
} else if (uri.length() != 0) {
Assert.fail("Expected empty String to indicate \"no namespace\": got '" + uri + "'");
}
}
protected static void assertNoAttrNamespace(String attrNsURI) throws XMLStreamException {
if (attrNsURI == null) {
// refer to 6903561; accept null for now.
// fail("Expected empty String to indicate \"no namespace\" (for attribute): got null");
} else if (attrNsURI.length() != 0) {
Assert.fail("Expected empty String to indicate \"no namespace\" (for attribute): got '" + attrNsURI + "'");
}
}
protected static void assertNoPrefixOrNs(XMLStreamReader sr) throws XMLStreamException {
assertNoPrefix(sr);
assertNoNsURI(sr);
}
/**
* Helper assertion that assert that the String is either null or empty
* ("").
*/
protected static void assertNullOrEmpty(String str) {
if (str != null && str.length() > 0) {
Assert.fail("Expected String to be empty or null; was '" + str + "' (length " + str.length() + ")");
}
}
/*
* ////////////////////////////////////////////////// // Debug/output
* helpers //////////////////////////////////////////////////
*/
protected static String tokenTypeDesc(int tt) {
String desc = (String) mTokenTypes.get(new Integer(tt));
if (desc == null) {
return "[" + tt + "]";
}
return desc;
}
protected static String tokenTypeDesc(XMLEvent evt) {
return tokenTypeDesc(evt.getEventType());
}
final static int MAX_DESC_TEXT_CHARS = 8;
protected static String tokenTypeDesc(int tt, XMLStreamReader sr) {
String desc = tokenTypeDesc(tt);
// Let's show first 8 chars or so...
if (tt == CHARACTERS || tt == SPACE || tt == CDATA) {
String str = sr.getText();
if (str.length() > MAX_DESC_TEXT_CHARS) {
desc = "\"" + str.substring(0, MAX_DESC_TEXT_CHARS) + "\"[...]";
} else {
desc = "\"" + desc + "\"";
}
desc = " (" + desc + ")";
}
return desc;
}
protected static String valueDesc(String value) {
if (value == null) {
return "[NULL]";
}
return "\"" + value + "\"";
}
protected static String printable(char ch) {
if (ch == '\n') {
return "\\n";
}
if (ch == '\r') {
return "\\r";
}
if (ch == '\t') {
return "\\t";
}
if (ch == ' ') {
return "_";
}
if (ch > 127 || ch < 32) {
StringBuffer sb = new StringBuffer(6);
sb.append("\\u");
String hex = Integer.toHexString((int) ch);
for (int i = 0, len = 4 - hex.length(); i < len; i++) {
sb.append('0');
}
sb.append(hex);
return sb.toString();
}
return null;
}
protected static String printable(String str) {
if (str == null || str.length() == 0) {
return str;
}
int len = str.length();
StringBuffer sb = new StringBuffer(len + 64);
for (int i = 0; i < len; ++i) {
char c = str.charAt(i);
String res = printable(c);
if (res == null) {
sb.append(c);
} else {
sb.append(res);
}
}
return sb.toString();
}
protected static String quotedPrintable(String str) {
if (str == null || str.length() == 0) {
return "[0]''";
}
return "[len: " + str.length() + "] '" + printable(str) + "'";
}
protected void reportNADueToProperty(String method, String prop) {
String clsName = getClass().getName();
/*
* 27-Sep-2005, TSa: Should probably use some other mechanism for
* reporting this. Does JUnit have something applicable?
*/
System.err.println("Skipping " + clsName + "#" + method + ": property '" + prop + "' (or one of its values) not supported.");
}
protected void reportNADueToNS(String method) {
reportNADueToProperty(method, "IS_NAMESPACE_AWARE");
}
protected void reportNADueToExtEnt(String method) {
reportNADueToProperty(method, "IS_SUPPORTING_EXTERNAL_ENTITIES");
}
protected void reportNADueToEntityExpansion(String method, int type) {
String clsName = getClass().getName();
String msg = (type > 0) ? " (next event: " + tokenTypeDesc(type) + ")" : "";
System.err.println("Skipping " + clsName + "#" + method + ": entity expansion does not seem to be functioning properly" + msg + ".");
}
}