diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java new file mode 100644 index 00000000..784f8f8d --- /dev/null +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomHandler.java @@ -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 endDocument is called. Used for returing SAXResults 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 { + } +} diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomResult.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomResult.java new file mode 100644 index 00000000..bed1e274 --- /dev/null +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomResult.java @@ -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 endDocument 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 UnsupportedOperationException. + * + * @throws UnsupportedOperationException always + */ + public void setHandler(ContentHandler handler) { + throw new UnsupportedOperationException("setHandler is not supported"); + } + + /** + * Throws a UnsupportedOperationException. + * + * @throws UnsupportedOperationException always + */ + public void setLexicalHandler(LexicalHandler handler) { + throw new UnsupportedOperationException("setLexicalHandler is not supported"); + } +} diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapBody.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapBody.java index 6c013e6b..8d55ded0 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapBody.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapBody.java @@ -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() { diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetail.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetail.java index ea276313..f7335311 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetail.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetail.java @@ -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() { diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetailElement.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetailElement.java index e032dbb7..b494b4f6 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetailElement.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapFaultDetailElement.java @@ -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); diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeader.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeader.java index 7804806b..ce5c2e83 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeader.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeader.java @@ -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) { diff --git a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java index fcf72500..b6ae1aa5 100644 --- a/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java +++ b/core/src/main/java/org/springframework/ws/soap/axiom/AxiomSoapHeaderElement.java @@ -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); diff --git a/core/src/test/java/org/springframework/ws/server/endpoint/StaxStreamPayloadEndpointTest.java b/core/src/test/java/org/springframework/ws/server/endpoint/StaxStreamPayloadEndpointTest.java index a40b254c..a86fb2ee 100644 --- a/core/src/test/java/org/springframework/ws/server/endpoint/StaxStreamPayloadEndpointTest.java +++ b/core/src/test/java/org/springframework/ws/server/endpoint/StaxStreamPayloadEndpointTest.java @@ -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(); diff --git a/core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java b/core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java new file mode 100644 index 00000000..bad5a63d --- /dev/null +++ b/core/src/test/java/org/springframework/ws/soap/axiom/AxiomHandlerTest.java @@ -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 = "" + "" + + "" + + "content" + + ""; + + private static final String XML_2_EXPECTED = "" + "" + + "" + ""; + + private static final String XML_2_SNIPPET = + "" + ""; + + 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")); + } +} \ No newline at end of file diff --git a/xml/src/main/java/org/springframework/xml/stream/StaxEventXmlReader.java b/xml/src/main/java/org/springframework/xml/stream/StaxEventXmlReader.java index 0cba124a..fa020b9b 100644 --- a/xml/src/main/java/org/springframework/xml/stream/StaxEventXmlReader.java +++ b/xml/src/main/java/org/springframework/xml/stream/StaxEventXmlReader.java @@ -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; } - } diff --git a/xml/src/main/java/org/springframework/xml/stream/StaxStreamXmlReader.java b/xml/src/main/java/org/springframework/xml/stream/StaxStreamXmlReader.java index 9613a363..1174e1e1 100644 --- a/xml/src/main/java/org/springframework/xml/stream/StaxStreamXmlReader.java +++ b/xml/src/main/java/org/springframework/xml/stream/StaxStreamXmlReader.java @@ -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()); } } diff --git a/xml/src/test/java/org/springframework/xml/stream/AbstractStaxXmlReaderTestCase.java b/xml/src/test/java/org/springframework/xml/stream/AbstractStaxXmlReaderTestCase.java index 22db5a23..fb9e8eb9 100644 --- a/xml/src/test/java/org/springframework/xml/stream/AbstractStaxXmlReaderTestCase.java +++ b/xml/src/test/java/org/springframework/xml/stream/AbstractStaxXmlReaderTestCase.java @@ -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 ArgumentMatcher 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) { diff --git a/xml/src/test/resources/org/springframework/xml/stream/testLexicalHandler.xml b/xml/src/test/resources/org/springframework/xml/stream/testLexicalHandler.xml new file mode 100644 index 00000000..9fc9243a --- /dev/null +++ b/xml/src/test/resources/org/springframework/xml/stream/testLexicalHandler.xml @@ -0,0 +1,8 @@ + +]> + + + + &entity; + \ No newline at end of file