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

@@ -0,0 +1,178 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import org.springframework.util.Assert;
import org.springframework.xml.namespace.QNameUtils;
/**
* Specific SAX {@link ContentHandler} and {@link LexicalHandler} that adds the resulting AXIOM OMElement to a specified
* parent element when <code>endDocument</code> is called. Used for returing <code>SAXResult</code>s from Axiom
* elements.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomHandler implements ContentHandler, LexicalHandler {
private final OMFactory factory;
private final List elements = new ArrayList();
private Map namespaces = new HashMap();
private final OMContainer container;
private int charactersType = XMLStreamConstants.CHARACTERS;
AxiomHandler(OMContainer container, OMFactory factory) {
Assert.notNull(container, "'container' must not be null");
Assert.notNull(factory, "'factory' must not be null");
this.factory = factory;
this.container = container;
}
private OMContainer getParent() {
if (!elements.isEmpty()) {
return (OMContainer) elements.get(elements.size() - 1);
}
else {
return container;
}
}
public void startPrefixMapping(String prefix, String uri) throws SAXException {
namespaces.put(prefix, uri);
}
public void endPrefixMapping(String prefix) throws SAXException {
namespaces.remove(prefix);
}
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
OMContainer parent = getParent();
OMElement element = factory.createOMElement(localName, null, parent);
for (Iterator iterator = namespaces.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
String prefix = (String) entry.getKey();
if (prefix.length() == 0) {
element.declareDefaultNamespace((String) entry.getValue());
}
else {
element.declareNamespace((String) entry.getValue(), prefix);
}
}
QName qname = QNameUtils.toQName(uri, qName);
element.setLocalName(qname.getLocalPart());
element.setNamespace(element.findNamespace(qname.getNamespaceURI(), qname.getPrefix()));
for (int i = 0; i < atts.getLength(); i++) {
QName attrName = QNameUtils.toQName(atts.getURI(i), atts.getQName(i));
String value = atts.getValue(i);
if (!atts.getQName(i).startsWith("xmlns")) {
OMNamespace namespace = factory.createOMNamespace(attrName.getNamespaceURI(), attrName.getPrefix());
OMAttribute attribute = factory.createOMAttribute(attrName.getLocalPart(), namespace, value);
element.addAttribute(attribute);
}
}
elements.add(element);
}
public void endElement(String uri, String localName, String qName) throws SAXException {
elements.remove(elements.size() - 1);
}
public void characters(char ch[], int start, int length) throws SAXException {
String data = new String(ch, start, length);
OMContainer parent = getParent();
factory.createOMText(parent, data, charactersType);
}
public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
charactersType = XMLStreamConstants.SPACE;
characters(ch, start, length);
charactersType = XMLStreamConstants.CHARACTERS;
}
public void processingInstruction(String target, String data) throws SAXException {
OMContainer parent = getParent();
factory.createOMProcessingInstruction(parent, target, data);
}
public void comment(char ch[], int start, int length) throws SAXException {
String content = new String(ch, start, length);
OMContainer parent = getParent();
factory.createOMComment(parent, content);
}
public void startCDATA() throws SAXException {
charactersType = XMLStreamConstants.CDATA;
}
public void endCDATA() throws SAXException {
charactersType = XMLStreamConstants.CHARACTERS;
}
public void startEntity(String name) throws SAXException {
charactersType = XMLStreamConstants.ENTITY_REFERENCE;
}
public void endEntity(String name) throws SAXException {
charactersType = XMLStreamConstants.CHARACTERS;
}
/*
* Unsupported
*/
public void setDocumentLocator(Locator locator) {
}
public void startDocument() throws SAXException {
}
public void endDocument() throws SAXException {
}
public void skippedEntity(String name) throws SAXException {
}
public void startDTD(String name, String publicId, String systemId) throws SAXException {
}
public void endDTD() throws SAXException {
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.ext.LexicalHandler;
/**
* Specific TrAX {@link javax.xml.transform.Result} that adds the resulting AXIOM OMElement to a specified parent
* element when <code>endDocument</code> is called.
*
* @author Arjen Poutsma
* @see AxiomHandler
* @since 1.5.0
*/
class AxiomResult extends SAXResult {
AxiomResult(OMContainer container, OMFactory factory) {
AxiomHandler handler = new AxiomHandler(container, factory);
super.setHandler(handler);
super.setLexicalHandler(handler);
}
/**
* Throws a <code>UnsupportedOperationException</code>.
*
* @throws UnsupportedOperationException always
*/
public void setHandler(ContentHandler handler) {
throw new UnsupportedOperationException("setHandler is not supported");
}
/**
* Throws a <code>UnsupportedOperationException</code>.
*
* @throws UnsupportedOperationException always
*/
public void setLexicalHandler(LexicalHandler handler) {
throw new UnsupportedOperationException("setLexicalHandler is not supported");
}
}

View File

@@ -20,7 +20,6 @@ import java.util.Iterator;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
@@ -70,7 +69,7 @@ abstract class AxiomSoapBody extends AxiomSoapElement implements SoapBody {
public Result getPayloadResult() {
AxiomUtils.removeContents(getAxiomBody());
return new SAXResult(new AxiomContentHandler(getAxiomBody(), getAxiomFactory()));
return new AxiomResult(getAxiomBody(), getAxiomFactory());
}
public boolean hasFault() {

View File

@@ -19,7 +19,6 @@ package org.springframework.ws.soap.axiom;
import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
@@ -57,7 +56,7 @@ class AxiomSoapFaultDetail extends AxiomSoapElement implements SoapFaultDetail {
}
public Result getResult() {
return new SAXResult(new AxiomContentHandler(getAxiomFaultDetail(), getAxiomFactory()));
return new AxiomResult(getAxiomFaultDetail(), getAxiomFactory());
}
protected SOAPFaultDetail getAxiomFaultDetail() {

View File

@@ -17,7 +17,6 @@
package org.springframework.ws.soap.axiom;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
@@ -39,7 +38,7 @@ class AxiomSoapFaultDetailElement extends AxiomSoapElement implements SoapFaultD
public Result getResult() {
try {
return new SAXResult(new AxiomContentHandler(getAxiomElement(), getAxiomFactory()));
return new AxiomResult(getAxiomElement(), getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);

View File

@@ -19,7 +19,6 @@ package org.springframework.ws.soap.axiom;
import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
@@ -46,7 +45,7 @@ abstract class AxiomSoapHeader extends AxiomSoapElement implements SoapHeader {
}
public Result getResult() {
return new SAXResult(new AxiomContentHandler(getAxiomHeader(), getAxiomFactory()));
return new AxiomResult(getAxiomHeader(), getAxiomFactory());
}
public SoapHeaderElement addHeaderElement(QName name) {

View File

@@ -17,7 +17,6 @@
package org.springframework.ws.soap.axiom;
import javax.xml.transform.Result;
import javax.xml.transform.sax.SAXResult;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAPFactory;
@@ -50,7 +49,7 @@ class AxiomSoapHeaderElement extends AxiomSoapElement implements SoapHeaderEleme
public Result getResult() {
try {
return new SAXResult(new AxiomContentHandler(getAxiomHeaderBlock(), getAxiomFactory()));
return new AxiomResult(getAxiomHeaderBlock(), getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);

View File

@@ -16,6 +16,7 @@
package org.springframework.ws.server.endpoint;
import javax.xml.soap.MessageFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
@@ -30,6 +31,8 @@ import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.axiom.AxiomSoapMessage;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
@@ -82,6 +85,23 @@ public class StaxStreamPayloadEndpointTest extends AbstractMessageEndpointTestCa
};
}
public void testSaajResponse() throws Exception {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
MessageFactory messageFactory = MessageFactory.newInstance();
SaajSoapMessage request = new SaajSoapMessage(messageFactory.createMessage());
transformer.transform(new StringSource(REQUEST), request.getPayloadResult());
SaajSoapMessageFactory soapMessageFactory = new SaajSoapMessageFactory();
soapMessageFactory.afterPropertiesSet();
MessageContext context = new DefaultMessageContext(request, soapMessageFactory);
MessageEndpoint endpoint = createResponseEndpoint();
endpoint.invoke(context);
assertTrue("context has not response", context.hasResponse());
StringResult stringResult = new StringResult();
transformer.transform(context.getResponse().getPayloadSource(), stringResult);
assertXMLEqual(RESPONSE, stringResult.toString());
}
public void testAxiomResponse() throws Exception {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMDocument;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.custommonkey.xmlunit.XMLTestCase;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
public class AxiomHandlerTest extends XMLTestCase {
private static final String XML_1 = "<?xml version='1.0' encoding='UTF-8'?>" + "<?pi content?>" +
"<root xmlns='namespace'>" +
"<prefix:child xmlns:prefix='namespace2' xmlns:prefix2='namespace3' prefix2:attr='value'>content</prefix:child>" +
"</root>";
private static final String XML_2_EXPECTED = "<?xml version='1.0' encoding='UTF-8'?>" + "<root xmlns='namespace'>" +
"<child xmlns='namespace2' />" + "</root>";
private static final String XML_2_SNIPPET =
"<?xml version='1.0' encoding='UTF-8'?>" + "<child xmlns='namespace2' />";
private AxiomHandler handler;
private OMDocument result;
private XMLReader xmlReader;
private OMFactory factory;
protected void setUp() throws Exception {
factory = OMAbstractFactory.getOMFactory();
result = factory.createOMDocument();
xmlReader = XMLReaderFactory.createXMLReader();
}
public void testContentHandlerDocumentNamespacePrefixes() throws Exception {
xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
handler = new AxiomHandler(result, factory);
xmlReader.setContentHandler(handler);
xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
xmlReader.parse(new InputSource(new StringReader(XML_1)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result.serialize(bos);
assertXMLEqual("Invalid result", XML_1, bos.toString("UTF-8"));
}
public void testContentHandlerDocumentNoNamespacePrefixes() throws Exception {
xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
handler = new AxiomHandler(result, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_1)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result.serialize(bos);
assertXMLEqual("Invalid result", XML_1, bos.toString("UTF-8"));
}
public void testContentHandlerElement() throws Exception {
OMNamespace namespace = factory.createOMNamespace("namespace", "");
OMElement rootElement = factory.createOMElement("root", namespace, result);
handler = new AxiomHandler(rootElement, factory);
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(new StringReader(XML_2_SNIPPET)));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result.serialize(bos);
assertXMLEqual("Invalid result", XML_2_EXPECTED, bos.toString("UTF-8"));
}
}

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>