From ccd59ca088a90ca2a471cc211999b0bc63a6c95e Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 7 Jan 2009 16:20:40 +0000 Subject: [PATCH] Added StaxUtils, hidden StaxSource, StaxResult, and various other Stax-specific classes behind this utility class. --- .../util/xml/AbstractXMLStreamReader.java | 182 +++++++++++ .../springframework/util/xml/DomUtils.java | 1 + .../springframework/util/xml/SaxUtils.java | 59 ++++ .../springframework/util/xml/StaxResult.java | 16 +- .../springframework/util/xml/StaxSource.java | 10 +- .../springframework/util/xml/StaxUtils.java | 284 ++++++++++++++++++ .../util/xml/TransformerUtils.java | 158 ---------- .../util/xml/XMLEventStreamReader.java | 254 ++++++++++++++++ .../util/xml/StaxUtilsTest.java | 92 ++++++ .../util/xml/TransformerUtilsTests.java | 69 +---- .../util/xml/XMLEventStreamReaderTest.java | 62 ++++ 11 files changed, 949 insertions(+), 238 deletions(-) create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/SaxUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java create mode 100644 org.springframework.core/src/test/java/org/springframework/util/xml/StaxUtilsTest.java create mode 100644 org.springframework.core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTest.java diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java b/org.springframework.core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java new file mode 100644 index 0000000000..01150501f3 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/AbstractXMLStreamReader.java @@ -0,0 +1,182 @@ +/* + * Copyright 2002-2009 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.util.xml; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.springframework.util.Assert; + +/** + * Abstract base class for XMLStreamReaders. + * + * @author Arjen Poutsma + * @since 3.0 + */ +abstract class AbstractXMLStreamReader implements XMLStreamReader { + + public String getElementText() throws XMLStreamException { + if (getEventType() != XMLStreamConstants.START_ELEMENT) { + throw new XMLStreamException("parser must be on START_ELEMENT to read next text", getLocation()); + } + int eventType = next(); + StringBuilder builder = new StringBuilder(); + while (eventType != XMLStreamConstants.END_ELEMENT) { + if (eventType == XMLStreamConstants.CHARACTERS || eventType == XMLStreamConstants.CDATA || + eventType == XMLStreamConstants.SPACE || eventType == XMLStreamConstants.ENTITY_REFERENCE) { + builder.append(getText()); + } + else if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION || + eventType == XMLStreamConstants.COMMENT) { + // skipping + } + else if (eventType == XMLStreamConstants.END_DOCUMENT) { + throw new XMLStreamException("unexpected end of document when reading element text content", + getLocation()); + } + else if (eventType == XMLStreamConstants.START_ELEMENT) { + throw new XMLStreamException("element text content may not contain START_ELEMENT", getLocation()); + } + else { + throw new XMLStreamException("Unexpected event type " + eventType, getLocation()); + } + eventType = next(); + } + return builder.toString(); + } + + public String getAttributeLocalName(int index) { + return getAttributeName(index).getLocalPart(); + } + + public String getAttributeNamespace(int index) { + return getAttributeName(index).getNamespaceURI(); + } + + public String getAttributePrefix(int index) { + return getAttributeName(index).getPrefix(); + } + + public String getNamespaceURI() { + int eventType = getEventType(); + if (eventType == XMLStreamConstants.START_ELEMENT || eventType == XMLStreamConstants.END_ELEMENT) { + return getName().getNamespaceURI(); + } + else { + throw new IllegalStateException("parser must be on START_ELEMENT or END_ELEMENT state"); + } + } + + public String getNamespaceURI(String prefix) { + Assert.notNull(prefix, "No prefix given"); + return getNamespaceContext().getNamespaceURI(prefix); + } + + public boolean hasText() { + int eventType = getEventType(); + return eventType == XMLStreamConstants.SPACE || eventType == XMLStreamConstants.CHARACTERS || + eventType == XMLStreamConstants.COMMENT || eventType == XMLStreamConstants.CDATA || + eventType == XMLStreamConstants.ENTITY_REFERENCE; + } + + public String getPrefix() { + int eventType = getEventType(); + if (eventType == XMLStreamConstants.START_ELEMENT || eventType == XMLStreamConstants.END_ELEMENT) { + return getName().getPrefix(); + } + else { + throw new IllegalStateException("parser must be on START_ELEMENT or END_ELEMENT state"); + } + } + + public boolean hasName() { + int eventType = getEventType(); + return eventType == XMLStreamConstants.START_ELEMENT || eventType == XMLStreamConstants.END_ELEMENT; + } + + public boolean isWhiteSpace() { + return getEventType() == XMLStreamConstants.SPACE; + } + + public boolean isStartElement() { + return getEventType() == XMLStreamConstants.START_ELEMENT; + } + + public boolean isEndElement() { + return getEventType() == XMLStreamConstants.END_ELEMENT; + } + + public boolean isCharacters() { + return getEventType() == XMLStreamConstants.CHARACTERS; + } + + public int nextTag() throws XMLStreamException { + int eventType = next(); + while (eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace() || + eventType == XMLStreamConstants.CDATA && isWhiteSpace() || eventType == XMLStreamConstants.SPACE || + eventType == XMLStreamConstants.PROCESSING_INSTRUCTION || eventType == XMLStreamConstants.COMMENT) { + eventType = next(); + } + if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) { + throw new XMLStreamException("expected start or end tag", getLocation()); + } + return eventType; + } + + public void require(int expectedType, String namespaceURI, String localName) throws XMLStreamException { + int eventType = getEventType(); + if (eventType != expectedType) { + throw new XMLStreamException("Expected [" + expectedType + "] but read [" + eventType + "]"); + } + } + + public String getAttributeValue(String namespaceURI, String localName) { + for (int i = 0; i < getAttributeCount(); i++) { + QName name = getAttributeName(i); + if (name.getNamespaceURI().equals(namespaceURI) && name.getLocalPart().equals(localName)) { + return getAttributeValue(i); + } + } + return null; + } + + public boolean hasNext() throws XMLStreamException { + return getEventType() != END_DOCUMENT; + } + + public String getLocalName() { + return getName().getLocalPart(); + } + + public char[] getTextCharacters() { + return getText().toCharArray(); + } + + public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) + throws XMLStreamException { + char[] source = getTextCharacters(); + length = Math.min(length, source.length); + System.arraycopy(source, sourceStart, target, targetStart, length); + return length; + } + + public int getTextLength() { + return getText().length(); + } +} diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/DomUtils.java b/org.springframework.core/src/main/java/org/springframework/util/xml/DomUtils.java index 6fdae8221e..7696d6b23b 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/xml/DomUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/DomUtils.java @@ -37,6 +37,7 @@ import org.springframework.util.Assert; * @author Juergen Hoeller * @author Rob Harrop * @author Costin Leau + * @author Arjen Poutsma * @see org.w3c.dom.Node * @see org.w3c.dom.Element * @since 1.2 diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/SaxUtils.java b/org.springframework.core/src/main/java/org/springframework/util/xml/SaxUtils.java new file mode 100644 index 0000000000..823da314fc --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/SaxUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2009 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.util.xml; + +import java.io.IOException; + +import org.xml.sax.InputSource; + +import org.springframework.core.io.Resource; + +/** + * Convenient utility methods for dealing with SAX. + * + * @author Arjen Poutsma + * @since 3.0 + */ +public abstract class SaxUtils { + + /** + * Creates a SAX InputSource from the given resource. Sets the system identifier to the resource's + * URL, if available. + * + * @param resource the resource + * @return the input source created from the resource + * @throws IOException if an I/O exception occurs + * @see InputSource#setSystemId(String) + * @see #getSystemId(org.springframework.core.io.Resource) + */ + public static InputSource createInputSource(Resource resource) throws IOException { + InputSource inputSource = new InputSource(resource.getInputStream()); + inputSource.setSystemId(getSystemId(resource)); + return inputSource; + } + + /** Retrieves the URL from the given resource as System ID. Returns null if it cannot be openened. */ + private static String getSystemId(Resource resource) { + try { + return resource.getURI().toString(); + } + catch (IOException e) { + return null; + } + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/StaxResult.java b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxResult.java index b9a5e61753..47aef074b9 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/xml/StaxResult.java +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxResult.java @@ -43,7 +43,7 @@ import org.xml.sax.ContentHandler; * @see javax.xml.transform.Transformer * @since 3.0 */ -public class StaxResult extends SAXResult { +class StaxResult extends SAXResult { private XMLEventWriter eventWriter; @@ -54,7 +54,7 @@ public class StaxResult extends SAXResult { * * @param streamWriter the XMLStreamWriter to write to */ - public StaxResult(XMLStreamWriter streamWriter) { + StaxResult(XMLStreamWriter streamWriter) { super.setHandler(new StaxStreamContentHandler(streamWriter)); this.streamWriter = streamWriter; } @@ -64,7 +64,7 @@ public class StaxResult extends SAXResult { * * @param eventWriter the XMLEventWriter to write to */ - public StaxResult(XMLEventWriter eventWriter) { + StaxResult(XMLEventWriter eventWriter) { super.setHandler(new StaxEventContentHandler(eventWriter)); this.eventWriter = eventWriter; } @@ -76,7 +76,7 @@ public class StaxResult extends SAXResult { * @param eventWriter the XMLEventWriter to write to * @param eventFactory the XMLEventFactory to use for creating events */ - public StaxResult(XMLEventWriter eventWriter, XMLEventFactory eventFactory) { + StaxResult(XMLEventWriter eventWriter, XMLEventFactory eventFactory) { super.setHandler(new StaxEventContentHandler(eventWriter, eventFactory)); this.eventWriter = eventWriter; } @@ -88,18 +88,18 @@ public class StaxResult extends SAXResult { * @return the StAX event writer used by this result * @see #StaxResult(javax.xml.stream.XMLEventWriter) */ - public XMLEventWriter getXMLEventWriter() { + XMLEventWriter getXMLEventWriter() { return eventWriter; } /** - * Returns the XMLStreamWriter used by this StaxResult. If this StaxResult - * was created with an XMLEventConsumer, the result will be null. + * Returns the XMLStreamWriter used by this StaxResult. If this StaxResult was + * created with an XMLEventConsumer, the result will be null. * * @return the StAX stream writer used by this result * @see #StaxResult(javax.xml.stream.XMLStreamWriter) */ - public XMLStreamWriter getXMLStreamWriter() { + XMLStreamWriter getXMLStreamWriter() { return streamWriter; } diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/StaxSource.java b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxSource.java index 01db225bd1..9a0b8d11c2 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/xml/StaxSource.java +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxSource.java @@ -43,7 +43,7 @@ import org.xml.sax.XMLReader; * @see javax.xml.transform.Transformer * @since 3.0 */ -public class StaxSource extends SAXSource { +class StaxSource extends SAXSource { private XMLEventReader eventReader; @@ -57,7 +57,7 @@ public class StaxSource extends SAXSource { * @param streamReader the XMLStreamReader to read from * @throws IllegalStateException if the reader is not at the start of a document or element */ - public StaxSource(XMLStreamReader streamReader) { + StaxSource(XMLStreamReader streamReader) { super(new StaxStreamXMLReader(streamReader), new InputSource()); this.streamReader = streamReader; } @@ -70,7 +70,7 @@ public class StaxSource extends SAXSource { * @param eventReader the XMLEventReader to read from * @throws IllegalStateException if the reader is not at the start of a document or element */ - public StaxSource(XMLEventReader eventReader) { + StaxSource(XMLEventReader eventReader) { super(new StaxEventXMLReader(eventReader), new InputSource()); this.eventReader = eventReader; } @@ -82,7 +82,7 @@ public class StaxSource extends SAXSource { * @return the StAX event reader used by this source * @see StaxSource#StaxSource(javax.xml.stream.XMLEventReader) */ - public XMLEventReader getXMLEventReader() { + XMLEventReader getXMLEventReader() { return eventReader; } @@ -93,7 +93,7 @@ public class StaxSource extends SAXSource { * @return the StAX event reader used by this source * @see StaxSource#StaxSource(javax.xml.stream.XMLEventReader) */ - public XMLStreamReader getXMLStreamReader() { + XMLStreamReader getXMLStreamReader() { return streamReader; } diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java new file mode 100644 index 0000000000..0b87fdf417 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxUtils.java @@ -0,0 +1,284 @@ +/* + * Copyright 2002-2009 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.util.xml; + +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.stax.StAXResult; +import javax.xml.transform.stax.StAXSource; + +import org.xml.sax.ContentHandler; +import org.xml.sax.XMLReader; + +import org.springframework.util.Assert; + +/** + * Convenience methods for working with the StAX API. + * + * In particular, methods for using StAX in combination with the TrAX API (javax.xml.transform), and + * converting StAX readers/writers into SAX readers/handlers and vice-versa. + * + * @author Arjen Poutsma + * @since 3.0 + */ +public abstract class StaxUtils { + + /** + * Creates a StAX {@link Source} for the given {@link XMLStreamReader}. Returns a {@link StAXSource} under JAXP 1.4 or + * higher, or a {@link StaxSource} otherwise. + * + * @param streamReader the StAX stream reader + * @return a source wrapping streamReader + */ + public static Source createStaxSource(XMLStreamReader streamReader) { + if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.createStaxSource(streamReader); + } + else { + return new StaxSource(streamReader); + } + } + + /** + * Creates a StAX {@link Source} for the given {@link XMLEventReader}. Returns a {@link StAXSource} under JAXP 1.4 or + * higher, or a {@link StaxSource} otherwise. + * + * @param eventReader the StAX event reader + * @return a source wrapping streamReader + * @throws XMLStreamException in case of StAX errors + */ + public static Source createStaxSource(XMLEventReader eventReader) throws XMLStreamException { + if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.createStaxSource(eventReader); + } + else { + return new StaxSource(eventReader); + } + } + + /** + * Indicates whether the given {@link javax.xml.transform.Source} is a StAX Source. + * + * @return true if source is a Spring {@link org.springframework.util.xml.StaxSource} or JAXP + * 1.4 {@link javax.xml.transform.stax.StAXSource}; false otherwise. + */ + public static boolean isStaxSource(Source source) { + if (source instanceof StaxSource) { + return true; + } + else if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.isStaxSource(source); + } + else { + return false; + } + } + + /** + * Indicates whether the given {@link javax.xml.transform.Result} is a StAX Result. + * + * @return true if result is a Spring {@link org.springframework.util.xml.StaxResult} or JAXP + * 1.4 {@link javax.xml.transform.stax.StAXResult}; false otherwise. + */ + public static boolean isStaxResult(Result result) { + if (result instanceof StaxResult) { + return true; + } + else if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.isStaxResult(result); + } + else { + return false; + } + } + + /** + * Returns the {@link javax.xml.stream.XMLStreamReader} for the given StAX Source. + * + * @param source a Spring {@link org.springframework.util.xml.StaxSource} or {@link javax.xml.transform.stax.StAXSource} + * @return the {@link javax.xml.stream.XMLStreamReader} + * @throws IllegalArgumentException if source is neither a Spring-WS {@link org.springframework.util.xml.StaxSource} + * or {@link javax.xml.transform.stax.StAXSource} + */ + public static XMLStreamReader getXMLStreamReader(Source source) { + if (source instanceof StaxSource) { + return ((StaxSource) source).getXMLStreamReader(); + } + else if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.getXMLStreamReader(source); + } + else { + throw new IllegalArgumentException("Source '" + source + "' is neither StaxSource nor StAXSource"); + } + } + + /** + * Returns the {@link javax.xml.stream.XMLEventReader} for the given StAX Source. + * + * @param source a Spring {@link org.springframework.util.xml.StaxSource} or {@link javax.xml.transform.stax.StAXSource} + * @return the {@link javax.xml.stream.XMLEventReader} + * @throws IllegalArgumentException if source is neither a Spring {@link org.springframework.util.xml.StaxSource} + * or {@link javax.xml.transform.stax.StAXSource} + */ + public static XMLEventReader getXMLEventReader(Source source) { + if (source instanceof StaxSource) { + return ((StaxSource) source).getXMLEventReader(); + } + else if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.getXMLEventReader(source); + } + else { + throw new IllegalArgumentException("Source '" + source + "' is neither StaxSource nor StAXSource"); + } + } + + /** + * Returns the {@link javax.xml.stream.XMLStreamWriter} for the given StAX Result. + * + * @param result a Spring {@link org.springframework.util.xml.StaxResult} or {@link javax.xml.transform.stax.StAXResult} + * @return the {@link javax.xml.stream.XMLStreamReader} + * @throws IllegalArgumentException if source is neither a Spring {@link org.springframework.util.xml.StaxResult} + * or {@link javax.xml.transform.stax.StAXResult} + */ + public static XMLStreamWriter getXMLStreamWriter(Result result) { + if (result instanceof StaxResult) { + return ((StaxResult) result).getXMLStreamWriter(); + } + else if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.getXMLStreamWriter(result); + } + else { + throw new IllegalArgumentException("Result '" + result + "' is neither StaxResult nor StAXResult"); + } + } + + /** + * Returns the {@link XMLEventWriter} for the given StAX Result. + * + * @param result a Spring {@link org.springframework.util.xml.StaxResult} or {@link javax.xml.transform.stax.StAXResult} + * @return the {@link javax.xml.stream.XMLStreamReader} + * @throws IllegalArgumentException if source is neither a Spring {@link org.springframework.util.xml.StaxResult} + * or {@link javax.xml.transform.stax.StAXResult} + */ + public static XMLEventWriter getXMLEventWriter(Result result) { + if (result instanceof StaxResult) { + return ((StaxResult) result).getXMLEventWriter(); + } + else if (JaxpVersion.isAtLeastJaxp14()) { + return Jaxp14StaxHandler.getXMLEventWriter(result); + } + else { + throw new IllegalArgumentException("Result '" + result + "' is neither StaxResult nor StAXResult"); + } + } + + /** + * Creates a SAX {@link ContentHandler} that writes to the given StAX {@link XMLStreamWriter}. + * + * @param streamWriter the StAX stream writer + * @return a content handler writing to the streamWriter + */ + public static ContentHandler createContentHandler(XMLStreamWriter streamWriter) { + return new StaxStreamContentHandler(streamWriter); + } + + /** + * Creates a SAX {@link ContentHandler} that writes events to the given StAX {@link XMLEventWriter}. + * + * @param eventWriter the StAX event writer + * @return a content handler writing to the eventWriter + */ + public static ContentHandler createContentHandler(XMLEventWriter eventWriter) { + return new StaxEventContentHandler(eventWriter); + } + + /** + * Creates a SAX {@link XMLReader} that reads from the given StAX {@link XMLStreamReader}. + * + * @param streamReader the StAX stream reader + * @return a XMLReader reading from the streamWriter + */ + public static XMLReader createXMLReader(XMLStreamReader streamReader) { + return new StaxStreamXMLReader(streamReader); + } + + /** + * Creates a SAX {@link XMLReader} that reads from the given StAX {@link XMLEventReader}. + * + * @param eventReader the StAX event reader + * @return a XMLReader reading from the eventWriter + */ + public static XMLReader createXMLReader(XMLEventReader eventReader) { + return new StaxEventXMLReader(eventReader); + } + + /** + * Returns a {@link XMLStreamReader} that reads from a {@link XMLEventReader}. Useful, because the StAX + * XMLInputFactory allows one to create a event reader from a stream reader, but not vice-versa. + * + * @return a stream reader that reads from an event reader + */ + public static XMLStreamReader createEventStreamReader(XMLEventReader eventReader) throws XMLStreamException { + return new XMLEventStreamReader(eventReader); + } + + /** Inner class to avoid a static JAXP 1.4 dependency. */ + private static class Jaxp14StaxHandler { + + private static Source createStaxSource(XMLStreamReader streamReader) { + return new StAXSource(streamReader); + } + + private static Source createStaxSource(XMLEventReader eventReader) throws XMLStreamException { + return new StAXSource(eventReader); + } + + private static boolean isStaxSource(Source source) { + return source instanceof StAXSource; + } + + private static boolean isStaxResult(Result result) { + return result instanceof StAXResult; + } + + private static XMLStreamReader getXMLStreamReader(Source source) { + Assert.isInstanceOf(StAXSource.class, source); + return ((StAXSource) source).getXMLStreamReader(); + } + + private static XMLEventReader getXMLEventReader(Source source) { + Assert.isInstanceOf(StAXSource.class, source); + return ((StAXSource) source).getXMLEventReader(); + } + + private static XMLStreamWriter getXMLStreamWriter(Result result) { + Assert.isInstanceOf(StAXResult.class, result); + return ((StAXResult) result).getXMLStreamWriter(); + } + + private static XMLEventWriter getXMLEventWriter(Result result) { + Assert.isInstanceOf(StAXResult.class, result); + return ((StAXResult) result).getXMLEventWriter(); + } + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/TransformerUtils.java b/org.springframework.core/src/main/java/org/springframework/util/xml/TransformerUtils.java index 9f7c6bd486..7c615ade5d 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/xml/TransformerUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/TransformerUtils.java @@ -16,16 +16,8 @@ package org.springframework.util.xml; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLEventWriter; -import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.OutputKeys; -import javax.xml.transform.Result; -import javax.xml.transform.Source; import javax.xml.transform.Transformer; -import javax.xml.transform.stax.StAXResult; -import javax.xml.transform.stax.StAXSource; import org.springframework.util.Assert; @@ -35,7 +27,6 @@ import org.springframework.util.Assert; * * @author Rick Evans * @author Juergen Hoeller - * @author Arjen Poutsma * @since 2.5.5 */ public abstract class TransformerUtils { @@ -92,153 +83,4 @@ public abstract class TransformerUtils { transformer.setOutputProperty(OutputKeys.INDENT, "no"); } - /** - * Indicates whether the given {@link Source} is a StAX Source. - * - * @return true if source is a Spring {@link StaxSource} or JAXP 1.4 {@link StAXSource}; - * false otherwise. - */ - public static boolean isStaxSource(Source source) { - if (source instanceof StaxSource) { - return true; - } - else if (JaxpVersion.isAtLeastJaxp14()) { - return Jaxp14StaxHandler.isStaxSource(source); - } - else { - return false; - } - } - - /** - * Indicates whether the given {@link Result} is a StAX Result. - * - * @return true if result is a Spring {@link StaxResult} or JAXP 1.4 {@link StAXResult}; - * false otherwise. - */ - public static boolean isStaxResult(Result result) { - if (result instanceof StaxResult) { - return true; - } - else if (JaxpVersion.isAtLeastJaxp14()) { - return Jaxp14StaxHandler.isStaxResult(result); - } - else { - return false; - } - } - - /** - * Returns the {@link XMLStreamReader} for the given StAX Source. - * - * @param source a Spring {@link StaxSource} or {@link StAXSource} - * @return the {@link XMLStreamReader} - * @throws IllegalArgumentException if source is neither a Spring-WS {@link StaxSource} or {@link - * StAXSource} - */ - public static XMLStreamReader getXMLStreamReader(Source source) { - if (source instanceof StaxSource) { - return ((StaxSource) source).getXMLStreamReader(); - } - else if (JaxpVersion.isAtLeastJaxp14()) { - return Jaxp14StaxHandler.getXMLStreamReader(source); - } - else { - throw new IllegalArgumentException("Source '" + source + "' is neither StaxSource nor StAXSource"); - } - } - - /** - * Returns the {@link XMLEventReader} for the given StAX Source. - * - * @param source a Spring {@link StaxSource} or {@link StAXSource} - * @return the {@link XMLEventReader} - * @throws IllegalArgumentException if source is neither a Spring {@link StaxSource} or {@link - * StAXSource} - */ - public static XMLEventReader getXMLEventReader(Source source) { - if (source instanceof StaxSource) { - return ((StaxSource) source).getXMLEventReader(); - } - else if (JaxpVersion.isAtLeastJaxp14()) { - return Jaxp14StaxHandler.getXMLEventReader(source); - } - else { - throw new IllegalArgumentException("Source '" + source + "' is neither StaxSource nor StAXSource"); - } - } - - /** - * Returns the {@link XMLStreamWriter} for the given StAX Result. - * - * @param result a Spring {@link StaxResult} or {@link StAXResult} - * @return the {@link XMLStreamReader} - * @throws IllegalArgumentException if source is neither a Spring {@link StaxResult} or {@link - * StAXResult} - */ - public static XMLStreamWriter getXMLStreamWriter(Result result) { - if (result instanceof StaxResult) { - return ((StaxResult) result).getXMLStreamWriter(); - } - else if (JaxpVersion.isAtLeastJaxp14()) { - return Jaxp14StaxHandler.getXMLStreamWriter(result); - } - else { - throw new IllegalArgumentException("Result '" + result + "' is neither StaxResult nor StAXResult"); - } - } - - /** - * Returns the {@link XMLEventWriter} for the given StAX Result. - * - * @param result a Spring {@link StaxResult} or {@link StAXResult} - * @return the {@link XMLStreamReader} - * @throws IllegalArgumentException if source is neither a Spring {@link StaxResult} or {@link - * StAXResult} - */ - public static XMLEventWriter getXMLEventWriter(Result result) { - if (result instanceof StaxResult) { - return ((StaxResult) result).getXMLEventWriter(); - } - else if (JaxpVersion.isAtLeastJaxp14()) { - return Jaxp14StaxHandler.getXMLEventWriter(result); - } - else { - throw new IllegalArgumentException("Result '" + result + "' is neither StaxResult nor StAXResult"); - } - } - - /** Inner class to avoid a static JAXP 1.4 dependency. */ - private static class Jaxp14StaxHandler { - - private static boolean isStaxSource(Source source) { - return source instanceof StAXSource; - } - - private static boolean isStaxResult(Result result) { - return result instanceof StAXResult; - } - - private static XMLStreamReader getXMLStreamReader(Source source) { - Assert.isInstanceOf(StAXSource.class, source); - return ((StAXSource) source).getXMLStreamReader(); - } - - private static XMLEventReader getXMLEventReader(Source source) { - Assert.isInstanceOf(StAXSource.class, source); - return ((StAXSource) source).getXMLEventReader(); - } - - private static XMLStreamWriter getXMLStreamWriter(Result result) { - Assert.isInstanceOf(StAXResult.class, result); - return ((StAXResult) result).getXMLStreamWriter(); - } - - private static XMLEventWriter getXMLEventWriter(Result result) { - Assert.isInstanceOf(StAXResult.class, result); - return ((StAXResult) result).getXMLEventWriter(); - } - } - - } diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java b/org.springframework.core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java new file mode 100644 index 0000000000..ac20b03f52 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/util/xml/XMLEventStreamReader.java @@ -0,0 +1,254 @@ +/* + * Copyright 2002-2009 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.util.xml; + +import java.util.Iterator; +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.Namespace; +import javax.xml.stream.events.ProcessingInstruction; +import javax.xml.stream.events.StartDocument; +import javax.xml.stream.events.XMLEvent; + +/** + * Implementation of the XMLStreamReader interface that wraps a XMLEventReader. Useful, + * because the StAX XMLInputFactory allows one to create a event reader from a stream reader, but not + * vice-versa. + * + * @author Arjen Poutsma + * @since 3.0 + */ +class XMLEventStreamReader extends AbstractXMLStreamReader { + + private XMLEvent event; + + private final XMLEventReader eventReader; + + XMLEventStreamReader(XMLEventReader eventReader) throws XMLStreamException { + this.eventReader = eventReader; + event = eventReader.nextEvent(); + } + + public boolean isStandalone() { + if (event.isStartDocument()) { + return ((StartDocument) event).isStandalone(); + } + else { + throw new IllegalStateException(); + } + } + + public String getVersion() { + if (event.isStartDocument()) { + return ((StartDocument) event).getVersion(); + } + else { + throw new IllegalStateException(); + } + } + + public int getTextStart() { + return 0; + } + + public String getText() { + if (event.isCharacters()) { + return event.asCharacters().getData(); + } + else { + throw new IllegalStateException(); + } + } + + public String getPITarget() { + if (event.isProcessingInstruction()) { + return ((ProcessingInstruction) event).getTarget(); + } + else { + throw new IllegalStateException(); + } + } + + public String getPIData() { + if (event.isProcessingInstruction()) { + return ((ProcessingInstruction) event).getData(); + } + else { + throw new IllegalStateException(); + } + } + + public int getNamespaceCount() { + Iterator namespaces; + if (event.isStartElement()) { + namespaces = event.asStartElement().getNamespaces(); + } + else if (event.isEndElement()) { + namespaces = event.asEndElement().getNamespaces(); + } + else { + throw new IllegalStateException(); + } + return countIterator(namespaces); + } + + public NamespaceContext getNamespaceContext() { + if (event.isStartElement()) { + return event.asStartElement().getNamespaceContext(); + } + else { + throw new IllegalStateException(); + } + } + + public QName getName() { + if (event.isStartElement()) { + return event.asStartElement().getName(); + } + else if (event.isEndElement()) { + return event.asEndElement().getName(); + } + else { + throw new IllegalStateException(); + } + } + + public Location getLocation() { + return event.getLocation(); + } + + public int getEventType() { + return event.getEventType(); + } + + public String getEncoding() { + return null; + } + + public String getCharacterEncodingScheme() { + return null; + } + + public int getAttributeCount() { + if (!event.isStartElement()) { + throw new IllegalStateException(); + } + Iterator attributes = event.asStartElement().getAttributes(); + return countIterator(attributes); + } + + public void close() throws XMLStreamException { + eventReader.close(); + } + + public QName getAttributeName(int index) { + return getAttribute(index).getName(); + } + + public String getAttributeType(int index) { + return getAttribute(index).getDTDType(); + } + + public String getAttributeValue(int index) { + return getAttribute(index).getValue(); + } + + public String getNamespacePrefix(int index) { + return getNamespace(index).getPrefix(); + } + + public String getNamespaceURI(int index) { + return getNamespace(index).getNamespaceURI(); + } + + public Object getProperty(String name) throws IllegalArgumentException { + return eventReader.getProperty(name); + } + + public boolean isAttributeSpecified(int index) { + return getAttribute(index).isSpecified(); + } + + public int next() throws XMLStreamException { + event = eventReader.nextEvent(); + return event.getEventType(); + } + + public boolean standaloneSet() { + if (event.isStartDocument()) { + return ((StartDocument) event).standaloneSet(); + } + else { + throw new IllegalStateException(); + } + } + + private int countIterator(Iterator iterator) { + int count = 0; + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return count; + } + + private Attribute getAttribute(int index) { + if (!event.isStartElement()) { + throw new IllegalStateException(); + } + int count = 0; + Iterator attributes = event.asStartElement().getAttributes(); + while (attributes.hasNext()) { + Attribute attribute = (Attribute) attributes.next(); + if (count == index) { + return attribute; + } + else { + count++; + } + } + throw new IllegalArgumentException(); + } + + private Namespace getNamespace(int index) { + Iterator namespaces; + if (event.isStartElement()) { + namespaces = event.asStartElement().getNamespaces(); + } + else if (event.isEndElement()) { + namespaces = event.asEndElement().getNamespaces(); + } + else { + throw new IllegalStateException(); + } + int count = 0; + while (namespaces.hasNext()) { + Namespace namespace = (Namespace) namespaces.next(); + if (count == index) { + return namespace; + } + else { + count++; + } + } + throw new IllegalArgumentException(); + } +} diff --git a/org.springframework.core/src/test/java/org/springframework/util/xml/StaxUtilsTest.java b/org.springframework.core/src/test/java/org/springframework/util/xml/StaxUtilsTest.java new file mode 100644 index 0000000000..6b48e9c890 --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/util/xml/StaxUtilsTest.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2009 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.util.xml; + +import java.io.StringReader; +import java.io.StringWriter; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stax.StAXResult; +import javax.xml.transform.stax.StAXSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class StaxUtilsTest { + + @Test + public void isStaxSourceInvalid() throws Exception { + assertFalse("A StAX Source", StaxUtils.isStaxSource(new DOMSource())); + assertFalse("A StAX Source", StaxUtils.isStaxSource(new SAXSource())); + assertFalse("A StAX Source", StaxUtils.isStaxSource(new StreamSource())); + } + + @Test + public void isStaxSource() throws Exception { + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + String expected = ""; + XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new StringReader(expected)); + StaxSource source = new StaxSource(streamReader); + + assertTrue("Not a StAX Source", StaxUtils.isStaxSource(source)); + } + + @Test + public void isStaxSourceJaxp14() throws Exception { + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + String expected = ""; + XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new StringReader(expected)); + StAXSource source = new StAXSource(streamReader); + + assertTrue("Not a StAX Source", StaxUtils.isStaxSource(source)); + } + + @Test + public void isStaxResultInvalid() throws Exception { + assertFalse("A StAX Result", StaxUtils.isStaxResult(new DOMResult())); + assertFalse("A StAX Result", StaxUtils.isStaxResult(new SAXResult())); + assertFalse("A StAX Result", StaxUtils.isStaxResult(new StreamResult())); + } + + @Test + public void isStaxResult() throws Exception { + XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); + XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(new StringWriter()); + StaxResult result = new StaxResult(streamWriter); + + assertTrue("Not a StAX Result", StaxUtils.isStaxResult(result)); + } + + @Test + public void isStaxResultJaxp14() throws Exception { + XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); + XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(new StringWriter()); + StAXResult result = new StAXResult(streamWriter); + + assertTrue("Not a StAX Result", StaxUtils.isStaxResult(result)); + } + +} diff --git a/org.springframework.core/src/test/java/org/springframework/util/xml/TransformerUtilsTests.java b/org.springframework.core/src/test/java/org/springframework/util/xml/TransformerUtilsTests.java index b5ebc845d4..21eb30b72b 100644 --- a/org.springframework.core/src/test/java/org/springframework/util/xml/TransformerUtilsTests.java +++ b/org.springframework.core/src/test/java/org/springframework/util/xml/TransformerUtilsTests.java @@ -16,13 +16,7 @@ package org.springframework.util.xml; -import java.io.StringReader; -import java.io.StringWriter; import java.util.Properties; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.ErrorListener; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; @@ -30,16 +24,9 @@ import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.sax.SAXSource; -import javax.xml.transform.stax.StAXResult; -import javax.xml.transform.stax.StAXSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import org.junit.Test; /** @@ -102,58 +89,6 @@ public class TransformerUtilsTests { TransformerUtils.enableIndenting(new StubTransformer(), 0); } - @Test - public void isStaxSourceInvalid() throws Exception { - assertFalse("A StAX Source", TransformerUtils.isStaxSource(new DOMSource())); - assertFalse("A StAX Source", TransformerUtils.isStaxSource(new SAXSource())); - assertFalse("A StAX Source", TransformerUtils.isStaxSource(new StreamSource())); - } - - @Test - public void isStaxSource() throws Exception { - XMLInputFactory inputFactory = XMLInputFactory.newInstance(); - String expected = ""; - XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new StringReader(expected)); - StaxSource source = new StaxSource(streamReader); - - assertTrue("Not a StAX Source", TransformerUtils.isStaxSource(source)); - } - - @Test - public void isStaxSourceJaxp14() throws Exception { - XMLInputFactory inputFactory = XMLInputFactory.newInstance(); - String expected = ""; - XMLStreamReader streamReader = inputFactory.createXMLStreamReader(new StringReader(expected)); - StAXSource source = new StAXSource(streamReader); - - assertTrue("Not a StAX Source", TransformerUtils.isStaxSource(source)); - } - - @Test - public void isStaxResultInvalid() throws Exception { - assertFalse("A StAX Result", TransformerUtils.isStaxResult(new DOMResult())); - assertFalse("A StAX Result", TransformerUtils.isStaxResult(new SAXResult())); - assertFalse("A StAX Result", TransformerUtils.isStaxResult(new StreamResult())); - } - - @Test - public void isStaxResult() throws Exception { - XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(new StringWriter()); - StaxResult result = new StaxResult(streamWriter); - - assertTrue("Not a StAX Result", TransformerUtils.isStaxResult(result)); - } - - @Test - public void isStaxResultJaxp14() throws Exception { - XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(new StringWriter()); - StAXResult result = new StAXResult(streamWriter); - - assertTrue("Not a StAX Result", TransformerUtils.isStaxResult(result)); - } - private static class StubTransformer extends Transformer { private Properties outputProperties = new Properties(); diff --git a/org.springframework.core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTest.java b/org.springframework.core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTest.java new file mode 100644 index 0000000000..0b49ce03f5 --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/util/xml/XMLEventStreamReaderTest.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2009 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.util.xml; + +import java.io.StringReader; +import java.io.StringWriter; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLInputFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.junit.Before; +import org.junit.Test; + +public class XMLEventStreamReaderTest { + + private static final String XML = + "content" + ; + + private XMLEventStreamReader streamReader; + + @Before + public void createStreamReader() throws Exception { + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + XMLEventReader eventReader = inputFactory.createXMLEventReader(new StringReader(XML)); + streamReader = new XMLEventStreamReader(eventReader); + } + + @Test + public void readAll() throws Exception { + while (streamReader.hasNext()) { + streamReader.next(); + } + } + + @Test + public void readCorrect() throws Exception { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + StaxSource source = new StaxSource(streamReader); + StringWriter writer = new StringWriter(); + transformer.transform(source, new StreamResult(writer)); + assertXMLEqual(XML, writer.toString()); + } + +} \ No newline at end of file