001/*
002 * Copyright 2002-2019 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 *      https://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
017package org.springframework.oxm.support;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.OutputStream;
022import java.io.Reader;
023import java.io.StringReader;
024import java.io.Writer;
025
026import javax.xml.parsers.DocumentBuilder;
027import javax.xml.parsers.DocumentBuilderFactory;
028import javax.xml.parsers.ParserConfigurationException;
029import javax.xml.stream.XMLEventReader;
030import javax.xml.stream.XMLEventWriter;
031import javax.xml.stream.XMLStreamReader;
032import javax.xml.stream.XMLStreamWriter;
033import javax.xml.transform.Result;
034import javax.xml.transform.Source;
035import javax.xml.transform.dom.DOMResult;
036import javax.xml.transform.dom.DOMSource;
037import javax.xml.transform.sax.SAXResult;
038import javax.xml.transform.sax.SAXSource;
039import javax.xml.transform.stax.StAXSource;
040import javax.xml.transform.stream.StreamResult;
041import javax.xml.transform.stream.StreamSource;
042
043import org.apache.commons.logging.Log;
044import org.apache.commons.logging.LogFactory;
045import org.w3c.dom.Document;
046import org.w3c.dom.Node;
047import org.xml.sax.ContentHandler;
048import org.xml.sax.EntityResolver;
049import org.xml.sax.InputSource;
050import org.xml.sax.SAXException;
051import org.xml.sax.XMLReader;
052import org.xml.sax.ext.LexicalHandler;
053
054import org.springframework.lang.Nullable;
055import org.springframework.oxm.Marshaller;
056import org.springframework.oxm.Unmarshaller;
057import org.springframework.oxm.UnmarshallingFailureException;
058import org.springframework.oxm.XmlMappingException;
059import org.springframework.util.Assert;
060import org.springframework.util.xml.StaxUtils;
061
062/**
063 * Abstract implementation of the {@code Marshaller} and {@code Unmarshaller} interface.
064 * This implementation inspects the given {@code Source} or {@code Result}, and
065 * delegates further handling to overridable template methods.
066 *
067 * @author Arjen Poutsma
068 * @author Juergen Hoeller
069 * @since 3.0
070 */
071public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
072
073        private static final EntityResolver NO_OP_ENTITY_RESOLVER =
074                        (publicId, systemId) -> new InputSource(new StringReader(""));
075
076        /** Logger available to subclasses. */
077        protected final Log logger = LogFactory.getLog(getClass());
078
079        private boolean supportDtd = false;
080
081        private boolean processExternalEntities = false;
082
083        @Nullable
084        private DocumentBuilderFactory documentBuilderFactory;
085
086        private final Object documentBuilderFactoryMonitor = new Object();
087
088
089        /**
090         * Indicate whether DTD parsing should be supported.
091         * <p>Default is {@code false} meaning that DTD is disabled.
092         */
093        public void setSupportDtd(boolean supportDtd) {
094                this.supportDtd = supportDtd;
095        }
096
097        /**
098         * Return whether DTD parsing is supported.
099         */
100        public boolean isSupportDtd() {
101                return this.supportDtd;
102        }
103
104        /**
105         * Indicate whether external XML entities are processed when unmarshalling.
106         * <p>Default is {@code false}, meaning that external entities are not resolved.
107         * Note that processing of external entities will only be enabled/disabled when the
108         * {@code Source} passed to {@link #unmarshal(Source)} is a {@link SAXSource} or
109         * {@link StreamSource}. It has no effect for {@link DOMSource} or {@link StAXSource}
110         * instances.
111         * <p><strong>Note:</strong> setting this option to {@code true} also
112         * automatically sets {@link #setSupportDtd} to {@code true}.
113         */
114        public void setProcessExternalEntities(boolean processExternalEntities) {
115                this.processExternalEntities = processExternalEntities;
116                if (processExternalEntities) {
117                        this.supportDtd = true;
118                }
119        }
120
121        /**
122         * Return whether XML external entities are allowed.
123         * @see #createXmlReader()
124         */
125        public boolean isProcessExternalEntities() {
126                return this.processExternalEntities;
127        }
128
129
130        /**
131         * Build a new {@link Document} from this marshaller's {@link DocumentBuilderFactory},
132         * as a placeholder for a DOM node.
133         * @see #createDocumentBuilderFactory()
134         * @see #createDocumentBuilder(DocumentBuilderFactory)
135         */
136        protected Document buildDocument() {
137                try {
138                        DocumentBuilder documentBuilder;
139                        synchronized (this.documentBuilderFactoryMonitor) {
140                                if (this.documentBuilderFactory == null) {
141                                        this.documentBuilderFactory = createDocumentBuilderFactory();
142                                }
143                                documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
144                        }
145                        return documentBuilder.newDocument();
146                }
147                catch (ParserConfigurationException ex) {
148                        throw new UnmarshallingFailureException("Could not create document placeholder: " + ex.getMessage(), ex);
149                }
150        }
151
152        /**
153         * Create a {@code DocumentBuilder} that this marshaller will use for creating
154         * DOM documents when passed an empty {@code DOMSource}.
155         * <p>The resulting {@code DocumentBuilderFactory} is cached, so this method
156         * will only be called once.
157         * @return the DocumentBuilderFactory
158         * @throws ParserConfigurationException if thrown by JAXP methods
159         */
160        protected DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
161                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
162                factory.setValidating(false);
163                factory.setNamespaceAware(true);
164                factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
165                factory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
166                return factory;
167        }
168
169        /**
170         * Create a {@code DocumentBuilder} that this marshaller will use for creating
171         * DOM documents when passed an empty {@code DOMSource}.
172         * <p>Can be overridden in subclasses, adding further initialization of the builder.
173         * @param factory the {@code DocumentBuilderFactory} that the DocumentBuilder should be created with
174         * @return the {@code DocumentBuilder}
175         * @throws ParserConfigurationException if thrown by JAXP methods
176         */
177        protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
178                        throws ParserConfigurationException {
179
180                DocumentBuilder documentBuilder = factory.newDocumentBuilder();
181                if (!isProcessExternalEntities()) {
182                        documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
183                }
184                return documentBuilder;
185        }
186
187        /**
188         * Create an {@code XMLReader} that this marshaller will when passed an empty {@code SAXSource}.
189         * @return the XMLReader
190         * @throws SAXException if thrown by JAXP methods
191         */
192        @SuppressWarnings("deprecation")  // on JDK 9
193        protected XMLReader createXmlReader() throws SAXException {
194                XMLReader xmlReader = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();
195                xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
196                xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
197                if (!isProcessExternalEntities()) {
198                        xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
199                }
200                return xmlReader;
201        }
202
203        /**
204         * Determine the default encoding to use for marshalling or unmarshalling from
205         * a byte stream, or {@code null} if none.
206         * <p>The default implementation returns {@code null}.
207         */
208        @Nullable
209        protected String getDefaultEncoding() {
210                return null;
211        }
212
213
214        // Marshalling
215
216        /**
217         * Marshals the object graph with the given root into the provided {@code javax.xml.transform.Result}.
218         * <p>This implementation inspects the given result, and calls {@code marshalDomResult},
219         * {@code marshalSaxResult}, or {@code marshalStreamResult}.
220         * @param graph the root of the object graph to marshal
221         * @param result the result to marshal to
222         * @throws IOException if an I/O exception occurs
223         * @throws XmlMappingException if the given object cannot be marshalled to the result
224         * @throws IllegalArgumentException if {@code result} if neither a {@code DOMResult},
225         * a {@code SAXResult}, nor a {@code StreamResult}
226         * @see #marshalDomResult(Object, javax.xml.transform.dom.DOMResult)
227         * @see #marshalSaxResult(Object, javax.xml.transform.sax.SAXResult)
228         * @see #marshalStreamResult(Object, javax.xml.transform.stream.StreamResult)
229         */
230        @Override
231        public final void marshal(Object graph, Result result) throws IOException, XmlMappingException {
232                if (result instanceof DOMResult) {
233                        marshalDomResult(graph, (DOMResult) result);
234                }
235                else if (StaxUtils.isStaxResult(result)) {
236                        marshalStaxResult(graph, result);
237                }
238                else if (result instanceof SAXResult) {
239                        marshalSaxResult(graph, (SAXResult) result);
240                }
241                else if (result instanceof StreamResult) {
242                        marshalStreamResult(graph, (StreamResult) result);
243                }
244                else {
245                        throw new IllegalArgumentException("Unknown Result type: " + result.getClass());
246                }
247        }
248
249        /**
250         * Template method for handling {@code DOMResult}s.
251         * <p>This implementation delegates to {@code marshalDomNode}.
252         * @param graph the root of the object graph to marshal
253         * @param domResult the {@code DOMResult}
254         * @throws XmlMappingException if the given object cannot be marshalled to the result
255         * @throws IllegalArgumentException if the {@code domResult} is empty
256         * @see #marshalDomNode(Object, org.w3c.dom.Node)
257         */
258        protected void marshalDomResult(Object graph, DOMResult domResult) throws XmlMappingException {
259                if (domResult.getNode() == null) {
260                        domResult.setNode(buildDocument());
261                }
262                marshalDomNode(graph, domResult.getNode());
263        }
264
265        /**
266         * Template method for handling {@code StaxResult}s.
267         * <p>This implementation delegates to {@code marshalXMLSteamWriter} or
268         * {@code marshalXMLEventConsumer}, depending on what is contained in the
269         * {@code StaxResult}.
270         * @param graph the root of the object graph to marshal
271         * @param staxResult a JAXP 1.4 {@link StAXSource}
272         * @throws XmlMappingException if the given object cannot be marshalled to the result
273         * @throws IllegalArgumentException if the {@code domResult} is empty
274         * @see #marshalDomNode(Object, org.w3c.dom.Node)
275         */
276        protected void marshalStaxResult(Object graph, Result staxResult) throws XmlMappingException {
277                XMLStreamWriter streamWriter = StaxUtils.getXMLStreamWriter(staxResult);
278                if (streamWriter != null) {
279                        marshalXmlStreamWriter(graph, streamWriter);
280                }
281                else {
282                        XMLEventWriter eventWriter = StaxUtils.getXMLEventWriter(staxResult);
283                        if (eventWriter != null) {
284                                marshalXmlEventWriter(graph, eventWriter);
285                        }
286                        else {
287                                throw new IllegalArgumentException("StaxResult contains neither XMLStreamWriter nor XMLEventConsumer");
288                        }
289                }
290        }
291
292        /**
293         * Template method for handling {@code SAXResult}s.
294         * <p>This implementation delegates to {@code marshalSaxHandlers}.
295         * @param graph the root of the object graph to marshal
296         * @param saxResult the {@code SAXResult}
297         * @throws XmlMappingException if the given object cannot be marshalled to the result
298         * @see #marshalSaxHandlers(Object, org.xml.sax.ContentHandler, org.xml.sax.ext.LexicalHandler)
299         */
300        protected void marshalSaxResult(Object graph, SAXResult saxResult) throws XmlMappingException {
301                ContentHandler contentHandler = saxResult.getHandler();
302                Assert.notNull(contentHandler, "ContentHandler not set on SAXResult");
303                LexicalHandler lexicalHandler = saxResult.getLexicalHandler();
304                marshalSaxHandlers(graph, contentHandler, lexicalHandler);
305        }
306
307        /**
308         * Template method for handling {@code StreamResult}s.
309         * <p>This implementation delegates to {@code marshalOutputStream} or {@code marshalWriter},
310         * depending on what is contained in the {@code StreamResult}
311         * @param graph the root of the object graph to marshal
312         * @param streamResult the {@code StreamResult}
313         * @throws IOException if an I/O Exception occurs
314         * @throws XmlMappingException if the given object cannot be marshalled to the result
315         * @throws IllegalArgumentException if {@code streamResult} does neither
316         * contain an {@code OutputStream} nor a {@code Writer}
317         */
318        protected void marshalStreamResult(Object graph, StreamResult streamResult)
319                        throws XmlMappingException, IOException {
320
321                if (streamResult.getOutputStream() != null) {
322                        marshalOutputStream(graph, streamResult.getOutputStream());
323                }
324                else if (streamResult.getWriter() != null) {
325                        marshalWriter(graph, streamResult.getWriter());
326                }
327                else {
328                        throw new IllegalArgumentException("StreamResult contains neither OutputStream nor Writer");
329                }
330        }
331
332
333        // Unmarshalling
334
335        /**
336         * Unmarshals the given provided {@code javax.xml.transform.Source} into an object graph.
337         * <p>This implementation inspects the given result, and calls {@code unmarshalDomSource},
338         * {@code unmarshalSaxSource}, or {@code unmarshalStreamSource}.
339         * @param source the source to marshal from
340         * @return the object graph
341         * @throws IOException if an I/O Exception occurs
342         * @throws XmlMappingException if the given source cannot be mapped to an object
343         * @throws IllegalArgumentException if {@code source} is neither a {@code DOMSource},
344         * a {@code SAXSource}, nor a {@code StreamSource}
345         * @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource)
346         * @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)
347         * @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource)
348         */
349        @Override
350        public final Object unmarshal(Source source) throws IOException, XmlMappingException {
351                if (source instanceof DOMSource) {
352                        return unmarshalDomSource((DOMSource) source);
353                }
354                else if (StaxUtils.isStaxSource(source)) {
355                        return unmarshalStaxSource(source);
356                }
357                else if (source instanceof SAXSource) {
358                        return unmarshalSaxSource((SAXSource) source);
359                }
360                else if (source instanceof StreamSource) {
361                        return unmarshalStreamSource((StreamSource) source);
362                }
363                else {
364                        throw new IllegalArgumentException("Unknown Source type: " + source.getClass());
365                }
366        }
367
368        /**
369         * Template method for handling {@code DOMSource}s.
370         * <p>This implementation delegates to {@code unmarshalDomNode}.
371         * If the given source is empty, an empty source {@code Document}
372         * will be created as a placeholder.
373         * @param domSource the {@code DOMSource}
374         * @return the object graph
375         * @throws XmlMappingException if the given source cannot be mapped to an object
376         * @throws IllegalArgumentException if the {@code domSource} is empty
377         * @see #unmarshalDomNode(org.w3c.dom.Node)
378         */
379        protected Object unmarshalDomSource(DOMSource domSource) throws XmlMappingException {
380                if (domSource.getNode() == null) {
381                        domSource.setNode(buildDocument());
382                }
383                try {
384                        return unmarshalDomNode(domSource.getNode());
385                }
386                catch (NullPointerException ex) {
387                        if (!isSupportDtd()) {
388                                throw new UnmarshallingFailureException("NPE while unmarshalling. " +
389                                                "This can happen on JDK 1.6 due to the presence of DTD " +
390                                                "declarations, which are disabled.", ex);
391                        }
392                        throw ex;
393                }
394        }
395
396        /**
397         * Template method for handling {@code StaxSource}s.
398         * <p>This implementation delegates to {@code unmarshalXmlStreamReader} or
399         * {@code unmarshalXmlEventReader}.
400         * @param staxSource the {@code StaxSource}
401         * @return the object graph
402         * @throws XmlMappingException if the given source cannot be mapped to an object
403         */
404        protected Object unmarshalStaxSource(Source staxSource) throws XmlMappingException {
405                XMLStreamReader streamReader = StaxUtils.getXMLStreamReader(staxSource);
406                if (streamReader != null) {
407                        return unmarshalXmlStreamReader(streamReader);
408                }
409                else {
410                        XMLEventReader eventReader = StaxUtils.getXMLEventReader(staxSource);
411                        if (eventReader != null) {
412                                return unmarshalXmlEventReader(eventReader);
413                        }
414                        else {
415                                throw new IllegalArgumentException("StaxSource contains neither XMLStreamReader nor XMLEventReader");
416                        }
417                }
418        }
419
420        /**
421         * Template method for handling {@code SAXSource}s.
422         * <p>This implementation delegates to {@code unmarshalSaxReader}.
423         * @param saxSource the {@code SAXSource}
424         * @return the object graph
425         * @throws XmlMappingException if the given source cannot be mapped to an object
426         * @throws IOException if an I/O Exception occurs
427         * @see #unmarshalSaxReader(org.xml.sax.XMLReader, org.xml.sax.InputSource)
428         */
429        protected Object unmarshalSaxSource(SAXSource saxSource) throws XmlMappingException, IOException {
430                if (saxSource.getXMLReader() == null) {
431                        try {
432                                saxSource.setXMLReader(createXmlReader());
433                        }
434                        catch (SAXException ex) {
435                                throw new UnmarshallingFailureException("Could not create XMLReader for SAXSource", ex);
436                        }
437                }
438                if (saxSource.getInputSource() == null) {
439                        saxSource.setInputSource(new InputSource());
440                }
441                try {
442                        return unmarshalSaxReader(saxSource.getXMLReader(), saxSource.getInputSource());
443                }
444                catch (NullPointerException ex) {
445                        if (!isSupportDtd()) {
446                                throw new UnmarshallingFailureException("NPE while unmarshalling. " +
447                                                "This can happen on JDK 1.6 due to the presence of DTD " +
448                                                "declarations, which are disabled.");
449                        }
450                        throw ex;
451                }
452        }
453
454        /**
455         * Template method for handling {@code StreamSource}s.
456         * <p>This implementation delegates to {@code unmarshalInputStream} or {@code unmarshalReader}.
457         * @param streamSource the {@code StreamSource}
458         * @return the object graph
459         * @throws IOException if an I/O exception occurs
460         * @throws XmlMappingException if the given source cannot be mapped to an object
461         */
462        protected Object unmarshalStreamSource(StreamSource streamSource) throws XmlMappingException, IOException {
463                if (streamSource.getInputStream() != null) {
464                        if (isProcessExternalEntities() && isSupportDtd()) {
465                                return unmarshalInputStream(streamSource.getInputStream());
466                        }
467                        else {
468                                InputSource inputSource = new InputSource(streamSource.getInputStream());
469                                inputSource.setEncoding(getDefaultEncoding());
470                                return unmarshalSaxSource(new SAXSource(inputSource));
471                        }
472                }
473                else if (streamSource.getReader() != null) {
474                        if (isProcessExternalEntities() && isSupportDtd()) {
475                                return unmarshalReader(streamSource.getReader());
476                        }
477                        else {
478                                return unmarshalSaxSource(new SAXSource(new InputSource(streamSource.getReader())));
479                        }
480                }
481                else {
482                        return unmarshalSaxSource(new SAXSource(new InputSource(streamSource.getSystemId())));
483                }
484        }
485
486
487        // Abstract template methods
488
489        /**
490         * Abstract template method for marshalling the given object graph to a DOM {@code Node}.
491         * <p>In practice, node is be a {@code Document} node, a {@code DocumentFragment} node,
492         * or a {@code Element} node. In other words, a node that accepts children.
493         * @param graph the root of the object graph to marshal
494         * @param node the DOM node that will contain the result tree
495         * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
496         * @see org.w3c.dom.Document
497         * @see org.w3c.dom.DocumentFragment
498         * @see org.w3c.dom.Element
499         */
500        protected abstract void marshalDomNode(Object graph, Node node)
501                        throws XmlMappingException;
502
503        /**
504         * Abstract template method for marshalling the given object to a StAX {@code XMLEventWriter}.
505         * @param graph the root of the object graph to marshal
506         * @param eventWriter the {@code XMLEventWriter} to write to
507         * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
508         */
509        protected abstract void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter)
510                        throws XmlMappingException;
511
512        /**
513         * Abstract template method for marshalling the given object to a StAX {@code XMLStreamWriter}.
514         * @param graph the root of the object graph to marshal
515         * @param streamWriter the {@code XMLStreamWriter} to write to
516         * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
517         */
518        protected abstract void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter)
519                        throws XmlMappingException;
520
521        /**
522         * Abstract template method for marshalling the given object graph to a SAX {@code ContentHandler}.
523         * @param graph the root of the object graph to marshal
524         * @param contentHandler the SAX {@code ContentHandler}
525         * @param lexicalHandler the SAX2 {@code LexicalHandler}. Can be {@code null}.
526         * @throws XmlMappingException if the given object cannot be marshalled to the handlers
527         */
528        protected abstract void marshalSaxHandlers(
529                        Object graph, ContentHandler contentHandler, @Nullable LexicalHandler lexicalHandler)
530                        throws XmlMappingException;
531
532        /**
533         * Abstract template method for marshalling the given object graph to a {@code OutputStream}.
534         * @param graph the root of the object graph to marshal
535         * @param outputStream the {@code OutputStream} to write to
536         * @throws XmlMappingException if the given object cannot be marshalled to the writer
537         * @throws IOException if an I/O exception occurs
538         */
539        protected abstract void marshalOutputStream(Object graph, OutputStream outputStream)
540                        throws XmlMappingException, IOException;
541
542        /**
543         * Abstract template method for marshalling the given object graph to a {@code Writer}.
544         * @param graph the root of the object graph to marshal
545         * @param writer the {@code Writer} to write to
546         * @throws XmlMappingException if the given object cannot be marshalled to the writer
547         * @throws IOException if an I/O exception occurs
548         */
549        protected abstract void marshalWriter(Object graph, Writer writer)
550                        throws XmlMappingException, IOException;
551
552        /**
553         * Abstract template method for unmarshalling from a given DOM {@code Node}.
554         * @param node the DOM node that contains the objects to be unmarshalled
555         * @return the object graph
556         * @throws XmlMappingException if the given DOM node cannot be mapped to an object
557         */
558        protected abstract Object unmarshalDomNode(Node node) throws XmlMappingException;
559
560        /**
561         * Abstract template method for unmarshalling from a given Stax {@code XMLEventReader}.
562         * @param eventReader the {@code XMLEventReader} to read from
563         * @return the object graph
564         * @throws XmlMappingException if the given event reader cannot be converted to an object
565         */
566        protected abstract Object unmarshalXmlEventReader(XMLEventReader eventReader)
567                        throws XmlMappingException;
568
569        /**
570         * Abstract template method for unmarshalling from a given Stax {@code XMLStreamReader}.
571         * @param streamReader the {@code XMLStreamReader} to read from
572         * @return the object graph
573         * @throws XmlMappingException if the given stream reader cannot be converted to an object
574         */
575        protected abstract Object unmarshalXmlStreamReader(XMLStreamReader streamReader)
576                        throws XmlMappingException;
577
578        /**
579         * Abstract template method for unmarshalling using a given SAX {@code XMLReader}
580         * and {@code InputSource}.
581         * @param xmlReader the SAX {@code XMLReader} to parse with
582         * @param inputSource the input source to parse from
583         * @return the object graph
584         * @throws XmlMappingException if the given reader and input source cannot be converted to an object
585         * @throws IOException if an I/O exception occurs
586         */
587        protected abstract Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
588                        throws XmlMappingException, IOException;
589
590        /**
591         * Abstract template method for unmarshalling from a given {@code InputStream}.
592         * @param inputStream the {@code InputStreamStream} to read from
593         * @return the object graph
594         * @throws XmlMappingException if the given stream cannot be converted to an object
595         * @throws IOException if an I/O exception occurs
596         */
597        protected abstract Object unmarshalInputStream(InputStream inputStream)
598                        throws XmlMappingException, IOException;
599
600        /**
601         * Abstract template method for unmarshalling from a given {@code Reader}.
602         * @param reader the {@code Reader} to read from
603         * @return the object graph
604         * @throws XmlMappingException if the given reader cannot be converted to an object
605         * @throws IOException if an I/O exception occurs
606         */
607        protected abstract Object unmarshalReader(Reader reader)
608                        throws XmlMappingException, IOException;
609
610}