package wstxtest.stream;
import java.util.*;
import javax.xml.stream.*;
import org.codehaus.stax2.*;
import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.ent.EntityDecl;
import com.ctc.wstx.exc.WstxLazyException;
import com.ctc.wstx.sr.BasicStreamReader;
/**
* This unit test suite checks to see that Woodstox implementation dependant
* functionality works the way it's planned to. In some cases future StAX
* revisions may dictate exact behaviour expected, but for now expected
* behaviour is based on
* a combination of educated guessing and intuitive behaviour.
*/
public class TestEntityRead
extends BaseStreamTest
{
/**
* This unit test checks that the information received as part of the
* event, in non-expanding mode, is as expected.
*/
public void testDeclaredInNonExpandingMode()
throws XMLStreamException
{
String XML = "<!DOCTYPE root [\n"
+" <!ENTITY myent 'value'>\n"
+"]><root>text:&myent;more</root>"
;
// Non-expanding, coalescing:
BasicStreamReader sr = getReader(XML, false, true);
assertTokenType(DTD, sr.next());
DTDInfo dtd = sr.getDTDInfo();
assertNotNull(dtd);
assertTokenType(START_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
assertTokenType(CHARACTERS, sr.next());
assertTokenType(ENTITY_REFERENCE, sr.next());
assertEquals("myent", sr.getLocalName());
EntityDecl ed = sr.getCurrentEntityDecl();
assertNotNull(ed);
assertEquals("myent", ed.getName());
assertEquals("value", ed.getReplacementText());
// The pure stax way:
assertEquals("value", sr.getText());
// Finally, let's see that location info is about right?
Location loc = ed.getLocation();
assertNotNull(loc);
assertEquals(2, loc.getLineNumber());
/* Hmmh. Not 100% if this location makes sense, but... it's the
* current behaviour, so we can regression test it.
*/
assertEquals(3, loc.getColumnNumber());
// don't care about offsets here... location tests catch them
assertTokenType(CHARACTERS, sr.next());
assertTokenType(END_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
assertTokenType(END_DOCUMENT, sr.next());
sr.close();
}
/**
* This unit test checks that in non-expanding mode it is acceptable
* to refer to undeclared entities.
*/
public void testUndeclaredInNonExpandingMode()
throws Exception
{
String XML = "<!DOCTYPE root [\n"
+"]><root>text:&myent;more</root>"
;
// Non-expanding, coalescing:
BasicStreamReader sr = getReader(XML, false, true);
assertTokenType(DTD, sr.next());
DTDInfo dtd = sr.getDTDInfo();
assertNotNull(dtd);
assertTokenType(START_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
assertTokenType(CHARACTERS, sr.next());
/* Exception would be a real possibility, so let's catch one (if
* any) for debugging purposes:
*/
try {
assertTokenType(ENTITY_REFERENCE, sr.next());
} catch (XMLStreamException sex) {
fail("Did not except a stream exception on undeclared entity in non-entity-expanding mode; got: "+sex);
}
assertEquals("myent", sr.getLocalName());
EntityDecl ed = sr.getCurrentEntityDecl();
assertNull(ed);
assertTokenType(CHARACTERS, sr.next());
assertTokenType(END_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
assertTokenType(END_DOCUMENT, sr.next());
sr.close();
}
/**
* This unit test verifies that it's possible to add a Map of
* expansions from Entity names to
*/
public void testUndeclaredUsingCustomMap()
throws XMLStreamException
{
// First, let's check actual usage:
String XML = "<root>ok: &myent;&myent2;</root>";
String EXP_TEXT = "ok: (simple)expand to ([text])";
XMLInputFactory fact = getConfiguredFactory(true, true);
Map m = new HashMap();
m.put("myent", "(simple)");
m.put("myent3", "[text]");
m.put("myent2", "expand to (&myent3;)");
fact.setProperty(WstxInputProperties.P_CUSTOM_INTERNAL_ENTITIES, m);
XMLStreamReader sr = constructStreamReader(fact, XML);
assertTokenType(START_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
assertTokenType(CHARACTERS, sr.next());
assertEquals(EXP_TEXT, getAndVerifyText(sr));
assertTokenType(END_ELEMENT, sr.next());
sr.close();
/* And then see if we can query configured value and get expected
* types of results
*/
m = (Map) fact.getProperty(WstxInputProperties.P_CUSTOM_INTERNAL_ENTITIES);
assertNotNull(m);
assertEquals(3, m.size());
Iterator it = m.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String name = entry.getKey().toString();
if (name.equals("myent") || name.equals("myent2")
|| name.equals("myent3")) {
// fine, let's just verify the type
EntityDecl ed = (EntityDecl) entry.getValue();
assertNotNull(ed);
} else {
fail("Unexpected entity '"+name+"' in the custom entity map");
}
}
}
/**
* This unit test checks that it is possible to deal with undeclared
* entities in resolving mode too, as long as a special resolver
* is used.
*/
public void testUndeclaredButResolved()
throws Exception
{
XMLInputFactory fact = getConfiguredFactory(true, true);
for (int i = 0; i < 3; ++i) {
String XML, expText;
XMLResolver resolver;
switch (i) {
case 0:
XML = "<root>value: &myent;</root>";
resolver = new Resolver("myent", "value");
expText = "value: value";
break;
case 1:
XML = "<root>expands to:&myent;...</root>";
resolver = new Resolver("myent", "X&Y");
expText = "expands to:X&Y...";
break;
default:
XML = "<root>testing: &myent;</root>";
resolver = new Resolver("dummy", "foobar");
expText = ""; // whatever;
break;
}
fact.setProperty(WstxInputProperties.P_UNDECLARED_ENTITY_RESOLVER,
resolver);
XMLStreamReader sr = constructStreamReader(fact, XML);
assertTokenType(START_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
if (i == 2) { // Should throw an exception, then:
try {
sr.next();
String text = sr.getText(); // to force parsing
fail("Expected an exception for undefined entity 'myent' that doesn't resolve via customer resolver");
} catch (XMLStreamException sex) {
; // good;
} catch (WstxLazyException lex) {
; // likewise
}
} else {
try {
assertTokenType(CHARACTERS, sr.next());
} catch (XMLStreamException sex) {
// only caught to provide more meaningful fail info:
fail("Did not expect an exception, since 'myent' should have resolved; got: "+sex);
}
assertEquals(expText, sr.getText());
assertTokenType(END_ELEMENT, sr.next());
assertEquals("root", sr.getLocalName());
}
}
}
/*
////////////////////////////////////////
// Private methods, other
////////////////////////////////////////
*/
/**
* Note: all readers for this set of unit tests enable DTD handling;
* otherwise entity definitions wouldn't be read. Validation shouldn't
* need to be enabled just for that purpose.
*/
private BasicStreamReader getReader(String contents, boolean replEntities,
boolean coalescing)
throws XMLStreamException
{
XMLInputFactory f = getConfiguredFactory(replEntities, coalescing);
return (BasicStreamReader) constructStreamReader(f, contents);
}
private XMLInputFactory getConfiguredFactory(boolean replEntities, boolean coalescing)
throws XMLStreamException
{
XMLInputFactory f = getInputFactory();
setNamespaceAware(f, true);
setSupportDTD(f, true);
setValidating(f, false);
setReplaceEntities(f, replEntities);
setCoalescing(f, coalescing);
return f;
}
/*
////////////////////////////////////////
// Helper classes
////////////////////////////////////////
*/
final static class Resolver
implements XMLResolver
{
final String mKey, mValue;
public Resolver(String key, String value) {
mKey = key;
mValue = value;
}
public Object resolveEntity(String publicID, String systemID, String baseURI, String namespace)
{
if (mKey.equals(namespace)) {
return mValue;
}
return null;
}
}
}