Arjen Poutsma
16 years ago
4 changed files with 372 additions and 0 deletions
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.util.xml; |
||||
|
||||
import javax.xml.namespace.QName; |
||||
import javax.xml.stream.XMLStreamException; |
||||
|
||||
import org.xml.sax.Attributes; |
||||
import org.xml.sax.ContentHandler; |
||||
import org.xml.sax.SAXException; |
||||
|
||||
/** |
||||
* Abstract base class for SAX <code>ContentHandler</code> implementations that use StAX as a basis. All methods |
||||
* delegate to internal template methods, capable of throwing a <code>XMLStreamException</code>. 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(); |
||||
|
||||
public final void startDocument() throws SAXException { |
||||
namespaceContext.clear(); |
||||
try { |
||||
startDocumentInternal(); |
||||
} |
||||
catch (XMLStreamException ex) { |
||||
throw new SAXException("Could not handle startDocument: " + ex.getMessage(), ex); |
||||
} |
||||
} |
||||
|
||||
protected abstract void startDocumentInternal() throws XMLStreamException; |
||||
|
||||
public final void endDocument() throws SAXException { |
||||
namespaceContext.clear(); |
||||
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) |
||||
*/ |
||||
public final void startPrefixMapping(String prefix, String uri) { |
||||
namespaceContext.bindNamespaceUri(prefix, uri); |
||||
} |
||||
|
||||
/** |
||||
* Removes the binding for the given prefix. |
||||
* |
||||
* @see SimpleNamespaceContext#removeBinding(String) |
||||
*/ |
||||
public final void endPrefixMapping(String prefix) { |
||||
namespaceContext.removeBinding(prefix); |
||||
} |
||||
|
||||
public final void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { |
||||
try { |
||||
startElementInternal(toQName(uri, qName), atts, namespaceContext); |
||||
} |
||||
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; |
||||
|
||||
public final void endElement(String uri, String localName, String qName) throws SAXException { |
||||
try { |
||||
endElementInternal(toQName(uri, qName), namespaceContext); |
||||
} |
||||
catch (XMLStreamException ex) { |
||||
throw new SAXException("Could not handle endElement: " + ex.getMessage(), ex); |
||||
} |
||||
} |
||||
|
||||
protected abstract void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) |
||||
throws XMLStreamException; |
||||
|
||||
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; |
||||
|
||||
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; |
||||
|
||||
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; |
||||
|
||||
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</code>. The qualified name can have the form |
||||
* <code>prefix:localname</code> or <code>localName</code>. |
||||
* |
||||
* @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; |
||||
} |
@ -0,0 +1,109 @@
@@ -0,0 +1,109 @@
|
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.util.xml; |
||||
|
||||
import java.util.Iterator; |
||||
import javax.xml.namespace.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.StringUtils; |
||||
|
||||
/** |
||||
* SAX <code>ContentHandler</code> that writes to a <code>XMLStreamWriter</code>. |
||||
* |
||||
* @author Arjen Poutsma |
||||
* @see XMLStreamWriter |
||||
* @since 3.0 |
||||
*/ |
||||
class StaxStreamContentHandler extends AbstractStaxContentHandler { |
||||
|
||||
private final XMLStreamWriter streamWriter; |
||||
|
||||
/** |
||||
* Constructs a new instance of the <code>StaxStreamContentHandler</code> that writes to the given |
||||
* <code>XMLStreamWriter</code>. |
||||
* |
||||
* @param streamWriter the stream writer to write to |
||||
*/ |
||||
StaxStreamContentHandler(XMLStreamWriter streamWriter) { |
||||
this.streamWriter = streamWriter; |
||||
} |
||||
|
||||
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()); |
||||
String defaultNamespaceUri = namespaceContext.getNamespaceURI(""); |
||||
if (StringUtils.hasLength(defaultNamespaceUri)) { |
||||
streamWriter.writeNamespace("", defaultNamespaceUri); |
||||
streamWriter.setDefaultNamespace(defaultNamespaceUri); |
||||
} |
||||
for (Iterator iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext();) { |
||||
String prefix = (String) 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)); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
} |
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.util.xml; |
||||
|
||||
import java.io.Writer; |
||||
import javax.xml.stream.XMLOutputFactory; |
||||
import javax.xml.stream.XMLStreamException; |
||||
|
||||
public class StaxStreamContentHandlerTest extends AbstractStaxContentHandlerTestCase { |
||||
|
||||
@Override |
||||
protected AbstractStaxContentHandler createStaxContentHandler(Writer writer) throws XMLStreamException { |
||||
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); |
||||
return new StaxStreamContentHandler(outputFactory.createXMLStreamWriter(writer)); |
||||
} |
||||
} |
Loading…
Reference in new issue