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