Improved StAX<->SAX bridge

Improved the SAX to StAX (and vice-versa) bridge exposed via StaxUtils.
The old integration had some issues with namespace declaration
attributes, brought to light in a XMLUnit upgrade.

Issue: SPR-11549
This commit is contained in:
Arjen Poutsma 2014-03-13 15:55:20 +01:00 committed by unknown
parent a2f1169e82
commit f2f355e76c
19 changed files with 850 additions and 625 deletions

View File

@ -242,7 +242,7 @@ project("spring-core") {
optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("net.sf.jopt-simple:jopt-simple:4.6") optional("net.sf.jopt-simple:jopt-simple:4.6")
optional("log4j:log4j:1.2.17") 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") { testCompile("org.codehaus.woodstox:wstx-asl:3.2.7") {
exclude group: "stax", module: "stax-api" exclude group: "stax", module: "stax-api"
} }
@ -460,7 +460,7 @@ project("spring-oxm") {
optional("org.codehaus.castor:castor-xml:1.3.2") optional("org.codehaus.castor:castor-xml:1.3.2")
testCompile(project(":spring-context")) testCompile(project(":spring-context"))
testCompile("org.codehaus.jettison:jettison:1.0.1") 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("xmlpull:xmlpull:1.1.3.4a")
testCompile(files(genCastor.classesDir).builtBy(genCastor)) testCompile(files(genCastor.classesDir).builtBy(genCastor))
testCompile(files(genJaxb.classesDir).builtBy(genJaxb)) testCompile(files(genJaxb.classesDir).builtBy(genJaxb))
@ -567,7 +567,7 @@ project("spring-web") {
} }
optional("log4j:log4j:1.2.17") optional("log4j:log4j:1.2.17")
testCompile(project(":spring-context-support")) // for JafMediaTypeFactory testCompile(project(":spring-context-support")) // for JafMediaTypeFactory
testCompile("xmlunit:xmlunit:1.3") testCompile("xmlunit:xmlunit:1.5")
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
} }
@ -703,7 +703,7 @@ project("spring-webmvc") {
} }
testCompile(project(":spring-aop")) testCompile(project(":spring-aop"))
testCompile("rhino:js:1.7R1") testCompile("rhino:js:1.7R1")
testCompile("xmlunit:xmlunit:1.3") testCompile("xmlunit:xmlunit:1.5")
testCompile("dom4j:dom4j:1.6.1") { testCompile("dom4j:dom4j:1.6.1") {
exclude group: "xml-apis", module: "xml-apis" exclude group: "xml-apis", module: "xml-apis"
} }
@ -806,7 +806,7 @@ project("spring-test") {
optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("org.hamcrest:hamcrest-core:1.3") optional("org.hamcrest:hamcrest-core:1.3")
optional("com.jayway.jsonpath:json-path:0.9.0") 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-context-support"))
testCompile(project(":spring-oxm")) testCompile(project(":spring-oxm"))
testCompile(project(":spring-webmvc-tiles3")) testCompile(project(":spring-webmvc-tiles3"))

View File

@ -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;
}

View File

@ -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<Map<String, String>> namespaceMappings = new ArrayList<Map<String, String>>();
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("<!DOCTYPE ");
builder.append(name);
if (publicId != null) {
builder.append(" PUBLIC \"");
builder.append(publicId);
builder.append("\" \"");
}
else {
builder.append(" SYSTEM \"");
}
builder.append(systemId);
builder.append("\">");
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<String, String> currentNamespaceMapping() {
return this.namespaceMappings.get(this.namespaceMappings.size() - 1);
}
private void newNamespaceMapping() {
this.namespaceMappings.add(new HashMap<String, String>());
}
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<String, String> namespaceMapping) throws XMLStreamException;
protected abstract void endElementInternal(QName name, Map<String, String> 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;
}

View File

@ -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<Attribute> attributes = getAttributes(atts);
List<Namespace> 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<Namespace> 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<Namespace> createNamespaces(SimpleNamespaceContext namespaceContext) {
if (namespaceContext == null) {
return null;
}
List<Namespace> namespaces = new ArrayList<Namespace>();
String defaultNamespaceUri = namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX);
if (StringUtils.hasLength(defaultNamespaceUri)) {
namespaces.add(this.eventFactory.createNamespace(defaultNamespaceUri));
}
for (Iterator<String> 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<Attribute> getAttributes(Attributes attributes) {
List<Attribute> list = new ArrayList<Attribute>();
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();
}
}
}

View File

@ -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<String, String> namespaceMapping) throws XMLStreamException {
List<Attribute> attributes = getAttributes(atts);
List<Namespace> namespaces = getNamespaces(namespaceMapping);
this.eventWriter.add(
this.eventFactory.createStartElement(name, attributes.iterator(), namespaces.iterator()));
}
private List<Namespace> getNamespaces(Map<String, String> namespaceMapping) {
List<Namespace> result = new ArrayList<Namespace>();
for (Map.Entry<String, String> entry : namespaceMapping.entrySet()) {
String prefix = entry.getKey();
String namespaceUri = entry.getValue();
result.add(this.eventFactory.createNamespace(prefix, namespaceUri));
}
return result;
}
private List<Attribute> getAttributes(Attributes attributes) {
List<Attribute> result = new ArrayList<Attribute>();
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<String, String> namespaceMapping) throws XMLStreamException {
List<Namespace> 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();
}
}
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 javax.xml.transform.sax.SAXResult;
import org.xml.sax.ContentHandler; 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 * 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 <strong>not supported</strong>. In general, the only supported operation * {@code SAXResult} is <strong>not supported</strong>. In general, the only supported operation
* on this class is to use the {@code ContentHandler} obtained via {@link #getHandler()} to parse an * 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)} * 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 * @author Arjen Poutsma
* @since 3.0 * @since 3.0
@ -54,8 +56,10 @@ class StaxResult extends SAXResult {
* Construct a new instance of the {@code StaxResult} with the specified {@code XMLStreamWriter}. * Construct a new instance of the {@code StaxResult} with the specified {@code XMLStreamWriter}.
* @param streamWriter the {@code XMLStreamWriter} to write to * @param streamWriter the {@code XMLStreamWriter} to write to
*/ */
StaxResult(XMLStreamWriter streamWriter) { public StaxResult(XMLStreamWriter streamWriter) {
super.setHandler(new StaxStreamContentHandler(streamWriter)); StaxStreamHandler handler = new StaxStreamHandler(streamWriter);
super.setHandler(handler);
super.setLexicalHandler(handler);
this.streamWriter = streamWriter; this.streamWriter = streamWriter;
} }
@ -63,19 +67,10 @@ class StaxResult extends SAXResult {
* Construct a new instance of the {@code StaxResult} with the specified {@code XMLEventWriter}. * Construct a new instance of the {@code StaxResult} with the specified {@code XMLEventWriter}.
* @param eventWriter the {@code XMLEventWriter} to write to * @param eventWriter the {@code XMLEventWriter} to write to
*/ */
StaxResult(XMLEventWriter eventWriter) { public StaxResult(XMLEventWriter eventWriter) {
super.setHandler(new StaxEventContentHandler(eventWriter)); StaxEventHandler handler = new StaxEventHandler(eventWriter);
this.eventWriter = eventWriter; super.setHandler(handler);
} super.setLexicalHandler(handler);
/**
* 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));
this.eventWriter = eventWriter; this.eventWriter = eventWriter;
} }
@ -86,7 +81,7 @@ class StaxResult extends SAXResult {
* @return the StAX event writer used by this result * @return the StAX event writer used by this result
* @see #StaxResult(javax.xml.stream.XMLEventWriter) * @see #StaxResult(javax.xml.stream.XMLEventWriter)
*/ */
XMLEventWriter getXMLEventWriter() { public XMLEventWriter getXMLEventWriter() {
return this.eventWriter; return this.eventWriter;
} }
@ -96,7 +91,7 @@ class StaxResult extends SAXResult {
* @return the StAX stream writer used by this result * @return the StAX stream writer used by this result
* @see #StaxResult(javax.xml.stream.XMLStreamWriter) * @see #StaxResult(javax.xml.stream.XMLStreamWriter)
*/ */
XMLStreamWriter getXMLStreamWriter() { public XMLStreamWriter getXMLStreamWriter() {
return this.streamWriter; return this.streamWriter;
} }
@ -110,4 +105,13 @@ class StaxResult extends SAXResult {
throw new UnsupportedOperationException("setHandler is not supported"); 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");
}
} }

View File

@ -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<String> 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));
}
}
}
}

View File

@ -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<String, String> namespaceMapping) throws XMLStreamException {
this.streamWriter.writeStartElement(name.getPrefix(), name.getLocalPart(), name.getNamespaceURI());
for (Map.Entry<String, String> 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<String, String> 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 {
}
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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} * @return a content handler writing to the {@code streamWriter}
*/ */
public static ContentHandler createContentHandler(XMLStreamWriter 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} * @return a content handler writing to the {@code eventWriter}
*/ */
public static ContentHandler createContentHandler(XMLEventWriter eventWriter) { public static ContentHandler createContentHandler(XMLEventWriter eventWriter) {
return new StaxEventContentHandler(eventWriter); return new StaxEventHandler(eventWriter);
} }
/** /**

View File

@ -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 =
"<?xml version='1.0' encoding='UTF-8'?><?pi content?><root xmlns='namespace'><prefix:child xmlns:prefix='namespace2' prefix:attr='value'>content</prefix:child></root>"
;
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;
}

View File

@ -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 =
"<?xml version='1.0' encoding='UTF-8'?>" +
"<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN 2.0//EN\" \"http://www.springframework.org/dtd/spring-beans-2.0.dtd\">" +
"<?pi content?><root xmlns='namespace'><prefix:child xmlns:prefix='namespace2' prefix:attr='value'>characters <![CDATA[cdata]]></prefix:child>" +
"<!-- comment -->" +
"</root>";
private static final String SIMPLE_XML = "<?xml version='1.0' encoding='UTF-8'?>" +
"<?pi content?><root xmlns='namespace'><prefix:child xmlns:prefix='namespace2' prefix:attr='value'>content</prefix:child>" +
"</root>";
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;
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,18 +17,14 @@
package org.springframework.util.xml; package org.springframework.util.xml;
import java.io.InputStream; import java.io.InputStream;
import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.mockito.BDDMockito.*;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; 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.Attributes;
import org.xml.sax.ContentHandler; import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource; 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.AttributesImpl;
import org.xml.sax.helpers.XMLReaderFactory; 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 { public abstract class AbstractStaxXMLReaderTestCase {
@ -224,6 +223,9 @@ public abstract class AbstractStaxXMLReaderTestCase {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
Attributes other = ((PartialAttributes) obj).attributes; Attributes other = ((PartialAttributes) obj).attributes;
if (this.attributes.getLength() != other.getLength()) {
return false;
}
for (int i = 0; i < other.getLength(); i++) { for (int i = 0; i < other.getLength(); i++) {
boolean found = false; boolean found = false;
for (int j = 0; j < attributes.getLength(); j++) { for (int j = 0; j < attributes.getLength(); j++) {

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,15 +16,22 @@
package org.springframework.util.xml; package org.springframework.util.xml;
import java.io.Writer; import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Result;
public class StaxEventContentHandlerTests extends AbstractStaxContentHandlerTestCase { /**
* @author Arjen Poutsma
*/
public class StaxEventHandlerTests extends AbstractStaxHandlerTestCase {
@Override @Override
protected AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException { protected AbstractStaxHandler createStaxHandler(Result result)
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); throws XMLStreamException {
return new StaxEventContentHandler(outputFactory.createXMLEventWriter(writer)); XMLOutputFactory outputFactory = XMLOutputFactory.newFactory();
XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(result);
return new StaxEventHandler(eventWriter);
} }
}
}

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,15 +16,22 @@
package org.springframework.util.xml; package org.springframework.util.xml;
import java.io.Writer;
import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException; 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 @Override
protected AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException { protected AbstractStaxHandler createStaxHandler(Result result)
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); throws XMLStreamException {
return new StaxStreamContentHandler(outputFactory.createXMLStreamWriter(writer)); XMLOutputFactory outputFactory = XMLOutputFactory.newFactory();
XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(result);
return new StaxStreamHandler(streamWriter);
} }
}
}

View File

@ -1,2 +1,2 @@
<h:hello xmlns:h="http://www.greeting.com/hello/" id="a1" h:person="David"><goodbye <h:hello xmlns:h="http://www.greeting.com/hello/" id="a1" h:person="David"><prefix:goodbye
xmlns="http://www.greeting.com/goodbye/" h:person="Arjen"/></h:hello> xmlns:prefix="http://www.greeting.com/goodbye/" h:person="Arjen"/></h:hello>

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 @Override
protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException { 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 @Override
protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException { 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 @Override

View File

@ -21,10 +21,10 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamReader;
@ -142,13 +142,21 @@ public class XmlBeansMarshaller extends AbstractMarshaller {
@Override @Override
protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) { protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) {
ContentHandler contentHandler = StaxUtils.createContentHandler(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 @Override
protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException { protected void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter) throws XmlMappingException {
ContentHandler contentHandler = StaxUtils.createContentHandler(streamWriter); ContentHandler contentHandler = StaxUtils.createContentHandler(streamWriter);
marshalSaxHandlers(graph, contentHandler, null); LexicalHandler lexicalHandler = null;
if (contentHandler instanceof LexicalHandler) {
lexicalHandler = (LexicalHandler) contentHandler;
}
marshalSaxHandlers(graph, contentHandler, lexicalHandler);
} }
@Override @Override

View File

@ -27,8 +27,11 @@ import java.lang.reflect.Constructor;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.*; 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 javax.xml.transform.stream.StreamSource;
import com.thoughtworks.xstream.MarshallingStrategy; 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.CannotResolveClassException;
import com.thoughtworks.xstream.mapper.Mapper; import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper; import com.thoughtworks.xstream.mapper.MapperWrapper;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.xml.sax.ContentHandler; import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.XMLReader; import org.xml.sax.XMLReader;
@ -627,7 +628,11 @@ public class XStreamMarshaller extends AbstractMarshaller implements Initializin
@Override @Override
protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException { protected void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException {
ContentHandler contentHandler = StaxUtils.createContentHandler(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 @Override

View File

@ -75,6 +75,7 @@ public class CastorMarshallerTests extends AbstractMarshallerTests {
*/ */
private static final String XSI_EXPECTED_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" private static final String XSI_EXPECTED_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<objects><castor-object xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + "<objects><castor-object xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xmlns:java=\"http://java.sun.com\""
+ " xsi:type=\"java:org.springframework.oxm.castor.CastorObject\">" + " xsi:type=\"java:org.springframework.oxm.castor.CastorObject\">"
+ "<name>test</name><value>8</value></castor-object></objects>"; + "<name>test</name><value>8</value></castor-object></objects>";
@ -89,6 +90,7 @@ public class CastorMarshallerTests extends AbstractMarshallerTests {
*/ */
private static final String ROOT_WITH_XSI_EXPECTED_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" private static final String ROOT_WITH_XSI_EXPECTED_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<objects xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + "<objects xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xmlns:java=\"http://java.sun.com\""
+ " xsi:type=\"java:java.util.Arrays$ArrayList\">" + " xsi:type=\"java:java.util.Arrays$ArrayList\">"
+ "<castor-object xsi:type=\"java:org.springframework.oxm.castor.CastorObject\">" + "<castor-object xsi:type=\"java:org.springframework.oxm.castor.CastorObject\">"
+ "<name>test</name><value>8</value></castor-object></objects>"; + "<name>test</name><value>8</value></castor-object></objects>";
@ -98,6 +100,7 @@ public class CastorMarshallerTests extends AbstractMarshallerTests {
*/ */
private static final String ROOT_WITHOUT_XSI_EXPECTED_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" private static final String ROOT_WITHOUT_XSI_EXPECTED_STRING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<objects><castor-object xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + "<objects><castor-object xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xmlns:java=\"http://java.sun.com\""
+ " xsi:type=\"java:org.springframework.oxm.castor.CastorObject\">" + " xsi:type=\"java:org.springframework.oxm.castor.CastorObject\">"
+ "<name>test</name><value>8</value></castor-object></objects>"; + "<name>test</name><value>8</value></castor-object></objects>";