diff --git a/org.springframework.core/src/main/java/org/springframework/util/xml/StaxEventContentHandler.java b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxEventContentHandler.java
new file mode 100644
index 0000000000..3f23918706
--- /dev/null
+++ b/org.springframework.core/src/main/java/org/springframework/util/xml/StaxEventContentHandler.java
@@ -0,0 +1,189 @@
+/*
+ * 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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Namespace;
+import javax.xml.stream.events.XMLEvent;
+import javax.xml.stream.util.XMLEventConsumer;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * SAX ContentHandler
that transforms callback calls to XMLEvent
s and writes them to a
+ * XMLEventConsumer
.
+ *
+ * @author Arjen Poutsma
+ * @see XMLEvent
+ * @see XMLEventConsumer
+ * @since 3.0
+ */
+class StaxEventContentHandler extends AbstractStaxContentHandler {
+
+ private final XMLEventFactory eventFactory;
+
+ private final XMLEventConsumer eventConsumer;
+
+ private Locator locator;
+
+ /**
+ * Constructs a new instance of the StaxEventContentHandler
that writes to the given
+ * XMLEventConsumer
. A default XMLEventFactory
will be created.
+ *
+ * @param consumer the consumer to write events to
+ */
+ StaxEventContentHandler(XMLEventConsumer consumer) {
+ eventFactory = XMLEventFactory.newInstance();
+ eventConsumer = consumer;
+ }
+
+ /**
+ * Constructs a new instance of the StaxEventContentHandler
that uses the given event factory to create
+ * events and writes to the given XMLEventConsumer
.
+ *
+ * @param consumer the consumer to write events to
+ * @param factory the factory used to create events
+ */
+ StaxEventContentHandler(XMLEventConsumer consumer, XMLEventFactory factory) {
+ eventFactory = factory;
+ eventConsumer = consumer;
+ }
+
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ @Override
+ protected void startDocumentInternal() throws XMLStreamException {
+ consumeEvent(eventFactory.createStartDocument());
+ }
+
+ @Override
+ protected void endDocumentInternal() throws XMLStreamException {
+ consumeEvent(eventFactory.createEndDocument());
+ }
+
+ @Override
+ protected void startElementInternal(QName name, Attributes atts, SimpleNamespaceContext namespaceContext)
+ throws XMLStreamException {
+ List attributes = getAttributes(atts);
+ List namespaces = createNamespaces(namespaceContext);
+ consumeEvent(eventFactory.createStartElement(name, attributes.iterator(), namespaces.iterator()));
+ }
+
+ @Override
+ protected void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) throws XMLStreamException {
+ List namespaces = createNamespaces(namespaceContext);
+ consumeEvent(eventFactory.createEndElement(name, namespaces.iterator()));
+ }
+
+ @Override
+ protected void charactersInternal(char[] ch, int start, int length) throws XMLStreamException {
+ consumeEvent(eventFactory.createCharacters(new String(ch, start, length)));
+ }
+
+ @Override
+ protected void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException {
+ consumeEvent(eventFactory.createIgnorableSpace(new String(ch, start, length)));
+ }
+
+ @Override
+ protected void processingInstructionInternal(String target, String data) throws XMLStreamException {
+ consumeEvent(eventFactory.createProcessingInstruction(target, data));
+ }
+
+ private void consumeEvent(XMLEvent event) throws XMLStreamException {
+ if (locator != null) {
+ eventFactory.setLocation(new SaxLocation(locator));
+ }
+ eventConsumer.add(event);
+ }
+
+ /** Creates and returns a list of NameSpace
objects from the NamespaceContext
. */
+ private List createNamespaces(SimpleNamespaceContext namespaceContext) {
+ List namespaces = new ArrayList();
+ String defaultNamespaceUri = namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX);
+ if (StringUtils.hasLength(defaultNamespaceUri)) {
+ namespaces.add(eventFactory.createNamespace(defaultNamespaceUri));
+ }
+ for (Iterator iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext();) {
+ String prefix = (String) iterator.next();
+ String namespaceUri = namespaceContext.getNamespaceURI(prefix);
+ namespaces.add(eventFactory.createNamespace(prefix, namespaceUri));
+ }
+ return namespaces;
+ }
+
+ private List getAttributes(Attributes attributes) {
+ List list = new ArrayList();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ QName name = toQName(attributes.getURI(i), attributes.getQName(i));
+ if (!("xmlns".equals(name.getLocalPart()) || "xmlns".equals(name.getPrefix()))) {
+ list.add(eventFactory.createAttribute(name, attributes.getValue(i)));
+ }
+ }
+ return list;
+ }
+
+ //
+ // No operation
+ //
+
+ @Override
+ protected void skippedEntityInternal(String name) throws XMLStreamException {
+ }
+
+ private static class SaxLocation implements Location {
+
+ private Locator locator;
+
+ private SaxLocation(Locator locator) {
+ this.locator = locator;
+ }
+
+ public int getLineNumber() {
+ return locator.getLineNumber();
+ }
+
+ public int getColumnNumber() {
+ return locator.getColumnNumber();
+ }
+
+ public int getCharacterOffset() {
+ return -1;
+ }
+
+ public String getPublicId() {
+ return locator.getPublicId();
+ }
+
+ public String getSystemId() {
+ return locator.getSystemId();
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.springframework.core/src/test/java/org/springframework/util/xml/StaxEventContentHandlerTests.java b/org.springframework.core/src/test/java/org/springframework/util/xml/StaxEventContentHandlerTests.java
new file mode 100644
index 0000000000..da1657b891
--- /dev/null
+++ b/org.springframework.core/src/test/java/org/springframework/util/xml/StaxEventContentHandlerTests.java
@@ -0,0 +1,30 @@
+/*
+ * 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.Writer;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+
+public class StaxEventContentHandlerTests extends AbstractStaxContentHandlerTestCase {
+
+ @Override
+ protected AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException {
+ XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
+ return new StaxEventContentHandler(outputFactory.createXMLEventWriter(writer));
+ }
+}
\ No newline at end of file