org.springframework.oxm.AbstractMarshaller Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-oxm Show documentation
Show all versions of spring-oxm Show documentation
Spring Object/XML Mapping abstraction
/*
* Copyright 2006 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.oxm;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.xml.transform.StaxResult;
import org.springframework.xml.transform.StaxSource;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* Abstract implementation of the Marshaller
and Unmarshaller
interface. This implementation
* inspects the given Source
or Result
, and defers further handling to overridable template
* methods.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
/**
* Logger available to subclasses.
*/
protected final Log logger = LogFactory.getLog(getClass());
private DocumentBuilderFactory documentBuilderFactory;
/**
* Marshals the object graph with the given root into the provided javax.xml.transform.Result
.
*
* This implementation inspects the given result, and calls marshalDomResult
,
* marshalSaxResult
, or marshalStreamResult
.
*
* @param graph the root of the object graph to marshal
* @param result the result to marshal to
* @throws XmlMappingException if the given object cannot be marshalled to the result
* @throws IOException if an I/O exception occurs
* @throws IllegalArgumentException if result
if neither a DOMResult
,
* SAXResult
, StreamResult
* @see #marshalDomResult(Object,javax.xml.transform.dom.DOMResult)
* @see #marshalSaxResult(Object,javax.xml.transform.sax.SAXResult)
* @see #marshalStreamResult(Object,javax.xml.transform.stream.StreamResult)
*/
public final void marshal(Object graph, Result result) throws XmlMappingException, IOException {
if (result instanceof DOMResult) {
marshalDomResult(graph, (DOMResult) result);
}
else if (result instanceof StaxResult) {
marshalStaxResult(graph, (StaxResult) result);
}
else if (result instanceof SAXResult) {
marshalSaxResult(graph, (SAXResult) result);
}
else if (result instanceof StreamResult) {
marshalStreamResult(graph, (StreamResult) result);
}
else {
throw new IllegalArgumentException("Unknown Result type: " + result.getClass());
}
}
/**
* Unmarshals the given provided javax.xml.transform.Source
into an object graph.
*
* This implementation inspects the given result, and calls unmarshalDomSource
,
* unmarshalSaxSource
, or unmarshalStreamSource
.
*
* @param source the source to marshal from
* @return the object graph
* @throws XmlMappingException if the given source cannot be mapped to an object
* @throws IOException if an I/O Exception occurs
* @throws IllegalArgumentException if source
is neither a DOMSource
, a
* SAXSource
, nor a StreamSource
* @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource)
* @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)
* @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource)
*/
public final Object unmarshal(Source source) throws XmlMappingException, IOException {
if (source instanceof DOMSource) {
return unmarshalDomSource((DOMSource) source);
}
else if (source instanceof StaxSource) {
return unmarshalStaxSource((StaxSource) source);
}
else if (source instanceof SAXSource) {
return unmarshalSaxSource((SAXSource) source);
}
else if (source instanceof StreamSource) {
return unmarshalStreamSource((StreamSource) source);
}
else {
throw new IllegalArgumentException("Unknown Source type: " + source.getClass());
}
}
/**
* Create a DocumentBuilder
that this marshaller will use for creating DOM documents when passed an
* empty DOMSource
. Can be overridden in subclasses, adding further initialization of the builder.
*
* @param factory the DocumentBuilderFactory
that the DocumentBuilder should be created with
* @return the DocumentBuilder
* @throws javax.xml.parsers.ParserConfigurationException
* if thrown by JAXP methods
*/
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
throws ParserConfigurationException {
return factory.newDocumentBuilder();
}
/**
* Create a DocumentBuilder
that this marshaller will use for creating DOM documents when passed an
* empty DOMSource
. The resulting DocumentBuilderFactory
is cached, so this method will
* only be called once.
*
* @return the DocumentBuilderFactory
* @throws ParserConfigurationException if thrown by JAXP methods
*/
protected DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
return factory;
}
/**
* Create a XMLReader
that this marshaller will when passed an empty SAXSource
.
*
* @return the XMLReader
* @throws SAXException if thrown by JAXP methods
*/
protected XMLReader createXmlReader() throws SAXException {
return XMLReaderFactory.createXMLReader();
}
//
// Marshalling
//
/**
* Template method for handling DOMResult
s. This implementation defers to marshalDomNode
.
*
* @param graph the root of the object graph to marshal
* @param domResult the DOMResult
* @throws XmlMappingException if the given object cannot be marshalled to the result
* @throws IllegalArgumentException if the domResult
is empty
* @see #marshalDomNode(Object,org.w3c.dom.Node)
*/
protected void marshalDomResult(Object graph, DOMResult domResult) throws XmlMappingException {
Assert.notNull(domResult.getNode(), "DOMResult does not contain Node");
marshalDomNode(graph, domResult.getNode());
}
/**
* Template method for handling StaxResult
s. This implementation defers to
* marshalXMLSteamWriter
, or marshalXMLEventConsumer
, depending on what is contained in
* the StaxResult
.
*
* @param graph the root of the object graph to marshal
* @param staxResult the StaxResult
* @throws XmlMappingException if the given object cannot be marshalled to the result
* @throws IllegalArgumentException if the domResult
is empty
* @see #marshalDomNode(Object,org.w3c.dom.Node)
*/
protected void marshalStaxResult(Object graph, StaxResult staxResult) throws XmlMappingException {
if (staxResult.getXMLStreamWriter() != null) {
marshalXmlStreamWriter(graph, staxResult.getXMLStreamWriter());
}
else if (staxResult.getXMLEventWriter() != null) {
marshalXmlEventWriter(graph, staxResult.getXMLEventWriter());
}
else {
throw new IllegalArgumentException("StaxResult contains neither XMLStreamWriter nor XMLEventConsumer");
}
}
/**
* Template method for handling SAXResult
s. This implementation defers to
* marshalSaxHandlers
.
*
* @param graph the root of the object graph to marshal
* @param saxResult the SAXResult
* @throws XmlMappingException if the given object cannot be marshalled to the result
* @see #marshalSaxHandlers(Object,org.xml.sax.ContentHandler,org.xml.sax.ext.LexicalHandler)
*/
protected void marshalSaxResult(Object graph, SAXResult saxResult) throws XmlMappingException {
ContentHandler contentHandler = saxResult.getHandler();
Assert.notNull(contentHandler, "ContentHandler not set on SAXResult");
LexicalHandler lexicalHandler = saxResult.getLexicalHandler();
marshalSaxHandlers(graph, contentHandler, lexicalHandler);
}
/**
* Template method for handling StreamResult
s. This implementation defers to
* marshalOutputStream
, or marshalWriter
, depending on what is contained in the
* StreamResult
*
* @param graph the root of the object graph to marshal
* @param streamResult the StreamResult
* @throws IOException if an I/O Exception occurs
* @throws XmlMappingException if the given object cannot be marshalled to the result
* @throws IllegalArgumentException if streamResult
contains neither OutputStream
nor
* Writer
.
*/
protected void marshalStreamResult(Object graph, StreamResult streamResult)
throws XmlMappingException, IOException {
if (streamResult.getOutputStream() != null) {
marshalOutputStream(graph, streamResult.getOutputStream());
}
else if (streamResult.getWriter() != null) {
marshalWriter(graph, streamResult.getWriter());
}
else {
throw new IllegalArgumentException("StreamResult contains neither OutputStream nor Writer");
}
}
//
// Unmarshalling
//
/**
* Template method for handling DOMSource
s. This implementation defers to
* unmarshalDomNode
. If the given source is empty, an empty source Document
will be
* created as a placeholder.
*
* @param domSource the DOMSource
* @return the object graph
* @throws IllegalArgumentException if the domSource
is empty
* @throws XmlMappingException if the given source cannot be mapped to an object
* @see #unmarshalDomNode(org.w3c.dom.Node)
*/
protected Object unmarshalDomSource(DOMSource domSource) throws XmlMappingException {
if (domSource.getNode() == null) {
try {
if (documentBuilderFactory == null) {
documentBuilderFactory = createDocumentBuilderFactory();
}
DocumentBuilder documentBuilder = createDocumentBuilder(documentBuilderFactory);
domSource.setNode(documentBuilder.newDocument());
}
catch (ParserConfigurationException ex) {
throw new UnmarshallingFailureException(
"Could not create document placeholder for DOMSource: " + ex.getMessage(), ex);
}
}
return unmarshalDomNode(domSource.getNode());
}
/**
* Template method for handling StaxSource
s. This implementation defers to
* unmarshalXmlStreamReader
, or unmarshalXmlEventReader
.
*
* @param staxSource the StaxSource
* @return the object graph
* @throws XmlMappingException if the given source cannot be mapped to an object
*/
protected Object unmarshalStaxSource(StaxSource staxSource) throws XmlMappingException {
if (staxSource.getXMLStreamReader() != null) {
return unmarshalXmlStreamReader(staxSource.getXMLStreamReader());
}
else if (staxSource.getXMLEventReader() != null) {
return unmarshalXmlEventReader(staxSource.getXMLEventReader());
}
else {
throw new IllegalArgumentException("StaxSource contains neither XMLStreamReader nor XMLEventReader");
}
}
/**
* Template method for handling SAXSource
s. This implementation defers to
* unmarshalSaxReader
.
*
* @param saxSource the SAXSource
* @return the object graph
* @throws XmlMappingException if the given source cannot be mapped to an object
* @throws IOException if an I/O Exception occurs
* @see #unmarshalSaxReader(org.xml.sax.XMLReader,org.xml.sax.InputSource)
*/
protected Object unmarshalSaxSource(SAXSource saxSource) throws XmlMappingException, IOException {
if (saxSource.getXMLReader() == null) {
try {
saxSource.setXMLReader(createXmlReader());
}
catch (SAXException ex) {
throw new UnmarshallingFailureException("Could not create XMLReader for SAXSource: " + ex.getMessage(),
ex);
}
}
if (saxSource.getInputSource() == null) {
saxSource.setInputSource(new InputSource());
}
return unmarshalSaxReader(saxSource.getXMLReader(), saxSource.getInputSource());
}
/**
* Template method for handling StreamSource
s. This implementation defers to
* unmarshalInputStream
, or unmarshalReader
.
*
* @param streamSource the StreamSource
* @return the object graph
* @throws IOException if an I/O exception occurs
* @throws XmlMappingException if the given source cannot be mapped to an object
*/
protected Object unmarshalStreamSource(StreamSource streamSource) throws XmlMappingException, IOException {
if (streamSource.getInputStream() != null) {
return unmarshalInputStream(streamSource.getInputStream());
}
else if (streamSource.getReader() != null) {
return unmarshalReader(streamSource.getReader());
}
else {
throw new IllegalArgumentException("StreamSource contains neither InputStream nor Reader");
}
}
//
// Abstract template methods
//
/**
* Abstract template method for marshalling the given object graph to a DOM Node
.
*
* In practice, node is be a Document
node, a DocumentFragment
node, or a
* Element
node. In other words, a node that accepts children.
*
* @param graph the root of the object graph to marshal
* @param node The DOM node that will contain the result tree
* @throws XmlMappingException if the given object cannot be marshalled to the DOM node
* @see org.w3c.dom.Document
* @see org.w3c.dom.DocumentFragment
* @see org.w3c.dom.Element
*/
protected abstract void marshalDomNode(Object graph, Node node) throws XmlMappingException;
/**
* Abstract template method for marshalling the given object to a StAX XMLEventWriter
.
*
* @param graph the root of the object graph to marshal
* @param eventWriter the XMLEventWriter
to write to
* @throws XmlMappingException if the given object cannot be marshalled to the DOM node
*/
protected abstract void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter) throws XmlMappingException;
/**
* Abstract template method for marshalling the given object to a StAX XMLStreamWriter
.
*
* @param graph the root of the object graph to marshal
* @param streamWriter the XMLStreamWriter
to write to
* @throws XmlMappingException if the given object cannot be marshalled to the DOM node
*/
protected abstract void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter)
throws XmlMappingException;
/**
* Abstract template method for marshalling the given object graph to a OutputStream
.
*
* @param graph the root of the object graph to marshal
* @param outputStream the OutputStream
to write to
* @throws XmlMappingException if the given object cannot be marshalled to the writer
* @throws IOException if an I/O exception occurs
*/
protected abstract void marshalOutputStream(Object graph, OutputStream outputStream)
throws XmlMappingException, IOException;
/**
* Abstract template method for marshalling the given object graph to a SAX ContentHandler
.
*
* @param graph the root of the object graph to marshal
* @param contentHandler the SAX ContentHandler
* @param lexicalHandler the SAX2 LexicalHandler
. Can be null
.
* @throws XmlMappingException if the given object cannot be marshalled to the handlers
*/
protected abstract void marshalSaxHandlers(Object graph,
ContentHandler contentHandler,
LexicalHandler lexicalHandler) throws XmlMappingException;
/**
* Abstract template method for marshalling the given object graph to a Writer
.
*
* @param graph the root of the object graph to marshal
* @param writer the Writer
to write to
* @throws XmlMappingException if the given object cannot be marshalled to the writer
* @throws IOException if an I/O exception occurs
*/
protected abstract void marshalWriter(Object graph, Writer writer) throws XmlMappingException, IOException;
/**
* Abstract template method for unmarshalling from a given DOM Node
.
*
* @param node The DOM node that contains the objects to be unmarshalled
* @return the object graph
* @throws XmlMappingException if the given DOM node cannot be mapped to an object
*/
protected abstract Object unmarshalDomNode(Node node) throws XmlMappingException;
/**
* Abstract template method for unmarshalling from a given Stax XMLEventReader
.
*
* @param eventReader The XMLEventReader
to read from
* @return the object graph
* @throws XmlMappingException if the given event reader cannot be converted to an object
*/
protected abstract Object unmarshalXmlEventReader(XMLEventReader eventReader) throws XmlMappingException;
/**
* Abstract template method for unmarshalling from a given Stax XMLStreamReader
.
*
* @param streamReader The XMLStreamReader
to read from
* @return the object graph
* @throws XmlMappingException if the given stream reader cannot be converted to an object
*/
protected abstract Object unmarshalXmlStreamReader(XMLStreamReader streamReader) throws XmlMappingException;
/**
* Abstract template method for unmarshalling from a given InputStream
.
*
* @param inputStream the InputStreamStream
to read from
* @return the object graph
* @throws XmlMappingException if the given stream cannot be converted to an object
* @throws IOException if an I/O exception occurs
*/
protected abstract Object unmarshalInputStream(InputStream inputStream) throws XmlMappingException, IOException;
/**
* Abstract template method for unmarshalling from a given Reader
.
*
* @param reader the Reader
to read from
* @return the object graph
* @throws XmlMappingException if the given reader cannot be converted to an object
* @throws IOException if an I/O exception occurs
*/
protected abstract Object unmarshalReader(Reader reader) throws XmlMappingException, IOException;
/**
* Abstract template method for unmarshalling using a given SAX XMLReader
and
* InputSource
.
*
* @param xmlReader the SAX XMLReader
to parse with
* @param inputSource the input source to parse from
* @return the object graph
* @throws XmlMappingException if the given reader and input source cannot be converted to an object
* @throws java.io.IOException if an I/O exception occurs
*/
protected abstract Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
throws XmlMappingException, IOException;
}