diff --git a/build.gradle b/build.gradle index 71069d1281..9218d16f78 100644 --- a/build.gradle +++ b/build.gradle @@ -242,7 +242,7 @@ project("spring-core") { optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("net.sf.jopt-simple:jopt-simple:4.6") optional("log4j:log4j:1.2.17") - testCompile("xmlunit:xmlunit:1.3") + testCompile("xmlunit:xmlunit:1.5") testCompile("org.codehaus.woodstox:wstx-asl:3.2.7") { exclude group: "stax", module: "stax-api" } @@ -460,7 +460,7 @@ project("spring-oxm") { optional("org.codehaus.castor:castor-xml:1.3.2") testCompile(project(":spring-context")) testCompile("org.codehaus.jettison:jettison:1.0.1") - testCompile("xmlunit:xmlunit:1.3") + testCompile("xmlunit:xmlunit:1.5") testCompile("xmlpull:xmlpull:1.1.3.4a") testCompile(files(genCastor.classesDir).builtBy(genCastor)) testCompile(files(genJaxb.classesDir).builtBy(genJaxb)) @@ -567,7 +567,7 @@ project("spring-web") { } optional("log4j:log4j:1.2.17") testCompile(project(":spring-context-support")) // for JafMediaTypeFactory - testCompile("xmlunit:xmlunit:1.3") + testCompile("xmlunit:xmlunit:1.5") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") } @@ -703,7 +703,7 @@ project("spring-webmvc") { } testCompile(project(":spring-aop")) testCompile("rhino:js:1.7R1") - testCompile("xmlunit:xmlunit:1.3") + testCompile("xmlunit:xmlunit:1.5") testCompile("dom4j:dom4j:1.6.1") { exclude group: "xml-apis", module: "xml-apis" } @@ -806,7 +806,7 @@ project("spring-test") { optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.hamcrest:hamcrest-core:1.3") optional("com.jayway.jsonpath:json-path:0.9.0") - optional("xmlunit:xmlunit:1.3") + optional("xmlunit:xmlunit:1.5") testCompile(project(":spring-context-support")) testCompile(project(":spring-oxm")) testCompile(project(":spring-webmvc-tiles3")) diff --git a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxContentHandler.java b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxContentHandler.java deleted file mode 100644 index f494b88592..0000000000 --- a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxContentHandler.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2002-2012 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.XMLStreamException; - -import org.xml.sax.Attributes; -import org.xml.sax.ContentHandler; -import org.xml.sax.SAXException; - -/** - * Abstract base class for SAX {@code ContentHandler} implementations that use StAX as a basis. All methods - * delegate to internal template methods, capable of throwing a {@code XMLStreamException}. Additionally, an - * namespace context is used to keep track of declared namespaces. - * - * @author Arjen Poutsma - * @since 3.0 - */ -abstract class AbstractStaxContentHandler implements ContentHandler { - - private SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext(); - - private boolean namespaceContextChanged = false; - - @Override - public final void startDocument() throws SAXException { - namespaceContext.clear(); - namespaceContextChanged = false; - try { - startDocumentInternal(); - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle startDocument: " + ex.getMessage(), ex); - } - } - - protected abstract void startDocumentInternal() throws XMLStreamException; - - @Override - public final void endDocument() throws SAXException { - namespaceContext.clear(); - namespaceContextChanged = false; - try { - endDocumentInternal(); - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle startDocument: " + ex.getMessage(), ex); - } - } - - protected abstract void endDocumentInternal() throws XMLStreamException; - - /** - * Binds the given prefix to the given namespaces. - * - * @see SimpleNamespaceContext#bindNamespaceUri(String,String) - */ - @Override - public final void startPrefixMapping(String prefix, String uri) { - namespaceContext.bindNamespaceUri(prefix, uri); - namespaceContextChanged = true; - } - - /** - * Removes the binding for the given prefix. - * - * @see SimpleNamespaceContext#removeBinding(String) - */ - @Override - public final void endPrefixMapping(String prefix) { - namespaceContext.removeBinding(prefix); - namespaceContextChanged = true; - } - - @Override - public final void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { - try { - startElementInternal(toQName(uri, qName), atts, namespaceContextChanged ? namespaceContext : null); - namespaceContextChanged = false; - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle startElement: " + ex.getMessage(), ex); - } - } - - protected abstract void startElementInternal(QName name, Attributes atts, SimpleNamespaceContext namespaceContext) - throws XMLStreamException; - - @Override - public final void endElement(String uri, String localName, String qName) throws SAXException { - try { - endElementInternal(toQName(uri, qName), namespaceContextChanged ? namespaceContext : null); - namespaceContextChanged = false; - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle endElement: " + ex.getMessage(), ex); - } - } - - protected abstract void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) - throws XMLStreamException; - - @Override - public final void characters(char ch[], int start, int length) throws SAXException { - try { - charactersInternal(ch, start, length); - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle characters: " + ex.getMessage(), ex); - } - } - - protected abstract void charactersInternal(char[] ch, int start, int length) throws XMLStreamException; - - @Override - public final void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { - try { - ignorableWhitespaceInternal(ch, start, length); - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle ignorableWhitespace:" + ex.getMessage(), ex); - } - } - - protected abstract void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException; - - @Override - public final void processingInstruction(String target, String data) throws SAXException { - try { - processingInstructionInternal(target, data); - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle processingInstruction: " + ex.getMessage(), ex); - } - } - - protected abstract void processingInstructionInternal(String target, String data) throws XMLStreamException; - - @Override - public final void skippedEntity(String name) throws SAXException { - try { - skippedEntityInternal(name); - } - catch (XMLStreamException ex) { - throw new SAXException("Could not handle skippedEntity: " + ex.getMessage(), ex); - } - } - - /** - * Convert a namespace URI and DOM or SAX qualified name to a {@code QName}. The qualified name can have the form - * {@code prefix:localname} or {@code localName}. - * - * @param namespaceUri the namespace URI - * @param qualifiedName the qualified name - * @return a QName - */ - protected QName toQName(String namespaceUri, String qualifiedName) { - int idx = qualifiedName.indexOf(':'); - if (idx == -1) { - return new QName(namespaceUri, qualifiedName); - } - else { - String prefix = qualifiedName.substring(0, idx); - String localPart = qualifiedName.substring(idx + 1); - return new QName(namespaceUri, localPart, prefix); - } - } - - protected abstract void skippedEntityInternal(String name) throws XMLStreamException; -} diff --git a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java new file mode 100644 index 0000000000..afbdf9a03b --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java @@ -0,0 +1,273 @@ +/* + * Copyright 2002-2014 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.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; + +/** + * Abstract base class for SAX {@code ContentHandler} and {@code LexicalHandler} + * implementations that use StAX as a basis. All methods delegate to internal template + * methods, capable of throwing a {@code XMLStreamException}. Additionally, an namespace + * context stack is used to keep track of declared namespaces. + * + * @author Arjen Poutsma + * @since 4.0.3 + */ +abstract class AbstractStaxHandler implements ContentHandler, LexicalHandler { + + private final List> namespaceMappings = new ArrayList>(); + + private boolean inCData; + + + @Override + public final void startDocument() throws SAXException { + removeAllNamespaceMappings(); + newNamespaceMapping(); + try { + startDocumentInternal(); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle startDocument: " + ex.getMessage(), ex); + } + } + + @Override + public final void endDocument() throws SAXException { + removeAllNamespaceMappings(); + try { + endDocumentInternal(); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle endDocument: " + ex.getMessage(), ex); + } + } + + @Override + public final void startPrefixMapping(String prefix, String uri) { + currentNamespaceMapping().put(prefix, uri); + } + + @Override + public final void endPrefixMapping(String prefix) { + } + + @Override + public final void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { + try { + startElementInternal(toQName(uri, qName), atts, currentNamespaceMapping()); + newNamespaceMapping(); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle startElement: " + ex.getMessage(), ex); + } + } + + @Override + public final void endElement(String uri, String localName, String qName) throws SAXException { + try { + endElementInternal(toQName(uri, qName), currentNamespaceMapping()); + removeNamespaceMapping(); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle endElement: " + ex.getMessage(), ex); + } + } + + @Override + public final void characters(char ch[], int start, int length) throws SAXException { + try { + String data = new String(ch, start, length); + if (!this.inCData) { + charactersInternal(data); + } + else { + cDataInternal(data); + } + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle characters: " + ex.getMessage(), ex); + } + } + + @Override + public final void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + try { + ignorableWhitespaceInternal(new String(ch, start, length)); + } + catch (XMLStreamException ex) { + throw new SAXException( + "Could not handle ignorableWhitespace:" + ex.getMessage(), ex); + } + } + + @Override + public final void processingInstruction(String target, String data) throws SAXException { + try { + processingInstructionInternal(target, data); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle processingInstruction: " + ex.getMessage(), ex); + } + } + + @Override + public final void skippedEntity(String name) throws SAXException { + try { + skippedEntityInternal(name); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle skippedEntity: " + ex.getMessage(), ex); + } + } + + @Override + public final void startDTD(String name, String publicId, String systemId) throws SAXException { + try { + StringBuilder builder = new StringBuilder(""); + + dtdInternal(builder.toString()); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle startDTD: " + ex.getMessage(), ex); + } + } + + @Override + public final void endDTD() throws SAXException { + } + + @Override + public final void startCDATA() throws SAXException { + this.inCData = true; + } + + @Override + public final void endCDATA() throws SAXException { + this.inCData = false; + } + + @Override + public final void comment(char[] ch, int start, int length) throws SAXException { + try { + commentInternal(new String(ch, start, length)); + } + catch (XMLStreamException ex) { + throw new SAXException("Could not handle comment: " + ex.getMessage(), ex); + } + } + + @Override + public void startEntity(String name) throws SAXException { + } + + @Override + public void endEntity(String name) throws SAXException { + } + + /** + * Convert a namespace URI and DOM or SAX qualified name to a {@code QName}. The + * qualified name can have the form {@code prefix:localname} or {@code localName}. + * @param namespaceUri the namespace URI + * @param qualifiedName the qualified name + * @return a QName + */ + protected QName toQName(String namespaceUri, String qualifiedName) { + int idx = qualifiedName.indexOf(':'); + if (idx == -1) { + return new QName(namespaceUri, qualifiedName); + } + else { + String prefix = qualifiedName.substring(0, idx); + String localPart = qualifiedName.substring(idx + 1); + return new QName(namespaceUri, localPart, prefix); + } + } + + protected boolean isNamespaceDeclaration(QName qName) { + String prefix = qName.getPrefix(); + String localPart = qName.getLocalPart(); + return (XMLConstants.XMLNS_ATTRIBUTE.equals(localPart) && prefix.length() == 0) || + (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) && localPart.length() != 0); + } + + + private Map currentNamespaceMapping() { + return this.namespaceMappings.get(this.namespaceMappings.size() - 1); + } + + private void newNamespaceMapping() { + this.namespaceMappings.add(new HashMap()); + } + + private void removeNamespaceMapping() { + this.namespaceMappings.remove(this.namespaceMappings.size() - 1); + } + + private void removeAllNamespaceMappings() { + this.namespaceMappings.clear(); + } + + + protected abstract void startDocumentInternal() throws XMLStreamException; + + protected abstract void endDocumentInternal() throws XMLStreamException; + + protected abstract void startElementInternal(QName name, Attributes attributes, + Map namespaceMapping) throws XMLStreamException; + + protected abstract void endElementInternal(QName name, Map namespaceMapping) + throws XMLStreamException; + + protected abstract void charactersInternal(String data) throws XMLStreamException; + + protected abstract void cDataInternal(String data) throws XMLStreamException; + + protected abstract void ignorableWhitespaceInternal(String data) throws XMLStreamException; + + protected abstract void processingInstructionInternal(String target, String data) + throws XMLStreamException; + + protected abstract void skippedEntityInternal(String name) throws XMLStreamException; + + protected abstract void dtdInternal(String dtd) throws XMLStreamException; + + protected abstract void commentInternal(String comment) throws XMLStreamException; + +} diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxEventContentHandler.java b/spring-core/src/main/java/org/springframework/util/xml/StaxEventContentHandler.java deleted file mode 100644 index f48bad5c6d..0000000000 --- a/spring-core/src/main/java/org/springframework/util/xml/StaxEventContentHandler.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2002-2012 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.springframework.util.StringUtils; -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -/** - * SAX {@code ContentHandler} that transforms callback calls to {@code XMLEvent}s - * and writes them to a {@code XMLEventConsumer}. - * - * @author Arjen Poutsma - * @since 3.0 - * @see XMLEvent - * @see XMLEventConsumer - */ -class StaxEventContentHandler extends AbstractStaxContentHandler { - - private final XMLEventFactory eventFactory; - - private final XMLEventConsumer eventConsumer; - - - /** - * Construct a new instance of the {@code StaxEventContentHandler} that writes to the given - * {@code XMLEventConsumer}. A default {@code XMLEventFactory} will be created. - * @param consumer the consumer to write events to - */ - StaxEventContentHandler(XMLEventConsumer consumer) { - this.eventFactory = XMLEventFactory.newInstance(); - this.eventConsumer = consumer; - } - - /** - * Construct a new instance of the {@code StaxEventContentHandler} that uses the given - * event factory to create events and writes to the given {@code XMLEventConsumer}. - * @param consumer the consumer to write events to - * @param factory the factory used to create events - */ - StaxEventContentHandler(XMLEventConsumer consumer, XMLEventFactory factory) { - this.eventFactory = factory; - this.eventConsumer = consumer; - } - - @Override - public void setDocumentLocator(Locator locator) { - if (locator != null) { - this.eventFactory.setLocation(new LocatorLocationAdapter(locator)); - } - } - - @Override - protected void startDocumentInternal() throws XMLStreamException { - consumeEvent(this.eventFactory.createStartDocument()); - } - - @Override - protected void endDocumentInternal() throws XMLStreamException { - consumeEvent(this.eventFactory.createEndDocument()); - } - - @Override - protected void startElementInternal(QName name, Attributes atts, SimpleNamespaceContext namespaceContext) - throws XMLStreamException { - - List attributes = getAttributes(atts); - List namespaces = createNamespaces(namespaceContext); - consumeEvent(this.eventFactory.createStartElement(name, attributes.iterator(), - (namespaces != null ? namespaces.iterator() : null))); - } - - @Override - protected void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) throws XMLStreamException { - List namespaces = createNamespaces(namespaceContext); - consumeEvent(this.eventFactory.createEndElement(name, namespaces != null ? namespaces.iterator() : null)); - } - - @Override - protected void charactersInternal(char[] ch, int start, int length) throws XMLStreamException { - consumeEvent(this.eventFactory.createCharacters(new String(ch, start, length))); - } - - @Override - protected void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException { - consumeEvent(this.eventFactory.createIgnorableSpace(new String(ch, start, length))); - } - - @Override - protected void processingInstructionInternal(String target, String data) throws XMLStreamException { - consumeEvent(this.eventFactory.createProcessingInstruction(target, data)); - } - - private void consumeEvent(XMLEvent event) throws XMLStreamException { - this.eventConsumer.add(event); - } - - /** - * Create and return a list of {@code NameSpace} objects from the {@code NamespaceContext}. - */ - private List createNamespaces(SimpleNamespaceContext namespaceContext) { - if (namespaceContext == null) { - return null; - } - - List namespaces = new ArrayList(); - String defaultNamespaceUri = namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX); - if (StringUtils.hasLength(defaultNamespaceUri)) { - namespaces.add(this.eventFactory.createNamespace(defaultNamespaceUri)); - } - for (Iterator iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext();) { - String prefix = iterator.next(); - String namespaceUri = namespaceContext.getNamespaceURI(prefix); - namespaces.add(this.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(this.eventFactory.createAttribute(name, attributes.getValue(i))); - } - } - return list; - } - - /* No operation */ - @Override - protected void skippedEntityInternal(String name) throws XMLStreamException { - } - - - private static final class LocatorLocationAdapter implements Location { - - private final Locator locator; - - public LocatorLocationAdapter(Locator locator) { - this.locator = locator; - } - - @Override - public int getLineNumber() { - return this.locator.getLineNumber(); - } - - @Override - public int getColumnNumber() { - return this.locator.getColumnNumber(); - } - - @Override - public int getCharacterOffset() { - return -1; - } - - @Override - public String getPublicId() { - return this.locator.getPublicId(); - } - - @Override - public String getSystemId() { - return this.locator.getSystemId(); - } - } - -} diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxEventHandler.java b/spring-core/src/main/java/org/springframework/util/xml/StaxEventHandler.java new file mode 100644 index 0000000000..7a4151dc3e --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/xml/StaxEventHandler.java @@ -0,0 +1,196 @@ +/* + * Copyright 2002-2014 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.List; +import java.util.Map; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.Namespace; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.ext.LexicalHandler; + +/** + * SAX {@link org.xml.sax.ContentHandler} and {@link LexicalHandler} + * that writes to a {@link javax.xml.stream.util.XMLEventConsumer}. + * + * @author Arjen Poutsma + * @since 4.0.3 + */ +class StaxEventHandler extends AbstractStaxHandler { + + private final XMLEventFactory eventFactory; + + private final XMLEventWriter eventWriter; + + + /** + * Construct a new instance of the {@code StaxEventContentHandler} that writes to the + * given {@code XMLEventWriter}. A default {@code XMLEventFactory} will be created. + * @param eventWriter the writer to write events to + */ + public StaxEventHandler(XMLEventWriter eventWriter) { + this.eventFactory = XMLEventFactory.newInstance(); + this.eventWriter = eventWriter; + } + + /** + * Construct a new instance of the {@code StaxEventContentHandler} that uses the given + * event factory to create events and writes to the given {@code XMLEventConsumer}. + * @param eventWriter the writer to write events to + * @param factory the factory used to create events + */ + public StaxEventHandler(XMLEventWriter eventWriter, XMLEventFactory factory) { + this.eventFactory = factory; + this.eventWriter = eventWriter; + } + + + @Override + public void setDocumentLocator(Locator locator) { + if (locator != null) { + this.eventFactory.setLocation(new LocatorLocationAdapter(locator)); + } + } + + @Override + protected void startDocumentInternal() throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createStartDocument()); + } + + @Override + protected void endDocumentInternal() throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createEndDocument()); + } + + @Override + protected void startElementInternal(QName name, Attributes atts, + Map namespaceMapping) throws XMLStreamException { + + List attributes = getAttributes(atts); + List namespaces = getNamespaces(namespaceMapping); + this.eventWriter.add( + this.eventFactory.createStartElement(name, attributes.iterator(), namespaces.iterator())); + + } + + private List getNamespaces(Map namespaceMapping) { + List result = new ArrayList(); + for (Map.Entry entry : namespaceMapping.entrySet()) { + String prefix = entry.getKey(); + String namespaceUri = entry.getValue(); + result.add(this.eventFactory.createNamespace(prefix, namespaceUri)); + } + return result; + } + + private List getAttributes(Attributes attributes) { + List result = new ArrayList(); + for (int i = 0; i < attributes.getLength(); i++) { + QName attrName = toQName(attributes.getURI(i), attributes.getQName(i)); + if (!isNamespaceDeclaration(attrName)) { + result.add(this.eventFactory.createAttribute(attrName, attributes.getValue(i))); + } + } + return result; + } + + @Override + protected void endElementInternal(QName name, Map namespaceMapping) throws XMLStreamException { + List namespaces = getNamespaces(namespaceMapping); + this.eventWriter.add(this.eventFactory.createEndElement(name, namespaces.iterator())); + } + + @Override + protected void charactersInternal(String data) throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createCharacters(data)); + } + + @Override + protected void cDataInternal(String data) throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createCData(data)); + } + + @Override + protected void ignorableWhitespaceInternal(String data) throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createIgnorableSpace(data)); + } + + @Override + protected void processingInstructionInternal(String target, String data) throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createProcessingInstruction(target, data)); + } + + @Override + protected void dtdInternal(String dtd) throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createDTD(dtd)); + } + + @Override + protected void commentInternal(String comment) throws XMLStreamException { + this.eventWriter.add(this.eventFactory.createComment(comment)); + } + + // Ignored + + @Override + protected void skippedEntityInternal(String name) throws XMLStreamException { + } + + + private static final class LocatorLocationAdapter implements Location { + + private final Locator locator; + + public LocatorLocationAdapter(Locator locator) { + this.locator = locator; + } + + @Override + public int getLineNumber() { + return this.locator.getLineNumber(); + } + + @Override + public int getColumnNumber() { + return this.locator.getColumnNumber(); + } + + @Override + public int getCharacterOffset() { + return -1; + } + + @Override + public String getPublicId() { + return this.locator.getPublicId(); + } + + @Override + public String getSystemId() { + return this.locator.getSystemId(); + } + } + +} diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxResult.java b/spring-core/src/main/java/org/springframework/util/xml/StaxResult.java index 801769cdfa..ef91ceaeb2 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/StaxResult.java +++ b/spring-core/src/main/java/org/springframework/util/xml/StaxResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -22,6 +22,7 @@ import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.sax.SAXResult; import org.xml.sax.ContentHandler; +import org.xml.sax.ext.LexicalHandler; /** * Implementation of the {@code Result} tagging interface for StAX writers. Can be constructed with @@ -35,7 +36,8 @@ import org.xml.sax.ContentHandler; * {@code SAXResult} is not supported. In general, the only supported operation * on this class is to use the {@code ContentHandler} obtained via {@link #getHandler()} to parse an * input source using an {@code XMLReader}. Calling {@link #setHandler(org.xml.sax.ContentHandler)} - * will result in {@code UnsupportedOperationException}s. + * or {@link #setLexicalHandler(org.xml.sax.ext.LexicalHandler)} will result in + * {@code UnsupportedOperationException}s. * * @author Arjen Poutsma * @since 3.0 @@ -54,8 +56,10 @@ class StaxResult extends SAXResult { * Construct a new instance of the {@code StaxResult} with the specified {@code XMLStreamWriter}. * @param streamWriter the {@code XMLStreamWriter} to write to */ - StaxResult(XMLStreamWriter streamWriter) { - super.setHandler(new StaxStreamContentHandler(streamWriter)); + public StaxResult(XMLStreamWriter streamWriter) { + StaxStreamHandler handler = new StaxStreamHandler(streamWriter); + super.setHandler(handler); + super.setLexicalHandler(handler); this.streamWriter = streamWriter; } @@ -63,19 +67,10 @@ class StaxResult extends SAXResult { * Construct a new instance of the {@code StaxResult} with the specified {@code XMLEventWriter}. * @param eventWriter the {@code XMLEventWriter} to write to */ - StaxResult(XMLEventWriter eventWriter) { - super.setHandler(new StaxEventContentHandler(eventWriter)); - this.eventWriter = eventWriter; - } - - /** - * Construct a new instance of the {@code StaxResult} with the specified {@code XMLEventWriter} - * and {@code XMLEventFactory}. - * @param eventWriter the {@code XMLEventWriter} to write to - * @param eventFactory the {@code XMLEventFactory} to use for creating events - */ - StaxResult(XMLEventWriter eventWriter, XMLEventFactory eventFactory) { - super.setHandler(new StaxEventContentHandler(eventWriter, eventFactory)); + public StaxResult(XMLEventWriter eventWriter) { + StaxEventHandler handler = new StaxEventHandler(eventWriter); + super.setHandler(handler); + super.setLexicalHandler(handler); this.eventWriter = eventWriter; } @@ -86,7 +81,7 @@ class StaxResult extends SAXResult { * @return the StAX event writer used by this result * @see #StaxResult(javax.xml.stream.XMLEventWriter) */ - XMLEventWriter getXMLEventWriter() { + public XMLEventWriter getXMLEventWriter() { return this.eventWriter; } @@ -96,7 +91,7 @@ class StaxResult extends SAXResult { * @return the StAX stream writer used by this result * @see #StaxResult(javax.xml.stream.XMLStreamWriter) */ - XMLStreamWriter getXMLStreamWriter() { + public XMLStreamWriter getXMLStreamWriter() { return this.streamWriter; } @@ -110,4 +105,13 @@ class StaxResult extends SAXResult { throw new UnsupportedOperationException("setHandler is not supported"); } + /** + * Throws an {@code UnsupportedOperationException}. + * @throws UnsupportedOperationException always + */ + @Override + public void setLexicalHandler(LexicalHandler handler) { + throw new UnsupportedOperationException("setLexicalHandler is not supported"); + } + } diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxStreamContentHandler.java b/spring-core/src/main/java/org/springframework/util/xml/StaxStreamContentHandler.java deleted file mode 100644 index 284f69d0ac..0000000000 --- a/spring-core/src/main/java/org/springframework/util/xml/StaxStreamContentHandler.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2002-2012 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.QName; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * SAX {@code ContentHandler} that writes to a {@code XMLStreamWriter}. - * - * @author Arjen Poutsma - * @see XMLStreamWriter - * @since 3.0 - */ -class StaxStreamContentHandler extends AbstractStaxContentHandler { - - private final XMLStreamWriter streamWriter; - - /** - * Constructs a new instance of the {@code StaxStreamContentHandler} that writes to the given - * {@code XMLStreamWriter}. - * - * @param streamWriter the stream writer to write to - */ - StaxStreamContentHandler(XMLStreamWriter streamWriter) { - Assert.notNull(streamWriter, "'streamWriter' must not be null"); - this.streamWriter = streamWriter; - } - - @Override - public void setDocumentLocator(Locator locator) { - } - - @Override - protected void charactersInternal(char[] ch, int start, int length) throws XMLStreamException { - streamWriter.writeCharacters(ch, start, length); - } - - @Override - protected void endDocumentInternal() throws XMLStreamException { - streamWriter.writeEndDocument(); - } - - @Override - protected void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) throws XMLStreamException { - streamWriter.writeEndElement(); - } - - @Override - protected void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException { - streamWriter.writeCharacters(ch, start, length); - } - - @Override - protected void processingInstructionInternal(String target, String data) throws XMLStreamException { - streamWriter.writeProcessingInstruction(target, data); - } - - @Override - protected void skippedEntityInternal(String name) { - } - - @Override - protected void startDocumentInternal() throws XMLStreamException { - streamWriter.writeStartDocument(); - } - - @Override - protected void startElementInternal(QName name, Attributes attributes, SimpleNamespaceContext namespaceContext) - throws XMLStreamException { - streamWriter.writeStartElement(name.getPrefix(), name.getLocalPart(), name.getNamespaceURI()); - if (namespaceContext != null) { - String defaultNamespaceUri = namespaceContext.getNamespaceURI(""); - if (StringUtils.hasLength(defaultNamespaceUri)) { - streamWriter.writeNamespace("", defaultNamespaceUri); - streamWriter.setDefaultNamespace(defaultNamespaceUri); - } - for (Iterator iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext();) { - String prefix = iterator.next(); - streamWriter.writeNamespace(prefix, namespaceContext.getNamespaceURI(prefix)); - streamWriter.setPrefix(prefix, namespaceContext.getNamespaceURI(prefix)); - } - } - for (int i = 0; i < attributes.getLength(); i++) { - QName attrName = toQName(attributes.getURI(i), attributes.getQName(i)); - if (!("xmlns".equals(attrName.getLocalPart()) || "xmlns".equals(attrName.getPrefix()))) { - streamWriter.writeAttribute(attrName.getPrefix(), attrName.getNamespaceURI(), attrName.getLocalPart(), - attributes.getValue(i)); - } - } - } -} diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxStreamHandler.java b/spring-core/src/main/java/org/springframework/util/xml/StaxStreamHandler.java new file mode 100644 index 0000000000..6323b44675 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/xml/StaxStreamHandler.java @@ -0,0 +1,139 @@ +/* + * Copyright 2002-2014 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.Map; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; + +import org.springframework.util.Assert; + +/** + * SAX {@link org.xml.sax.ContentHandler} and {@link LexicalHandler} + * that writes to an {@link XMLStreamWriter}. + * + * @author Arjen Poutsma + * @since 4.0.3 + */ +class StaxStreamHandler extends AbstractStaxHandler { + + private final XMLStreamWriter streamWriter; + + + public StaxStreamHandler(XMLStreamWriter streamWriter) { + Assert.notNull(streamWriter, "XMLStreamWriter must not be null"); + this.streamWriter = streamWriter; + } + + + @Override + protected void startDocumentInternal() throws XMLStreamException { + this.streamWriter.writeStartDocument(); + } + + @Override + protected void endDocumentInternal() throws XMLStreamException { + this.streamWriter.writeEndDocument(); + } + + @Override + protected void startElementInternal(QName name, Attributes attributes, + Map namespaceMapping) throws XMLStreamException { + + this.streamWriter.writeStartElement(name.getPrefix(), name.getLocalPart(), name.getNamespaceURI()); + + for (Map.Entry entry : namespaceMapping.entrySet()) { + String prefix = entry.getKey(); + String namespaceUri = entry.getValue(); + this.streamWriter.writeNamespace(prefix, namespaceUri); + if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { + this.streamWriter.setDefaultNamespace(namespaceUri); + } + else { + this.streamWriter.setPrefix(prefix, namespaceUri); + } + } + for (int i = 0; i < attributes.getLength(); i++) { + QName attrName = toQName(attributes.getURI(i), attributes.getQName(i)); + if (!isNamespaceDeclaration(attrName)) { + this.streamWriter.writeAttribute(attrName.getPrefix(), attrName.getNamespaceURI(), + attrName.getLocalPart(), attributes.getValue(i)); + } + } + } + + @Override + protected void endElementInternal(QName name, Map namespaceMapping) throws XMLStreamException { + this.streamWriter.writeEndElement(); + } + + @Override + protected void charactersInternal(String data) throws XMLStreamException { + this.streamWriter.writeCharacters(data); + } + + @Override + protected void cDataInternal(String data) throws XMLStreamException { + this.streamWriter.writeCData(data); + } + + @Override + protected void ignorableWhitespaceInternal(String data) throws XMLStreamException { + this.streamWriter.writeCharacters(data); + } + + @Override + protected void processingInstructionInternal(String target, String data) throws XMLStreamException { + this.streamWriter.writeProcessingInstruction(target, data); + } + + @Override + protected void dtdInternal(String dtd) throws XMLStreamException { + this.streamWriter.writeDTD(dtd); + } + + @Override + protected void commentInternal(String comment) throws XMLStreamException { + this.streamWriter.writeComment(comment); + } + + // Ignored + + @Override + public void setDocumentLocator(Locator locator) { + } + + @Override + public void startEntity(String name) throws SAXException { + } + + @Override + public void endEntity(String name) throws SAXException { + } + + @Override + protected void skippedEntityInternal(String name) throws XMLStreamException { + } + +} diff --git a/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java b/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java index e19381a588..eb98be0701 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java +++ b/spring-core/src/main/java/org/springframework/util/xml/StaxUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -217,7 +217,7 @@ public abstract class StaxUtils { * @return a content handler writing to the {@code streamWriter} */ public static ContentHandler createContentHandler(XMLStreamWriter streamWriter) { - return new StaxStreamContentHandler(streamWriter); + return new StaxStreamHandler(streamWriter); } /** @@ -226,7 +226,7 @@ public abstract class StaxUtils { * @return a content handler writing to the {@code eventWriter} */ public static ContentHandler createContentHandler(XMLEventWriter eventWriter) { - return new StaxEventContentHandler(eventWriter); + return new StaxEventHandler(eventWriter); } /** diff --git a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxContentHandlerTestCase.java b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxContentHandlerTestCase.java deleted file mode 100644 index 9cab8c0d3f..0000000000 --- a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxContentHandlerTestCase.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 java.io.Writer; -import javax.xml.stream.XMLStreamException; - -import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; -import org.junit.Before; -import org.junit.Test; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.XMLReaderFactory; - -public abstract class AbstractStaxContentHandlerTestCase { - - private static final String XML_CONTENT_HANDLER = - "content" - ; - - private XMLReader xmlReader; - - @Before - public void createXMLReader() throws Exception { - xmlReader = XMLReaderFactory.createXMLReader(); - } - - @Test - public void contentHandler() throws Exception { - StringWriter stringWriter = new StringWriter(); - AbstractStaxContentHandler handler = createStaxContentHandler(stringWriter); - xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false); - xmlReader.setContentHandler(handler); - xmlReader.parse(new InputSource(new StringReader(XML_CONTENT_HANDLER))); - assertXMLEqual("Invalid result", XML_CONTENT_HANDLER, stringWriter.toString()); - } - - @Test - public void contentHandlerNamespacePrefixes() throws Exception { - StringWriter stringWriter = new StringWriter(); - AbstractStaxContentHandler handler = createStaxContentHandler(stringWriter); - xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); - xmlReader.setContentHandler(handler); - xmlReader.parse(new InputSource(new StringReader(XML_CONTENT_HANDLER))); - assertXMLEqual("Invalid result", XML_CONTENT_HANDLER, stringWriter.toString()); - } - - protected abstract AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException; - -} diff --git a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java new file mode 100644 index 0000000000..86a5d0d11e --- /dev/null +++ b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2014 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.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.Result; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.stream.StreamResult; + +import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +public abstract class AbstractStaxHandlerTestCase { + + private static final String COMPLEX_XML = + "" + + "" + + "characters " + + "" + + ""; + + private static final String SIMPLE_XML = "" + + "content" + + ""; + + + private XMLReader xmlReader; + + @Before + public void createXMLReader() throws Exception { + xmlReader = XMLReaderFactory.createXMLReader(); + } + + @Test + public void noNamespacePrefixes() throws Exception { + StringWriter stringWriter = new StringWriter(); + AbstractStaxHandler handler = createStaxHandler(new StreamResult(stringWriter)); + xmlReader.setContentHandler(handler); + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); + + xmlReader.setFeature("http://xml.org/sax/features/namespaces", true); + xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false); + + xmlReader.parse(new InputSource(new StringReader(COMPLEX_XML))); + + assertXMLEqual(COMPLEX_XML, stringWriter.toString()); + } + + @Test + public void namespacePrefixes() throws Exception { + StringWriter stringWriter = new StringWriter(); + AbstractStaxHandler handler = createStaxHandler(new StreamResult(stringWriter)); + xmlReader.setContentHandler(handler); + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); + + xmlReader.setFeature("http://xml.org/sax/features/namespaces", true); + xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); + + xmlReader.parse(new InputSource(new StringReader(COMPLEX_XML))); + + assertXMLEqual(COMPLEX_XML, stringWriter.toString()); + } + + @Test + public void noNamespacePrefixesDom() throws Exception { + DocumentBuilderFactory documentBuilderFactory = + DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + Document expected = documentBuilder.parse(new InputSource(new StringReader(SIMPLE_XML))); + + Document result = documentBuilder.newDocument(); + AbstractStaxHandler handler = createStaxHandler(new DOMResult(result)); + xmlReader.setContentHandler(handler); + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); + + xmlReader.setFeature("http://xml.org/sax/features/namespaces", true); + xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", false); + + xmlReader.parse(new InputSource(new StringReader(SIMPLE_XML))); + + assertXMLEqual(expected, result); + } + + @Test + public void namespacePrefixesDom() throws Exception { + DocumentBuilderFactory documentBuilderFactory = + DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + Document expected = documentBuilder.parse(new InputSource(new StringReader(SIMPLE_XML))); + + Document result = documentBuilder.newDocument(); + AbstractStaxHandler handler = createStaxHandler(new DOMResult(result)); + xmlReader.setContentHandler(handler); + xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler); + + xmlReader.setFeature("http://xml.org/sax/features/namespaces", true); + xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true); + + xmlReader.parse(new InputSource(new StringReader(SIMPLE_XML))); + + assertXMLEqual(expected, result); + } + + + protected abstract AbstractStaxHandler createStaxHandler(Result result) throws XMLStreamException; + +} diff --git a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxXMLReaderTestCase.java b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxXMLReaderTestCase.java index d258a73dca..2072b2e362 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxXMLReaderTestCase.java +++ b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxXMLReaderTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -17,18 +17,14 @@ package org.springframework.util.xml; import java.io.InputStream; - import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import org.junit.Before; import org.junit.Test; +import static org.mockito.BDDMockito.*; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.tests.MockitoUtils; -import org.springframework.tests.MockitoUtils.InvocationArgumentsAdapter; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; @@ -38,7 +34,10 @@ import org.xml.sax.ext.LexicalHandler; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.XMLReaderFactory; -import static org.mockito.BDDMockito.*; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.tests.MockitoUtils; +import org.springframework.tests.MockitoUtils.InvocationArgumentsAdapter; public abstract class AbstractStaxXMLReaderTestCase { @@ -224,6 +223,9 @@ public abstract class AbstractStaxXMLReaderTestCase { @Override public boolean equals(Object obj) { Attributes other = ((PartialAttributes) obj).attributes; + if (this.attributes.getLength() != other.getLength()) { + return false; + } for (int i = 0; i < other.getLength(); i++) { boolean found = false; for (int j = 0; j < attributes.getLength(); j++) { diff --git a/spring-core/src/test/java/org/springframework/util/xml/StaxEventContentHandlerTests.java b/spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java similarity index 57% rename from spring-core/src/test/java/org/springframework/util/xml/StaxEventContentHandlerTests.java rename to spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java index da1657b891..d2c44d66b6 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/StaxEventContentHandlerTests.java +++ b/spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2014 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. @@ -16,15 +16,22 @@ package org.springframework.util.xml; -import java.io.Writer; +import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; +import javax.xml.transform.Result; -public class StaxEventContentHandlerTests extends AbstractStaxContentHandlerTestCase { +/** + * @author Arjen Poutsma + */ +public class StaxEventHandlerTests extends AbstractStaxHandlerTestCase { @Override - protected AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException { - XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - return new StaxEventContentHandler(outputFactory.createXMLEventWriter(writer)); + protected AbstractStaxHandler createStaxHandler(Result result) + throws XMLStreamException { + XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); + XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(result); + return new StaxEventHandler(eventWriter); } -} \ No newline at end of file + +} diff --git a/spring-core/src/test/java/org/springframework/util/xml/StaxStreamContentHandlerTests.java b/spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java similarity index 57% rename from spring-core/src/test/java/org/springframework/util/xml/StaxStreamContentHandlerTests.java rename to spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java index f39f0bff11..a039826bed 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/StaxStreamContentHandlerTests.java +++ b/spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2014 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. @@ -16,15 +16,22 @@ package org.springframework.util.xml; -import java.io.Writer; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.Result; -public class StaxStreamContentHandlerTests extends AbstractStaxContentHandlerTestCase { +/** + * @author Arjen Poutsma + */ +public class StaxStreamHandlerTests extends AbstractStaxHandlerTestCase { @Override - protected AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException { - XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - return new StaxStreamContentHandler(outputFactory.createXMLStreamWriter(writer)); + protected AbstractStaxHandler createStaxHandler(Result result) + throws XMLStreamException { + XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); + XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(result); + return new StaxStreamHandler(streamWriter); } -} \ No newline at end of file + +} diff --git a/spring-core/src/test/resources/org/springframework/util/xml/testContentHandler.xml b/spring-core/src/test/resources/org/springframework/util/xml/testContentHandler.xml index 27129db12f..680faab7ff 100644 --- a/spring-core/src/test/resources/org/springframework/util/xml/testContentHandler.xml +++ b/spring-core/src/test/resources/org/springframework/util/xml/testContentHandler.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/spring-oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java index 4189c0eeb3..ed13d839b8 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -509,12 +509,22 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing @Override protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException { - marshalSaxHandlers(graph, StaxUtils.createContentHandler(eventWriter), null); + ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter); + LexicalHandler lexicalHandler = null; + if (contentHandler instanceof LexicalHandler) { + lexicalHandler = (LexicalHandler) contentHandler; + } + marshalSaxHandlers(graph, contentHandler, lexicalHandler); } @Override protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException { - marshalSaxHandlers(graph, StaxUtils.createContentHandler(streamWriter), null); + ContentHandler contentHandler = StaxUtils.createContentHandler(streamWriter); + LexicalHandler lexicalHandler = null; + if (contentHandler instanceof LexicalHandler) { + lexicalHandler = (LexicalHandler) contentHandler; + } + marshalSaxHandlers(graph, StaxUtils.createContentHandler(streamWriter), lexicalHandler); } @Override diff --git a/spring-oxm/src/main/java/org/springframework/oxm/xmlbeans/XmlBeansMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/xmlbeans/XmlBeansMarshaller.java index b3bb5cf11f..05b9f34113 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/xmlbeans/XmlBeansMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/xmlbeans/XmlBeansMarshaller.java @@ -21,10 +21,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; -import java.util.ArrayList; -import java.util.List; import java.lang.ref.WeakReference; import java.nio.CharBuffer; +import java.util.ArrayList; +import java.util.List; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamReader; @@ -142,13 +142,21 @@ public class XmlBeansMarshaller extends AbstractMarshaller { @Override protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) { ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter); - marshalSaxHandlers(graph, contentHandler, null); + LexicalHandler lexicalHandler = null; + if (contentHandler instanceof LexicalHandler) { + lexicalHandler = (LexicalHandler) contentHandler; + } + marshalSaxHandlers(graph, contentHandler, lexicalHandler); } @Override protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException { ContentHandler contentHandler = StaxUtils.createContentHandler(streamWriter); - marshalSaxHandlers(graph, contentHandler, null); + LexicalHandler lexicalHandler = null; + if (contentHandler instanceof LexicalHandler) { + lexicalHandler = (LexicalHandler) contentHandler; + } + marshalSaxHandlers(graph, contentHandler, lexicalHandler); } @Override diff --git a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java index 4bbe55f3fc..c349c6112d 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/xstream/XStreamMarshaller.java @@ -27,8 +27,11 @@ import java.lang.reflect.Constructor; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - -import javax.xml.stream.*; +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.stream.StreamSource; import com.thoughtworks.xstream.MarshallingStrategy; @@ -59,11 +62,9 @@ import com.thoughtworks.xstream.io.xml.XppDriver; import com.thoughtworks.xstream.mapper.CannotResolveClassException; import com.thoughtworks.xstream.mapper.Mapper; import com.thoughtworks.xstream.mapper.MapperWrapper; - import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; - import org.xml.sax.ContentHandler; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -627,7 +628,11 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin @Override protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException { ContentHandler contentHandler = StaxUtils.createContentHandler(eventWriter); - marshalSaxHandlers(graph, contentHandler, null); + LexicalHandler lexicalHandler = null; + if (contentHandler instanceof LexicalHandler) { + lexicalHandler = (LexicalHandler) contentHandler; + } + marshalSaxHandlers(graph, contentHandler, lexicalHandler); } @Override diff --git a/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorMarshallerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorMarshallerTests.java index 33fd19b36e..620c9a2340 100644 --- a/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorMarshallerTests.java +++ b/spring-oxm/src/test/java/org/springframework/oxm/castor/CastorMarshallerTests.java @@ -75,6 +75,7 @@ public class CastorMarshallerTests extends AbstractMarshallerTests { */ private static final String XSI_EXPECTED_STRING = "" + "" + "test8"; @@ -89,6 +90,7 @@ public class CastorMarshallerTests extends AbstractMarshallerTests { */ private static final String ROOT_WITH_XSI_EXPECTED_STRING = "" + "" + "" + "test8"; @@ -98,6 +100,7 @@ public class CastorMarshallerTests extends AbstractMarshallerTests { */ private static final String ROOT_WITHOUT_XSI_EXPECTED_STRING = "" + "" + "test8";