001: /*
002: * Copyright 2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.oxm;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.OutputStream;
022: import java.io.Reader;
023: import java.io.Writer;
024: import javax.xml.parsers.DocumentBuilder;
025: import javax.xml.parsers.DocumentBuilderFactory;
026: import javax.xml.parsers.ParserConfigurationException;
027: import javax.xml.stream.XMLEventReader;
028: import javax.xml.stream.XMLEventWriter;
029: import javax.xml.stream.XMLStreamReader;
030: import javax.xml.stream.XMLStreamWriter;
031: import javax.xml.transform.Result;
032: import javax.xml.transform.Source;
033: import javax.xml.transform.dom.DOMResult;
034: import javax.xml.transform.dom.DOMSource;
035: import javax.xml.transform.sax.SAXResult;
036: import javax.xml.transform.sax.SAXSource;
037: import javax.xml.transform.stream.StreamResult;
038: import javax.xml.transform.stream.StreamSource;
039:
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.springframework.util.Assert;
043: import org.springframework.xml.transform.StaxResult;
044: import org.springframework.xml.transform.StaxSource;
045: import org.w3c.dom.Node;
046: import org.xml.sax.ContentHandler;
047: import org.xml.sax.InputSource;
048: import org.xml.sax.SAXException;
049: import org.xml.sax.XMLReader;
050: import org.xml.sax.ext.LexicalHandler;
051: import org.xml.sax.helpers.XMLReaderFactory;
052:
053: /**
054: * Abstract implementation of the <code>Marshaller</code> and <code>Unmarshaller</code> interface. This implementation
055: * inspects the given <code>Source</code> or <code>Result</code>, and defers further handling to overridable template
056: * methods.
057: *
058: * @author Arjen Poutsma
059: * @since 1.0.0
060: */
061: public abstract class AbstractMarshaller implements Marshaller,
062: Unmarshaller {
063:
064: /** Logger available to subclasses. */
065: protected final Log logger = LogFactory.getLog(getClass());
066:
067: private DocumentBuilderFactory documentBuilderFactory;
068:
069: /**
070: * Marshals the object graph with the given root into the provided <code>javax.xml.transform.Result</code>.
071: * <p/>
072: * This implementation inspects the given result, and calls <code>marshalDomResult</code>,
073: * <code>marshalSaxResult</code>, or <code>marshalStreamResult</code>.
074: *
075: * @param graph the root of the object graph to marshal
076: * @param result the result to marshal to
077: * @throws XmlMappingException if the given object cannot be marshalled to the result
078: * @throws IOException if an I/O exception occurs
079: * @throws IllegalArgumentException if <code>result</code> if neither a <code>DOMResult</code>,
080: * <code>SAXResult</code>, <code>StreamResult</code>
081: * @see #marshalDomResult(Object,javax.xml.transform.dom.DOMResult)
082: * @see #marshalSaxResult(Object,javax.xml.transform.sax.SAXResult)
083: * @see #marshalStreamResult(Object,javax.xml.transform.stream.StreamResult)
084: */
085: public final void marshal(Object graph, Result result)
086: throws XmlMappingException, IOException {
087: if (result instanceof DOMResult) {
088: marshalDomResult(graph, (DOMResult) result);
089: } else if (result instanceof StaxResult) {
090: marshalStaxResult(graph, (StaxResult) result);
091: } else if (result instanceof SAXResult) {
092: marshalSaxResult(graph, (SAXResult) result);
093: } else if (result instanceof StreamResult) {
094: marshalStreamResult(graph, (StreamResult) result);
095: } else {
096: throw new IllegalArgumentException("Unknown Result type: "
097: + result.getClass());
098: }
099: }
100:
101: /**
102: * Unmarshals the given provided <code>javax.xml.transform.Source</code> into an object graph.
103: * <p/>
104: * This implementation inspects the given result, and calls <code>unmarshalDomSource</code>,
105: * <code>unmarshalSaxSource</code>, or <code>unmarshalStreamSource</code>.
106: *
107: * @param source the source to marshal from
108: * @return the object graph
109: * @throws XmlMappingException if the given source cannot be mapped to an object
110: * @throws IOException if an I/O Exception occurs
111: * @throws IllegalArgumentException if <code>source</code> is neither a <code>DOMSource</code>, a
112: * <code>SAXSource</code>, nor a <code>StreamSource</code>
113: * @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource)
114: * @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)
115: * @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource)
116: */
117: public final Object unmarshal(Source source)
118: throws XmlMappingException, IOException {
119: if (source instanceof DOMSource) {
120: return unmarshalDomSource((DOMSource) source);
121: } else if (source instanceof StaxSource) {
122: return unmarshalStaxSource((StaxSource) source);
123: } else if (source instanceof SAXSource) {
124: return unmarshalSaxSource((SAXSource) source);
125: } else if (source instanceof StreamSource) {
126: return unmarshalStreamSource((StreamSource) source);
127: } else {
128: throw new IllegalArgumentException("Unknown Source type: "
129: + source.getClass());
130: }
131: }
132:
133: /**
134: * Create a <code>DocumentBuilder</code> that this marshaller will use for creating DOM documents when passed an
135: * empty <code>DOMSource</code>. Can be overridden in subclasses, adding further initialization of the builder.
136: *
137: * @param factory the <code>DocumentBuilderFactory</code> that the DocumentBuilder should be created with
138: * @return the <code>DocumentBuilder</code>
139: * @throws javax.xml.parsers.ParserConfigurationException
140: * if thrown by JAXP methods
141: */
142: protected DocumentBuilder createDocumentBuilder(
143: DocumentBuilderFactory factory)
144: throws ParserConfigurationException {
145: return factory.newDocumentBuilder();
146: }
147:
148: /**
149: * Create a <code>DocumentBuilder</code> that this marshaller will use for creating DOM documents when passed an
150: * empty <code>DOMSource</code>. The resulting <code>DocumentBuilderFactory</code> is cached, so this method will
151: * only be called once.
152: *
153: * @return the DocumentBuilderFactory
154: * @throws ParserConfigurationException if thrown by JAXP methods
155: */
156: protected DocumentBuilderFactory createDocumentBuilderFactory()
157: throws ParserConfigurationException {
158: DocumentBuilderFactory factory = DocumentBuilderFactory
159: .newInstance();
160: factory.setValidating(false);
161: factory.setNamespaceAware(true);
162: return factory;
163: }
164:
165: /**
166: * Create a <code>XMLReader</code> that this marshaller will when passed an empty <code>SAXSource</code>.
167: *
168: * @return the XMLReader
169: * @throws SAXException if thrown by JAXP methods
170: */
171: protected XMLReader createXmlReader() throws SAXException {
172: return XMLReaderFactory.createXMLReader();
173: }
174:
175: //
176: // Marshalling
177: //
178:
179: /**
180: * Template method for handling <code>DOMResult</code>s. This implementation defers to <code>marshalDomNode</code>.
181: *
182: * @param graph the root of the object graph to marshal
183: * @param domResult the <code>DOMResult</code>
184: * @throws XmlMappingException if the given object cannot be marshalled to the result
185: * @throws IllegalArgumentException if the <code>domResult</code> is empty
186: * @see #marshalDomNode(Object,org.w3c.dom.Node)
187: */
188: protected void marshalDomResult(Object graph, DOMResult domResult)
189: throws XmlMappingException {
190: Assert.notNull(domResult.getNode(),
191: "DOMResult does not contain Node");
192: marshalDomNode(graph, domResult.getNode());
193: }
194:
195: /**
196: * Template method for handling <code>StaxResult</code>s. This implementation defers to
197: * <code>marshalXMLSteamWriter</code>, or <code>marshalXMLEventConsumer</code>, depending on what is contained in
198: * the <code>StaxResult</code>.
199: *
200: * @param graph the root of the object graph to marshal
201: * @param staxResult the <code>StaxResult</code>
202: * @throws XmlMappingException if the given object cannot be marshalled to the result
203: * @throws IllegalArgumentException if the <code>domResult</code> is empty
204: * @see #marshalDomNode(Object,org.w3c.dom.Node)
205: */
206: protected void marshalStaxResult(Object graph, StaxResult staxResult)
207: throws XmlMappingException {
208: if (staxResult.getXMLStreamWriter() != null) {
209: marshalXmlStreamWriter(graph, staxResult
210: .getXMLStreamWriter());
211: } else if (staxResult.getXMLEventWriter() != null) {
212: marshalXmlEventWriter(graph, staxResult.getXMLEventWriter());
213: } else {
214: throw new IllegalArgumentException(
215: "StaxResult contains neither XMLStreamWriter nor XMLEventConsumer");
216: }
217: }
218:
219: /**
220: * Template method for handling <code>SAXResult</code>s. This implementation defers to
221: * <code>marshalSaxHandlers</code>.
222: *
223: * @param graph the root of the object graph to marshal
224: * @param saxResult the <code>SAXResult</code>
225: * @throws XmlMappingException if the given object cannot be marshalled to the result
226: * @see #marshalSaxHandlers(Object,org.xml.sax.ContentHandler,org.xml.sax.ext.LexicalHandler)
227: */
228: protected void marshalSaxResult(Object graph, SAXResult saxResult)
229: throws XmlMappingException {
230: ContentHandler contentHandler = saxResult.getHandler();
231: Assert.notNull(contentHandler,
232: "ContentHandler not set on SAXResult");
233: LexicalHandler lexicalHandler = saxResult.getLexicalHandler();
234: marshalSaxHandlers(graph, contentHandler, lexicalHandler);
235: }
236:
237: /**
238: * Template method for handling <code>StreamResult</code>s. This implementation defers to
239: * <code>marshalOutputStream</code>, or <code>marshalWriter</code>, depending on what is contained in the
240: * <code>StreamResult</code>
241: *
242: * @param graph the root of the object graph to marshal
243: * @param streamResult the <code>StreamResult</code>
244: * @throws IOException if an I/O Exception occurs
245: * @throws XmlMappingException if the given object cannot be marshalled to the result
246: * @throws IllegalArgumentException if <code>streamResult</code> contains neither <code>OutputStream</code> nor
247: * <code>Writer</code>.
248: */
249: protected void marshalStreamResult(Object graph,
250: StreamResult streamResult) throws XmlMappingException,
251: IOException {
252: if (streamResult.getOutputStream() != null) {
253: marshalOutputStream(graph, streamResult.getOutputStream());
254: } else if (streamResult.getWriter() != null) {
255: marshalWriter(graph, streamResult.getWriter());
256: } else {
257: throw new IllegalArgumentException(
258: "StreamResult contains neither OutputStream nor Writer");
259: }
260: }
261:
262: //
263: // Unmarshalling
264: //
265:
266: /**
267: * Template method for handling <code>DOMSource</code>s. This implementation defers to
268: * <code>unmarshalDomNode</code>. If the given source is empty, an empty source <code>Document</code> will be
269: * created as a placeholder.
270: *
271: * @param domSource the <code>DOMSource</code>
272: * @return the object graph
273: * @throws IllegalArgumentException if the <code>domSource</code> is empty
274: * @throws XmlMappingException if the given source cannot be mapped to an object
275: * @see #unmarshalDomNode(org.w3c.dom.Node)
276: */
277: protected Object unmarshalDomSource(DOMSource domSource)
278: throws XmlMappingException {
279: if (domSource.getNode() == null) {
280: try {
281: if (documentBuilderFactory == null) {
282: documentBuilderFactory = createDocumentBuilderFactory();
283: }
284: DocumentBuilder documentBuilder = createDocumentBuilder(documentBuilderFactory);
285: domSource.setNode(documentBuilder.newDocument());
286: } catch (ParserConfigurationException ex) {
287: throw new UnmarshallingFailureException(
288: "Could not create document placeholder for DOMSource: "
289: + ex.getMessage(), ex);
290: }
291: }
292: return unmarshalDomNode(domSource.getNode());
293: }
294:
295: /**
296: * Template method for handling <code>StaxSource</code>s. This implementation defers to
297: * <code>unmarshalXmlStreamReader</code>, or <code>unmarshalXmlEventReader</code>.
298: *
299: * @param staxSource the <code>StaxSource</code>
300: * @return the object graph
301: * @throws XmlMappingException if the given source cannot be mapped to an object
302: */
303: protected Object unmarshalStaxSource(StaxSource staxSource)
304: throws XmlMappingException {
305: if (staxSource.getXMLStreamReader() != null) {
306: return unmarshalXmlStreamReader(staxSource
307: .getXMLStreamReader());
308: } else if (staxSource.getXMLEventReader() != null) {
309: return unmarshalXmlEventReader(staxSource
310: .getXMLEventReader());
311: } else {
312: throw new IllegalArgumentException(
313: "StaxSource contains neither XMLStreamReader nor XMLEventReader");
314: }
315: }
316:
317: /**
318: * Template method for handling <code>SAXSource</code>s. This implementation defers to
319: * <code>unmarshalSaxReader</code>.
320: *
321: * @param saxSource the <code>SAXSource</code>
322: * @return the object graph
323: * @throws XmlMappingException if the given source cannot be mapped to an object
324: * @throws IOException if an I/O Exception occurs
325: * @see #unmarshalSaxReader(org.xml.sax.XMLReader,org.xml.sax.InputSource)
326: */
327: protected Object unmarshalSaxSource(SAXSource saxSource)
328: throws XmlMappingException, IOException {
329: if (saxSource.getXMLReader() == null) {
330: try {
331: saxSource.setXMLReader(createXmlReader());
332: } catch (SAXException ex) {
333: throw new UnmarshallingFailureException(
334: "Could not create XMLReader for SAXSource: "
335: + ex.getMessage(), ex);
336: }
337: }
338: if (saxSource.getInputSource() == null) {
339: saxSource.setInputSource(new InputSource());
340: }
341: return unmarshalSaxReader(saxSource.getXMLReader(), saxSource
342: .getInputSource());
343: }
344:
345: /**
346: * Template method for handling <code>StreamSource</code>s. This implementation defers to
347: * <code>unmarshalInputStream</code>, or <code>unmarshalReader</code>.
348: *
349: * @param streamSource the <code>StreamSource</code>
350: * @return the object graph
351: * @throws IOException if an I/O exception occurs
352: * @throws XmlMappingException if the given source cannot be mapped to an object
353: */
354: protected Object unmarshalStreamSource(StreamSource streamSource)
355: throws XmlMappingException, IOException {
356: if (streamSource.getInputStream() != null) {
357: return unmarshalInputStream(streamSource.getInputStream());
358: } else if (streamSource.getReader() != null) {
359: return unmarshalReader(streamSource.getReader());
360: } else {
361: throw new IllegalArgumentException(
362: "StreamSource contains neither InputStream nor Reader");
363: }
364: }
365:
366: //
367: // Abstract template methods
368: //
369:
370: /**
371: * Abstract template method for marshalling the given object graph to a DOM <code>Node</code>.
372: * <p/>
373: * In practice, node is be a <code>Document</code> node, a <code>DocumentFragment</code> node, or a
374: * <code>Element</code> node. In other words, a node that accepts children.
375: *
376: * @param graph the root of the object graph to marshal
377: * @param node The DOM node that will contain the result tree
378: * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
379: * @see org.w3c.dom.Document
380: * @see org.w3c.dom.DocumentFragment
381: * @see org.w3c.dom.Element
382: */
383: protected abstract void marshalDomNode(Object graph, Node node)
384: throws XmlMappingException;
385:
386: /**
387: * Abstract template method for marshalling the given object to a StAX <code>XMLEventWriter</code>.
388: *
389: * @param graph the root of the object graph to marshal
390: * @param eventWriter the <code>XMLEventWriter</code> to write to
391: * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
392: */
393: protected abstract void marshalXmlEventWriter(Object graph,
394: XMLEventWriter eventWriter) throws XmlMappingException;
395:
396: /**
397: * Abstract template method for marshalling the given object to a StAX <code>XMLStreamWriter</code>.
398: *
399: * @param graph the root of the object graph to marshal
400: * @param streamWriter the <code>XMLStreamWriter</code> to write to
401: * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
402: */
403: protected abstract void marshalXmlStreamWriter(Object graph,
404: XMLStreamWriter streamWriter) throws XmlMappingException;
405:
406: /**
407: * Abstract template method for marshalling the given object graph to a <code>OutputStream</code>.
408: *
409: * @param graph the root of the object graph to marshal
410: * @param outputStream the <code>OutputStream</code> to write to
411: * @throws XmlMappingException if the given object cannot be marshalled to the writer
412: * @throws IOException if an I/O exception occurs
413: */
414: protected abstract void marshalOutputStream(Object graph,
415: OutputStream outputStream) throws XmlMappingException,
416: IOException;
417:
418: /**
419: * Abstract template method for marshalling the given object graph to a SAX <code>ContentHandler</code>.
420: *
421: * @param graph the root of the object graph to marshal
422: * @param contentHandler the SAX <code>ContentHandler</code>
423: * @param lexicalHandler the SAX2 <code>LexicalHandler</code>. Can be <code>null</code>.
424: * @throws XmlMappingException if the given object cannot be marshalled to the handlers
425: */
426: protected abstract void marshalSaxHandlers(Object graph,
427: ContentHandler contentHandler, LexicalHandler lexicalHandler)
428: throws XmlMappingException;
429:
430: /**
431: * Abstract template method for marshalling the given object graph to a <code>Writer</code>.
432: *
433: * @param graph the root of the object graph to marshal
434: * @param writer the <code>Writer</code> to write to
435: * @throws XmlMappingException if the given object cannot be marshalled to the writer
436: * @throws IOException if an I/O exception occurs
437: */
438: protected abstract void marshalWriter(Object graph, Writer writer)
439: throws XmlMappingException, IOException;
440:
441: /**
442: * Abstract template method for unmarshalling from a given DOM <code>Node</code>.
443: *
444: * @param node The DOM node that contains the objects to be unmarshalled
445: * @return the object graph
446: * @throws XmlMappingException if the given DOM node cannot be mapped to an object
447: */
448: protected abstract Object unmarshalDomNode(Node node)
449: throws XmlMappingException;
450:
451: /**
452: * Abstract template method for unmarshalling from a given Stax <code>XMLEventReader</code>.
453: *
454: * @param eventReader The <code>XMLEventReader</code> to read from
455: * @return the object graph
456: * @throws XmlMappingException if the given event reader cannot be converted to an object
457: */
458: protected abstract Object unmarshalXmlEventReader(
459: XMLEventReader eventReader) throws XmlMappingException;
460:
461: /**
462: * Abstract template method for unmarshalling from a given Stax <code>XMLStreamReader</code>.
463: *
464: * @param streamReader The <code>XMLStreamReader</code> to read from
465: * @return the object graph
466: * @throws XmlMappingException if the given stream reader cannot be converted to an object
467: */
468: protected abstract Object unmarshalXmlStreamReader(
469: XMLStreamReader streamReader) throws XmlMappingException;
470:
471: /**
472: * Abstract template method for unmarshalling from a given <code>InputStream</code>.
473: *
474: * @param inputStream the <code>InputStreamStream</code> to read from
475: * @return the object graph
476: * @throws XmlMappingException if the given stream cannot be converted to an object
477: * @throws IOException if an I/O exception occurs
478: */
479: protected abstract Object unmarshalInputStream(
480: InputStream inputStream) throws XmlMappingException,
481: IOException;
482:
483: /**
484: * Abstract template method for unmarshalling from a given <code>Reader</code>.
485: *
486: * @param reader the <code>Reader</code> to read from
487: * @return the object graph
488: * @throws XmlMappingException if the given reader cannot be converted to an object
489: * @throws IOException if an I/O exception occurs
490: */
491: protected abstract Object unmarshalReader(Reader reader)
492: throws XmlMappingException, IOException;
493:
494: /**
495: * Abstract template method for unmarshalling using a given SAX <code>XMLReader</code> and
496: * <code>InputSource</code>.
497: *
498: * @param xmlReader the SAX <code>XMLReader</code> to parse with
499: * @param inputSource the input source to parse from
500: * @return the object graph
501: * @throws XmlMappingException if the given reader and input source cannot be converted to an object
502: */
503: protected abstract Object unmarshalSaxReader(XMLReader xmlReader,
504: InputSource inputSource) throws XmlMappingException,
505: IOException;
506: }
|