Making the StAX-based XML readers and handlers more compliant

This commit is contained in:
Arjen Poutsma
2008-02-09 04:04:25 +00:00
parent 16d0a8f98d
commit 0e85927bdf
13 changed files with 538 additions and 49 deletions

View File

@@ -23,8 +23,11 @@ import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.Comment;
import javax.xml.stream.events.DTD;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.EntityDeclaration;
import javax.xml.stream.events.EntityReference;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.NotationDeclaration;
import javax.xml.stream.events.ProcessingInstruction;
@@ -120,6 +123,15 @@ public class StaxEventXmlReader extends AbstractStaxXmlReader {
case XMLStreamConstants.ENTITY_DECLARATION:
handleEntityDeclaration((EntityDeclaration) event);
break;
case XMLStreamConstants.COMMENT:
handleComment((Comment) event);
break;
case XMLStreamConstants.DTD:
handleDtd((DTD) event);
break;
case XMLStreamConstants.ENTITY_REFERENCE:
handleEntityReference((EntityReference) event);
break;
}
}
if (!documentEnded) {
@@ -128,24 +140,41 @@ public class StaxEventXmlReader extends AbstractStaxXmlReader {
}
private void handleCharacters(Characters characters) throws SAXException {
private void handleStartElement(StartElement startElement) throws SAXException {
if (getContentHandler() != null) {
if (characters.isIgnorableWhiteSpace()) {
getContentHandler()
.ignorableWhitespace(characters.getData().toCharArray(), 0, characters.getData().length());
QName qName = startElement.getName();
if (hasNamespacesFeature()) {
for (Iterator i = startElement.getNamespaces(); i.hasNext();) {
Namespace namespace = (Namespace) i.next();
getContentHandler().startPrefixMapping(namespace.getPrefix(), namespace.getNamespaceURI());
}
getContentHandler().startElement(qName.getNamespaceURI(), qName.getLocalPart(),
QNameUtils.toQualifiedName(qName), getAttributes(startElement));
}
else {
if (characters.isCData() && getLexicalHandler() != null) {
getLexicalHandler().startCDATA();
}
getContentHandler().characters(characters.getData().toCharArray(), 0, characters.getData().length());
if (characters.isCData() && getLexicalHandler() != null) {
getLexicalHandler().endCDATA();
}
getContentHandler()
.startElement("", "", QNameUtils.toQualifiedName(qName), getAttributes(startElement));
}
}
}
private void handleCharacters(Characters characters) throws SAXException {
char[] data = characters.getData().toCharArray();
if (getContentHandler() != null && characters.isIgnorableWhiteSpace()) {
getContentHandler().ignorableWhitespace(data, 0, data.length);
return;
}
if (characters.isCData() && getLexicalHandler() != null) {
getLexicalHandler().startCDATA();
}
if (getContentHandler() != null) {
getContentHandler().characters(data, 0, data.length);
}
if (characters.isCData() && getLexicalHandler() != null) {
getLexicalHandler().endCDATA();
}
}
private void handleEndDocument() throws SAXException {
if (getContentHandler() != null) {
getContentHandler().endDocument();
@@ -195,24 +224,34 @@ public class StaxEventXmlReader extends AbstractStaxXmlReader {
}
}
private void handleStartElement(StartElement startElement) throws SAXException {
if (getContentHandler() != null) {
QName qName = startElement.getName();
if (hasNamespacesFeature()) {
for (Iterator i = startElement.getNamespaces(); i.hasNext();) {
Namespace namespace = (Namespace) i.next();
getContentHandler().startPrefixMapping(namespace.getPrefix(), namespace.getNamespaceURI());
}
getContentHandler().startElement(qName.getNamespaceURI(), qName.getLocalPart(),
QNameUtils.toQualifiedName(qName), getAttributes(startElement));
}
else {
getContentHandler()
.startElement("", "", QNameUtils.toQualifiedName(qName), getAttributes(startElement));
}
private void handleComment(Comment comment) throws SAXException {
if (getLexicalHandler() != null) {
char[] ch = comment.getText().toCharArray();
getLexicalHandler().comment(ch, 0, ch.length);
}
}
private void handleDtd(DTD dtd) throws SAXException {
if (getLexicalHandler() != null) {
javax.xml.stream.Location location = dtd.getLocation();
getLexicalHandler().startDTD(null, location.getPublicId(), location.getSystemId());
}
if (getLexicalHandler() != null) {
getLexicalHandler().endDTD();
}
}
private void handleEntityReference(EntityReference reference) throws SAXException {
if (getLexicalHandler() != null) {
getLexicalHandler().startEntity(reference.getName());
}
if (getLexicalHandler() != null) {
getLexicalHandler().endEntity(reference.getName());
}
}
private Attributes getAttributes(StartElement event) {
AttributesImpl attributes = new AttributesImpl();
@@ -249,5 +288,4 @@ public class StaxEventXmlReader extends AbstractStaxXmlReader {
return attributes;
}
}

View File

@@ -99,6 +99,15 @@ public class StaxStreamXmlReader extends AbstractStaxXmlReader {
handleEndDocument();
documentEnded = true;
break;
case XMLStreamConstants.COMMENT:
handleComment();
break;
case XMLStreamConstants.DTD:
handleDtd();
break;
case XMLStreamConstants.ENTITY_REFERENCE:
handleEntityReference();
break;
}
if (reader.hasNext() && elementDepth >= 0) {
eventType = reader.next();
@@ -110,7 +119,6 @@ public class StaxStreamXmlReader extends AbstractStaxXmlReader {
if (!documentEnded) {
handleEndDocument();
}
}
private void handleStartDocument() throws SAXException {
@@ -163,15 +171,45 @@ public class StaxStreamXmlReader extends AbstractStaxXmlReader {
}
private void handleCharacters() throws SAXException {
if (getContentHandler() != null && reader.isWhiteSpace()) {
getContentHandler()
.ignorableWhitespace(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
return;
}
if (XMLStreamConstants.CDATA == reader.getEventType() && getLexicalHandler() != null) {
getLexicalHandler().startCDATA();
}
if (getContentHandler() != null) {
if (reader.isWhiteSpace()) {
getContentHandler()
.ignorableWhitespace(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
}
else {
getContentHandler()
.characters(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
}
getContentHandler()
.characters(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
}
if (XMLStreamConstants.CDATA == reader.getEventType() && getLexicalHandler() != null) {
getLexicalHandler().endCDATA();
}
}
private void handleComment() throws SAXException {
if (getLexicalHandler() != null) {
getLexicalHandler().comment(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
}
}
private void handleDtd() throws SAXException {
if (getLexicalHandler() != null) {
javax.xml.stream.Location location = reader.getLocation();
getLexicalHandler().startDTD(null, location.getPublicId(), location.getSystemId());
}
if (getLexicalHandler() != null) {
getLexicalHandler().endDTD();
}
}
private void handleEntityReference() throws SAXException {
if (getLexicalHandler() != null) {
getLexicalHandler().startEntity(reader.getLocalName());
}
if (getLexicalHandler() != null) {
getLexicalHandler().endEntity(reader.getLocalName());
}
}

View File

@@ -31,6 +31,7 @@ import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.XMLReaderFactory;
@@ -40,7 +41,7 @@ import org.springframework.xml.sax.SaxUtils;
public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
protected static XMLInputFactory inputFactory = XMLInputFactory.newInstance();
protected static XMLInputFactory inputFactory;
private Resource testContentHandler;
@@ -51,6 +52,7 @@ public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
private ContentHandler contentHandler;
protected void setUp() throws Exception {
inputFactory = XMLInputFactory.newInstance();
standardReader = XMLReaderFactory.createXMLReader();
contentHandlerControl = MockControl.createStrictControl(ContentHandler.class);
contentHandlerControl.setDefaultMatcher(new SaxArgumentMatcher());
@@ -109,6 +111,32 @@ public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
contentHandlerControl.verify();
}
public void testLexicalHandler() throws SAXException, IOException, XMLStreamException {
MockControl lexicalHandlerControl = MockControl.createStrictControl(LexicalHandler.class);
lexicalHandlerControl.setDefaultMatcher(new SaxArgumentMatcher());
LexicalHandler lexicalHandlerMock = (LexicalHandler) lexicalHandlerControl.getMock();
LexicalHandler lexicalHandler = new CopyingLexicalHandler(lexicalHandlerMock);
Resource testLexicalHandlerXml = new ClassPathResource("testLexicalHandler.xml", getClass());
standardReader.setContentHandler(null);
standardReader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
standardReader.parse(SaxUtils.createInputSource(testLexicalHandlerXml));
lexicalHandlerControl.replay();
inputFactory.setProperty("javax.xml.stream.isCoalescing", Boolean.FALSE);
inputFactory.setProperty("http://java.sun.com/xml/stream/properties/report-cdata-event", Boolean.TRUE);
inputFactory.setProperty("javax.xml.stream.isReplacingEntityReferences", Boolean.FALSE);
inputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", Boolean.FALSE);
AbstractStaxXmlReader staxXmlReader = createStaxXmlReader(testLexicalHandlerXml.getInputStream());
staxXmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
staxXmlReader.parse(new InputSource());
lexicalHandlerControl.verify();
}
protected abstract AbstractStaxXmlReader createStaxXmlReader(InputStream inputStream) throws XMLStreamException;
/** Easymock <code>ArgumentMatcher</code> implementation that matches SAX arguments. */
@@ -156,7 +184,6 @@ public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
for (int j = 0; j < actualAttributes.getLength(); j++) {
if (expectedAttributes.getURI(i).equals(actualAttributes.getURI(j)) &&
expectedAttributes.getQName(i).equals(actualAttributes.getQName(j)) &&
// expectedAttributes.getLocalName(i).equals(actualAttributes.getLocalName(j)) &&
expectedAttributes.getType(i).equals(actualAttributes.getType(j)) &&
expectedAttributes.getValue(i).equals(actualAttributes.getValue(j))) {
found = true;
@@ -237,7 +264,7 @@ public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
private static class CopyingContentHandler implements ContentHandler {
private ContentHandler wrappee;
private final ContentHandler wrappee;
private CopyingContentHandler(ContentHandler wrappee) {
this.wrappee = wrappee;
@@ -286,7 +313,43 @@ public abstract class AbstractStaxXmlReaderTestCase extends TestCase {
public void skippedEntity(String name) throws SAXException {
wrappee.skippedEntity(name);
}
}
private static class CopyingLexicalHandler implements LexicalHandler {
private final LexicalHandler wrappee;
private CopyingLexicalHandler(LexicalHandler wrappee) {
this.wrappee = wrappee;
}
public void startDTD(String name, String publicId, String systemId) throws SAXException {
wrappee.startDTD("element", publicId, systemId);
}
public void endDTD() throws SAXException {
wrappee.endDTD();
}
public void startEntity(String name) throws SAXException {
wrappee.startEntity(name);
}
public void endEntity(String name) throws SAXException {
wrappee.endEntity(name);
}
public void startCDATA() throws SAXException {
wrappee.startCDATA();
}
public void endCDATA() throws SAXException {
wrappee.endCDATA();
}
public void comment(char ch[], int start, int length) throws SAXException {
wrappee.comment(copy(ch), start, length);
}
}
private static char[] copy(char[] ch) {

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE element
[<!ENTITY entity "entity">]>
<!--Comment-->
<element>
<cdata><![CDATA[cdata]]></cdata>
<entity>&entity;</entity>
</element>