BaseStax2Test.java
package stax2;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import junit.framework.TestCase;
import javax.xml.stream.*;
import javax.xml.stream.events.XMLEvent;
import org.codehaus.stax2.*;
import org.codehaus.stax2.evt.*;
import org.codehaus.stax2.ri.Stax2ReaderAdapter;
import org.codehaus.stax2.validation.XMLValidationProblem;
import org.codehaus.stax2.validation.XMLValidationSchema;
import com.ctc.wstx.stax.WstxOutputFactory;
/**
* Base unit test class to be inherited by all unit tests that test
* StAX2 API compatibility.
*/
public abstract class BaseStax2Test
extends TestCase
implements XMLStreamConstants
{
/**
* Value that should be reported by stax(2) impl to denote "no prefix"
* for elements
*/
final static String ELEM_NO_PREFIX = "";
/**
* Value that should be reported by stax(2) impl to denote "no prefix"
* for attributes
*/
final static String ATTR_NO_PREFIX = "";
final static HashMap<Integer,String> mTokenTypes = new HashMap<>();
static {
mTokenTypes.put(START_ELEMENT, "START_ELEMENT");
mTokenTypes.put(END_ELEMENT, "END_ELEMENT");
mTokenTypes.put(START_DOCUMENT, "START_DOCUMENT");
mTokenTypes.put(END_DOCUMENT, "END_DOCUMENT");
mTokenTypes.put(CHARACTERS, "CHARACTERS");
mTokenTypes.put(CDATA, "CDATA");
mTokenTypes.put(COMMENT, "COMMENT");
mTokenTypes.put(PROCESSING_INSTRUCTION, "PROCESSING_INSTRUCTION");
mTokenTypes.put(DTD, "DTD");
mTokenTypes.put(SPACE, "SPACE");
mTokenTypes.put(ENTITY_REFERENCE, "ENTITY_REFERENCE");
}
/**
* Switch that can be turned on to verify to display ALL exact Exceptions
* thrown when Exceptions are expected. This is sometimes necessary
* when debugging, since it's impossible to automatically verify
* that Exception is exactly the right one, since there is no
* strict Exception type hierarchy for StAX problems.
*<p>
* Note: Not made 'final static', so that compiler won't inline
* it. Makes possible to do partial re-compilations.
* Note: Since it's only used as the default value, sub-classes
* can separately turn it off as necessary
*/
//protected static boolean DEF_PRINT_EXP_EXCEPTION = true;
protected static boolean DEF_PRINT_EXP_EXCEPTION = false;
protected boolean PRINT_EXP_EXCEPTION = DEF_PRINT_EXP_EXCEPTION;
/*
///////////////////////////////////////////////////////////
// Lazy-loaded thingies
///////////////////////////////////////////////////////////
*/
XMLInputFactory2 mInputFactory = null;
XMLOutputFactory2 mOutputFactory = null;
XMLEventFactory2 mEventFactory = null;
/*
///////////////////////////////////////////////////////////
// Factory methods
///////////////////////////////////////////////////////////
*/
protected XMLInputFactory2 getInputFactory()
{
if (mInputFactory == null) {
/* Shouldn't try to set these here, if these tests are
* to be reusable. Rather, junit (ant) task should
* define system properties if necessary.
*/
//System.setProperty("javax.xml.stream.XMLInputFactory", "...");
mInputFactory = getNewInputFactory();
}
return mInputFactory;
}
protected XMLStreamReader2 constructNsStreamReader(String content, boolean coal)
throws XMLStreamException
{
XMLInputFactory f = getInputFactory();
setNamespaceAware(f, true);
setCoalescing(f, coal);
return (XMLStreamReader2) f.createXMLStreamReader(new StringReader(content));
}
protected XMLStreamReader2 constructNsStreamReader(InputStream in, boolean coal)
throws XMLStreamException
{
XMLInputFactory f = getInputFactory();
setNamespaceAware(f, true);
setCoalescing(f, coal);
return (XMLStreamReader2) f.createXMLStreamReader(in);
}
protected XMLEventFactory2 getEventFactory()
{
if (mEventFactory == null) {
/* Shouldn't try to set these here, if these tests are
* to be reusable. Rather, junit (ant) task should
* define system properties if necessary.
*/
//System.setProperty("javax.xml.stream.XMLEventFactory", "...");
mEventFactory = (XMLEventFactory2) XMLEventFactory.newInstance();
}
return mEventFactory;
}
protected static XMLInputFactory2 getNewInputFactory()
{
return (XMLInputFactory2) XMLInputFactory.newInstance();
}
protected XMLOutputFactory2 getOutputFactory()
{
if (mOutputFactory == null) {
//System.setProperty("javax.xml.stream.XMLOutputFactory", "...");
mOutputFactory = getNewOutputFactory();
}
return mOutputFactory;
}
protected static XMLOutputFactory2 getNewOutputFactory()
{
return (XMLOutputFactory2) XMLOutputFactory.newInstance();
}
protected XMLEventFactory getNewEventFactory() {
return XMLEventFactory.newFactory();
}
protected static XMLStreamReader2 constructStreamReader(XMLInputFactory f, String content)
throws XMLStreamException
{
return (XMLStreamReader2) f.createXMLStreamReader(new StringReader(content));
}
protected static XMLStreamReader2 constructStreamReader(XMLInputFactory f, byte[] data)
throws XMLStreamException
{
return (XMLStreamReader2) f.createXMLStreamReader(new ByteArrayInputStream(data));
}
@SuppressWarnings({ "deprecation", "resource" })
protected static XMLStreamReader2 constructStreamReaderForFile(XMLInputFactory f, String filename)
throws IOException, XMLStreamException
{
File inf = new File(filename);
XMLStreamReader sr = f.createXMLStreamReader(inf.toURL().toString(),
new FileReader(inf));
assertEquals(sr.getEventType(), START_DOCUMENT);
return (XMLStreamReader2) sr;
}
protected static XMLEventReader2 constructEventReader(XMLInputFactory f, String content)
throws XMLStreamException
{
return (XMLEventReader2) f.createXMLEventReader(new StringReader(content));
}
protected XMLStreamReader2 constructNonNsStreamReader(String content, boolean coal)
throws XMLStreamException
{
XMLInputFactory f = getInputFactory();
setNamespaceAware(f, false);
setCoalescing(f, coal);
return (XMLStreamReader2) f.createXMLStreamReader(new StringReader(content));
}
protected static XMLStreamWriter2 constructStreamWriter(Writer writer, boolean nsSupported, boolean repairing) throws XMLStreamException {
WstxOutputFactory f = new WstxOutputFactory();
f.getConfig().doSupportNamespaces(nsSupported);
f.getConfig().enableAutomaticNamespaces(repairing);
return (XMLStreamWriter2) f.createXMLStreamWriter(writer);
}
/**
* Method to force constructing a wrapper for given stream reader.
* Have to use this method to work around natural resistance by
* the wrapper to apply itself on what it considered "unnecessary"
* target.
*/
protected XMLStreamReader2 wrapWithAdapter(XMLStreamReader sr)
{
return new ForcedAdapter(sr);
}
/*
///////////////////////////////////////////////////////////
// Configuring input factory
///////////////////////////////////////////////////////////
*/
protected static boolean setNamespaceAware(XMLInputFactory f, boolean state)
throws XMLStreamException
{
/* Let's not assert, but see if it sticks. Some implementations
* might choose to silently ignore setting, at least for 'false'?
*/
try {
f.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, state ? Boolean.TRUE : Boolean.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 boolean isNamespaceAware(XMLInputFactory f)
throws XMLStreamException
{
return (Boolean) f.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE);
}
protected static void setCoalescing(XMLInputFactory f, boolean state)
throws XMLStreamException
{
f.setProperty(XMLInputFactory.IS_COALESCING, state);
}
protected static void setValidating(XMLInputFactory f, boolean state)
throws XMLStreamException
{
f.setProperty(XMLInputFactory.IS_VALIDATING, state);
}
protected static boolean setSupportDTD(XMLInputFactory f, boolean state)
throws XMLStreamException
{
try {
f.setProperty(XMLInputFactory.SUPPORT_DTD, state);
return (willSupportDTD(f) == state);
} catch (IllegalArgumentException e) {
// Let's assume that the property (or specific value) is NOT supported...
return false;
}
}
protected static boolean willSupportDTD(XMLInputFactory f)
throws XMLStreamException
{
return (Boolean) f.getProperty(XMLInputFactory.SUPPORT_DTD);
}
protected static void setReplaceEntities(XMLInputFactory f, boolean state)
throws XMLStreamException
{
f.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
state ? Boolean.TRUE : Boolean.FALSE);
}
protected static void setSupportExternalEntities(XMLInputFactory f, boolean state)
throws XMLStreamException
{
f.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
state ? Boolean.TRUE : Boolean.FALSE);
}
protected static void setLazyParsing(XMLInputFactory f, boolean state)
throws XMLStreamException
{
f.setProperty(XMLInputFactory2.P_LAZY_PARSING,
state ? Boolean.TRUE : Boolean.FALSE);
}
/*
///////////////////////////////////////////////////////////
// Configuring output factory
///////////////////////////////////////////////////////////
*/
protected static void setRepairing(XMLOutputFactory f, boolean state)
{
f.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, state);
}
protected static boolean setNamespaceAware(XMLOutputFactory f, boolean state)
throws XMLStreamException
{
/* Let's not assert, but see if it sticks. Some implementations
* might choose to silently ignore setting, at least for 'false'?
*/
try {
f.setProperty(XMLStreamProperties.XSP_NAMESPACE_AWARE, state ? Boolean.TRUE : Boolean.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 boolean isNamespaceAware(XMLOutputFactory f)
throws XMLStreamException
{
return (Boolean) f.getProperty(XMLStreamProperties.XSP_NAMESPACE_AWARE);
}
/*
///////////////////////////////////////////////////////////
// Higher-level test methods
///////////////////////////////////////////////////////////
*/
/**
* Method that will iterate through contents of an XML document
* using specified stream reader; will also access some of data
* to make sure reader reads most of lazy-loadable data.
* Method is usually called to try to get an exception for invalid
* content.
*
* @return Dummy value calculated on contents; used to make sure
* no dead code is eliminated
*/
protected static int streamThrough(XMLStreamReader sr)
throws XMLStreamException
{
int result = 0;
while (sr.hasNext()) {
int type = sr.next();
result += type;
if (sr.hasText()) {
/* will also do basic verification for text content, to
* see that all text accessor methods return same content
*/
result += getAndVerifyText(sr).hashCode();
}
if (sr.hasName()) {
result += sr.getName().hashCode();
}
}
return result;
}
protected int streamThroughFailing(XMLInputFactory f, String contents,
String msg)
throws XMLStreamException
{
int result = 0;
try {
XMLStreamReader sr = constructStreamReader(f, contents);
result = streamThrough(sr);
} catch (XMLStreamException ex) { // good
if (PRINT_EXP_EXCEPTION) {
System.out.println("Expected failure: '"+ex.getMessage()+"' "
+"(matching message: '"+msg+"')");
}
return 0;
} catch (RuntimeException ex2) { // ok
if (PRINT_EXP_EXCEPTION) {
System.out.println("Expected failure: '"+ex2.getMessage()+"' "
+"(matching message: '"+msg+"')");
}
return 0;
} catch (Throwable t) { // not so good
fail("Expected an XMLStreamException or RuntimeException for "+msg
+", got: "+t);
}
fail("Expected an exception for "+msg);
return result; // never gets here
}
protected int streamThroughFailing(XMLStreamReader sr, String msg)
throws XMLStreamException
{
int result = 0;
try {
result = streamThrough(sr);
} catch (XMLStreamException ex) { // good
if (PRINT_EXP_EXCEPTION) {
System.out.println("Expected failure: '"+ex.getMessage()+"' "
+"(matching message: '"+msg+"')");
}
return 0;
} catch (RuntimeException ex2) { // ok
if (PRINT_EXP_EXCEPTION) {
System.out.println("Expected failure: '"+ex2.getMessage()+"' "
+"(matching message: '"+msg+"')");
}
return 0;
} catch (Throwable t) { // not so good
fail("Expected an XMLStreamException or RuntimeException for "+msg
+", got: "+t);
}
fail("Expected an exception for "+msg);
return result; // never gets here
}
/*
///////////////////////////////////////////////////////////
// Assertions
///////////////////////////////////////////////////////////
*/
protected static String tokenTypeDesc(int tt)
{
String desc = mTokenTypes.get(tt);
return (desc == null) ? ("["+tt+"]") : desc;
}
protected static void assertTokenType(int expType, XMLEvent evt)
{
assertTokenType(expType, evt.getEventType());
}
protected static void assertTokenType(int expType, int actType)
{
if (expType != actType) {
String expStr = tokenTypeDesc(expType);
String actStr = tokenTypeDesc(actType);
if (expStr == null) {
expStr = ""+expType;
}
if (actStr == null) {
actStr = ""+actType;
}
fail("Expected token "+expStr+"; got "+actStr+".");
}
}
/**
* Helper assertion that assert that the String is either null or
* empty ("").
*/
protected static void assertNullOrEmpty(String str)
{
if (str != null && str.length() > 0) {
fail("Expected String to be empty or null; was '"+str+"' (length "
+str.length()+")");
}
}
protected static void assertNotNullOrEmpty(String str)
{
if (str == null || str.length() == 0) {
fail("Expected String to be non-empty; got "
+((str == null) ? "NULL" : "\"\""));
}
}
/**
* Method that can be used to verify that the current element
* pointed to by the stream reader has no prefix.
*/
protected static void assertNoElemPrefix(XMLStreamReader sr)
throws XMLStreamException
{
String prefix = sr.getPrefix();
if (prefix != ELEM_NO_PREFIX) {
fail("Element that does not have a prefix should be indicated with <"+ELEM_NO_PREFIX+">, not <"+prefix+">");
}
}
/**
* Helper method for ensuring that the given return value for
* attribute prefix accessor has returned a value that
* represents "no prefix" value.
*<p>
* Current thinking (early 2008) is that empty string is the
* expected value here.
*/
protected static void assertNoAttrPrefix(String attrPrefix)
throws XMLStreamException
{
if (attrPrefix != ATTR_NO_PREFIX) {
fail("Attribute that does not have a prefix should be indicated with <"+ATTR_NO_PREFIX+">, not <"+attrPrefix+">");
}
}
/**
* Method that can be used to verify that the current element
* pointed to by the stream reader does not belong to a namespace.
*/
protected static void assertElemNotInNamespace(XMLStreamReader sr)
throws XMLStreamException
{
String uri = sr.getNamespaceURI();
if (uri == null) {
fail("Excepted empty String to indicate \"no namespace\": got null");
} else if (uri.length() != 0) {
fail("Excepted no (null) namespace URI: got '"+uri+"'");
}
}
protected static void assertNoAttrNamespace(String attrNsURI)
throws XMLStreamException
{
if (attrNsURI == null) {
fail("Expected empty String to indicate \"no namespace\" (for attribute): got null");
} else if (attrNsURI.length() != 0) {
fail("Expected empty String to indicate \"no namespace\" (for attribute): got '"+attrNsURI+"'");
}
}
protected static void assertMessageContains(Throwable e, String substring) {
final String actual = e.getMessage();
if (!actual.contains(substring)) {
fail("Message '"+ actual +"' should contain '"+ substring +"'");
}
}
protected static void failStrings(String msg, String exp, String act)
{
// !!! TODO: Indicate position where Strings differ
fail(msg+": expected "+quotedPrintable(exp)+", got "
+quotedPrintable(act));
}
/**
* Method that not only gets currently available text from the
* reader, but also checks that its consistenly accessible using
* different (basic) StAX methods.
*/
protected static String getAndVerifyText(XMLStreamReader sr)
throws XMLStreamException
{
/* 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) {
return sr.getText();
}
int expLen = sr.getTextLength();
/* Hmmh. It's only ok to return empty text for DTD event... well,
* maybe also for CDATA, since empty CDATA 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 (type == CHARACTERS) {
assertTrue("Stream reader should never return empty Strings.", (expLen > 0));
}
String text = sr.getText();
assertNotNull("getText() should never return null.", text);
assertEquals("Expected text length of "+expLen+", got "+text.length(),
expLen, text.length());
char[] textChars = sr.getTextCharacters();
int start = sr.getTextStart();
String text2 = new String(textChars, start, expLen);
assertEquals(text, text2);
return text;
}
protected void verifyException(Throwable e, String match)
{
String msg = e.getMessage();
String lmsg = msg.toLowerCase();
String lmatch = match.toLowerCase();
if (!lmsg.contains(lmatch)) {
fail("Expected an exception with sub-string \""+match+"\": got one with message \""+msg+"\"");
}
}
protected static void validateWriter(final String DOC, final List<XMLValidationProblem> probs, XMLInputFactory f,
XMLValidationSchema schema, StringWriter writer, XMLStreamWriter2 sw) throws XMLStreamException {
sw.validateAgainst(schema);
final List<XMLValidationProblem> writerProbs = new ArrayList<>();
sw.setValidationProblemHandler(writerProbs::add);
XMLStreamReader2 sr = (XMLStreamReader2)f.createXMLStreamReader(
new StringReader(DOC));
sw.copyEventFromReader(sr, false);
while (sr.hasNext()) {
/* int type = */sr.next();
sw.copyEventFromReader(sr, false);
}
sr.close();
sw.close();
assertEquals(DOC, writer.toString());
assertEquals(probs.size(), writerProbs.size());
for (int i = 0; i < probs.size(); i++) {
XMLValidationProblem expected = probs.get(i);
XMLValidationProblem actual = writerProbs.get(i);
assertEquals(expected.getMessage(), actual.getMessage());
assertEquals(expected.getSeverity(), actual.getSeverity());
}
}
/*
///////////////////////////////////////////////////////////
// Debug/output helpers
///////////////////////////////////////////////////////////
*/
public static void warn(String msg)
{
System.err.println("WARN: "+msg);
}
public 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) {
StringBuilder sb = new StringBuilder(6);
sb.append("\\u");
String hex = Integer.toHexString(ch);
for (int i = 0, len = 4 - hex.length(); i < len; i++) {
sb.append('0');
}
sb.append(hex);
return sb.toString();
}
return null;
}
public static String printableWithSpaces(char ch)
{
if (ch == '\n') {
return "\\n";
}
if (ch == '\r') {
return "\\r";
}
if (ch == '\t') {
return "\\t";
}
if (ch > 127 || ch < 32) {
StringBuilder sb = new StringBuilder(6);
sb.append("\\u");
String hex = Integer.toHexString(ch);
for (int i = 0, len = 4 - hex.length(); i < len; i++) {
sb.append('0');
}
sb.append(hex);
return sb.toString();
}
return null;
}
public static String printable(String str)
{
if (str == null || str.length() == 0) {
return str;
}
int len = str.length();
StringBuilder sb = new StringBuilder(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();
}
public static String printableWithSpaces(String str)
{
if (str == null || str.length() == 0) {
return str;
}
int len = str.length();
StringBuilder sb = new StringBuilder(len + 64);
for (int i = 0; i < len; ++i) {
char c = str.charAt(i);
String res = printableWithSpaces(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)+"'";
}
/*
///////////////////////////////////////////////////////////
// Helper classes
///////////////////////////////////////////////////////////
*/
/**
* Need a dummy base class to be able to access protected
* constructor for testing purposes.
*/
final static class ForcedAdapter
extends Stax2ReaderAdapter
{
public ForcedAdapter(XMLStreamReader sr)
{
super(sr);
}
}
}