View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.oxm.support;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.OutputStream;
22  import java.io.Reader;
23  import java.io.StringReader;
24  import java.io.Writer;
25  import javax.xml.parsers.DocumentBuilder;
26  import javax.xml.parsers.DocumentBuilderFactory;
27  import javax.xml.parsers.ParserConfigurationException;
28  import javax.xml.stream.XMLEventReader;
29  import javax.xml.stream.XMLEventWriter;
30  import javax.xml.stream.XMLStreamReader;
31  import javax.xml.stream.XMLStreamWriter;
32  import javax.xml.transform.Result;
33  import javax.xml.transform.Source;
34  import javax.xml.transform.dom.DOMResult;
35  import javax.xml.transform.dom.DOMSource;
36  import javax.xml.transform.sax.SAXResult;
37  import javax.xml.transform.sax.SAXSource;
38  import javax.xml.transform.stax.StAXSource;
39  import javax.xml.transform.stream.StreamResult;
40  import javax.xml.transform.stream.StreamSource;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.w3c.dom.Document;
45  import org.w3c.dom.Node;
46  import org.xml.sax.ContentHandler;
47  import org.xml.sax.EntityResolver;
48  import org.xml.sax.InputSource;
49  import org.xml.sax.SAXException;
50  import org.xml.sax.XMLReader;
51  import org.xml.sax.ext.LexicalHandler;
52  import org.xml.sax.helpers.XMLReaderFactory;
53  
54  import org.springframework.oxm.Marshaller;
55  import org.springframework.oxm.Unmarshaller;
56  import org.springframework.oxm.UnmarshallingFailureException;
57  import org.springframework.oxm.XmlMappingException;
58  import org.springframework.util.Assert;
59  import org.springframework.util.xml.StaxUtils;
60  
61  /**
62   * Abstract implementation of the {@code Marshaller} and {@code Unmarshaller} interface.
63   * This implementation inspects the given {@code Source} or {@code Result}, and
64   * delegates further handling to overridable template methods.
65   *
66   * @author Arjen Poutsma
67   * @author Juergen Hoeller
68   * @since 3.0
69   */
70  public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
71  
72  	/** Logger available to subclasses */
73  	protected final Log logger = LogFactory.getLog(getClass());
74  
75  	private boolean processExternalEntities = false;
76  
77  	private DocumentBuilderFactory documentBuilderFactory;
78  
79  	private final Object documentBuilderFactoryMonitor = new Object();
80  
81  
82  	/**
83  	 * Indicates whether external XML entities are processed when unmarshalling.
84  	 * <p>Default is {@code false}, meaning that external entities are not resolved.
85  	 * Note that processing of external entities will only be enabled/disabled when the
86  	 * {@code Source} passed to {@link #unmarshal(Source)} is a {@link SAXSource} or
87  	 * {@link StreamSource}. It has no effect for {@link DOMSource} or {@link StAXSource}
88  	 * instances.
89  	 */
90  	public void setProcessExternalEntities(boolean processExternalEntities) {
91  		this.processExternalEntities = processExternalEntities;
92  	}
93  
94  	/**
95  	 * Returns the configured value for whether XML external entities are allowed.
96  	 * @see #createXmlReader()
97  	 */
98  	public boolean isProcessExternalEntities() {
99  		return this.processExternalEntities;
100 	}
101 
102 
103 	/**
104 	 * Build a new {@link Document} from this marshaller's {@link DocumentBuilderFactory},
105 	 * as a placeholder for a DOM node.
106 	 * @see #createDocumentBuilderFactory()
107 	 * @see #createDocumentBuilder(DocumentBuilderFactory)
108 	 */
109 	protected Document buildDocument() {
110 		try {
111 			synchronized (this.documentBuilderFactoryMonitor) {
112 				if (this.documentBuilderFactory == null) {
113 					this.documentBuilderFactory = createDocumentBuilderFactory();
114 				}
115 			}
116 			DocumentBuilder documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
117 			return documentBuilder.newDocument();
118 		}
119 		catch (ParserConfigurationException ex) {
120 			throw new UnmarshallingFailureException("Could not create document placeholder: " + ex.getMessage(), ex);
121 		}
122 	}
123 
124 	/**
125 	 * Create a {@code DocumentBuilder} that this marshaller will use for creating
126 	 * DOM documents when passed an empty {@code DOMSource}.
127 	 * <p>The resulting {@code DocumentBuilderFactory} is cached, so this method
128 	 * will only be called once.
129 	 * @return the DocumentBuilderFactory
130 	 * @throws ParserConfigurationException if thrown by JAXP methods
131 	 */
132 	protected DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
133 		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
134 		factory.setValidating(false);
135 		factory.setNamespaceAware(true);
136 		return factory;
137 	}
138 
139 	/**
140 	 * Create a {@code DocumentBuilder} that this marshaller will use for creating
141 	 * DOM documents when passed an empty {@code DOMSource}.
142 	 * <p>Can be overridden in subclasses, adding further initialization of the builder.
143 	 * @param factory the {@code DocumentBuilderFactory} that the DocumentBuilder should be created with
144 	 * @return the {@code DocumentBuilder}
145 	 * @throws ParserConfigurationException if thrown by JAXP methods
146 	 */
147 	protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
148 			throws ParserConfigurationException {
149 
150 		return factory.newDocumentBuilder();
151 	}
152 
153 	/**
154 	 * Create an {@code XMLReader} that this marshaller will when passed an empty {@code SAXSource}.
155 	 * @return the XMLReader
156 	 * @throws SAXException if thrown by JAXP methods
157 	 */
158 	protected XMLReader createXmlReader() throws SAXException {
159 		XMLReader xmlReader = XMLReaderFactory.createXMLReader();
160 		xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
161 		if (!isProcessExternalEntities()) {
162 			xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
163 		}
164 		return xmlReader;
165 	}
166 
167 	/**
168 	 * Determine the default encoding to use for marshalling or unmarshalling from
169 	 * a byte stream, or {@code null} if none.
170 	 * <p>The default implementation returns {@code null}.
171 	 */
172 	protected String getDefaultEncoding() {
173 		return null;
174 	}
175 
176 
177 	// Marshalling
178 
179 	/**
180 	 * Marshals the object graph with the given root into the provided {@code javax.xml.transform.Result}.
181 	 * <p>This implementation inspects the given result, and calls {@code marshalDomResult},
182 	 * {@code marshalSaxResult}, or {@code marshalStreamResult}.
183 	 * @param graph the root of the object graph to marshal
184 	 * @param result the result to marshal to
185 	 * @throws IOException if an I/O exception occurs
186 	 * @throws XmlMappingException if the given object cannot be marshalled to the result
187 	 * @throws IllegalArgumentException if {@code result} if neither a {@code DOMResult},
188 	 * a {@code SAXResult}, nor a {@code StreamResult}
189 	 * @see #marshalDomResult(Object, javax.xml.transform.dom.DOMResult)
190 	 * @see #marshalSaxResult(Object, javax.xml.transform.sax.SAXResult)
191 	 * @see #marshalStreamResult(Object, javax.xml.transform.stream.StreamResult)
192 	 */
193 	@Override
194 	public final void marshal(Object graph, Result result) throws IOException, XmlMappingException {
195 		if (result instanceof DOMResult) {
196 			marshalDomResult(graph, (DOMResult) result);
197 		}
198 		else if (StaxUtils.isStaxResult(result)) {
199 			marshalStaxResult(graph, result);
200 		}
201 		else if (result instanceof SAXResult) {
202 			marshalSaxResult(graph, (SAXResult) result);
203 		}
204 		else if (result instanceof StreamResult) {
205 			marshalStreamResult(graph, (StreamResult) result);
206 		}
207 		else {
208 			throw new IllegalArgumentException("Unknown Result type: " + result.getClass());
209 		}
210 	}
211 
212 	/**
213 	 * Template method for handling {@code DOMResult}s.
214 	 * <p>This implementation delegates to {@code marshalDomNode}.
215 	 * @param graph the root of the object graph to marshal
216 	 * @param domResult the {@code DOMResult}
217 	 * @throws XmlMappingException if the given object cannot be marshalled to the result
218 	 * @throws IllegalArgumentException if the {@code domResult} is empty
219 	 * @see #marshalDomNode(Object, org.w3c.dom.Node)
220 	 */
221 	protected void marshalDomResult(Object graph, DOMResult domResult) throws XmlMappingException {
222 		if (domResult.getNode() == null) {
223 			domResult.setNode(buildDocument());
224 		}
225 		marshalDomNode(graph, domResult.getNode());
226 	}
227 
228 	/**
229 	 * Template method for handling {@code StaxResult}s.
230 	 * <p>This implementation delegates to {@code marshalXMLSteamWriter} or
231 	 * {@code marshalXMLEventConsumer}, depending on what is contained in the
232 	 * {@code StaxResult}.
233 	 * @param graph the root of the object graph to marshal
234 	 * @param staxResult a JAXP 1.4 {@link StAXSource}
235 	 * @throws XmlMappingException if the given object cannot be marshalled to the result
236 	 * @throws IllegalArgumentException if the {@code domResult} is empty
237 	 * @see #marshalDomNode(Object, org.w3c.dom.Node)
238 	 */
239 	protected void marshalStaxResult(Object graph, Result staxResult) throws XmlMappingException {
240 		XMLStreamWriter streamWriter = StaxUtils.getXMLStreamWriter(staxResult);
241 		if (streamWriter != null) {
242 			marshalXmlStreamWriter(graph, streamWriter);
243 		}
244 		else {
245 			XMLEventWriter eventWriter = StaxUtils.getXMLEventWriter(staxResult);
246 			if (eventWriter != null) {
247 				marshalXmlEventWriter(graph, eventWriter);
248 			}
249 			else {
250 				throw new IllegalArgumentException("StaxResult contains neither XMLStreamWriter nor XMLEventConsumer");
251 			}
252 		}
253 	}
254 
255 	/**
256 	 * Template method for handling {@code SAXResult}s.
257 	 * <p>This implementation delegates to {@code marshalSaxHandlers}.
258 	 * @param graph the root of the object graph to marshal
259 	 * @param saxResult the {@code SAXResult}
260 	 * @throws XmlMappingException if the given object cannot be marshalled to the result
261 	 * @see #marshalSaxHandlers(Object, org.xml.sax.ContentHandler, org.xml.sax.ext.LexicalHandler)
262 	 */
263 	protected void marshalSaxResult(Object graph, SAXResult saxResult) throws XmlMappingException {
264 		ContentHandler contentHandler = saxResult.getHandler();
265 		Assert.notNull(contentHandler, "ContentHandler not set on SAXResult");
266 		LexicalHandler lexicalHandler = saxResult.getLexicalHandler();
267 		marshalSaxHandlers(graph, contentHandler, lexicalHandler);
268 	}
269 
270 	/**
271 	 * Template method for handling {@code StreamResult}s.
272 	 * <p>This implementation delegates to {@code marshalOutputStream} or {@code marshalWriter},
273 	 * depending on what is contained in the {@code StreamResult}
274 	 * @param graph the root of the object graph to marshal
275 	 * @param streamResult the {@code StreamResult}
276 	 * @throws IOException if an I/O Exception occurs
277 	 * @throws XmlMappingException if the given object cannot be marshalled to the result
278 	 * @throws IllegalArgumentException if {@code streamResult} does neither
279 	 * contain an {@code OutputStream} nor a {@code Writer}
280 	 */
281 	protected void marshalStreamResult(Object graph, StreamResult streamResult)
282 			throws XmlMappingException, IOException {
283 
284 		if (streamResult.getOutputStream() != null) {
285 			marshalOutputStream(graph, streamResult.getOutputStream());
286 		}
287 		else if (streamResult.getWriter() != null) {
288 			marshalWriter(graph, streamResult.getWriter());
289 		}
290 		else {
291 			throw new IllegalArgumentException("StreamResult contains neither OutputStream nor Writer");
292 		}
293 	}
294 
295 
296 	// Unmarshalling
297 
298 	/**
299 	 * Unmarshals the given provided {@code javax.xml.transform.Source} into an object graph.
300 	 * <p>This implementation inspects the given result, and calls {@code unmarshalDomSource},
301 	 * {@code unmarshalSaxSource}, or {@code unmarshalStreamSource}.
302 	 * @param source the source to marshal from
303 	 * @return the object graph
304 	 * @throws IOException if an I/O Exception occurs
305 	 * @throws XmlMappingException if the given source cannot be mapped to an object
306 	 * @throws IllegalArgumentException if {@code source} is neither a {@code DOMSource},
307 	 * a {@code SAXSource}, nor a {@code StreamSource}
308 	 * @see #unmarshalDomSource(javax.xml.transform.dom.DOMSource)
309 	 * @see #unmarshalSaxSource(javax.xml.transform.sax.SAXSource)
310 	 * @see #unmarshalStreamSource(javax.xml.transform.stream.StreamSource)
311 	 */
312 	@Override
313 	public final Object unmarshal(Source source) throws IOException, XmlMappingException {
314 		if (source instanceof DOMSource) {
315 			return unmarshalDomSource((DOMSource) source);
316 		}
317 		else if (StaxUtils.isStaxSource(source)) {
318 			return unmarshalStaxSource(source);
319 		}
320 		else if (source instanceof SAXSource) {
321 			return unmarshalSaxSource((SAXSource) source);
322 		}
323 		else if (source instanceof StreamSource) {
324 			return unmarshalStreamSource((StreamSource) source);
325 		}
326 		else {
327 			throw new IllegalArgumentException("Unknown Source type: " + source.getClass());
328 		}
329 	}
330 
331 	/**
332 	 * Template method for handling {@code DOMSource}s.
333 	 * <p>This implementation delegates to {@code unmarshalDomNode}.
334 	 * If the given source is empty, an empty source {@code Document}
335 	 * will be created as a placeholder.
336 	 * @param domSource the {@code DOMSource}
337 	 * @return the object graph
338 	 * @throws XmlMappingException if the given source cannot be mapped to an object
339 	 * @throws IllegalArgumentException if the {@code domSource} is empty
340 	 * @see #unmarshalDomNode(org.w3c.dom.Node)
341 	 */
342 	protected Object unmarshalDomSource(DOMSource domSource) throws XmlMappingException {
343 		if (domSource.getNode() == null) {
344 			domSource.setNode(buildDocument());
345 		}
346 		return unmarshalDomNode(domSource.getNode());
347 	}
348 
349 	/**
350 	 * Template method for handling {@code StaxSource}s.
351 	 * <p>This implementation delegates to {@code unmarshalXmlStreamReader} or
352 	 * {@code unmarshalXmlEventReader}.
353 	 * @param staxSource the {@code StaxSource}
354 	 * @return the object graph
355 	 * @throws XmlMappingException if the given source cannot be mapped to an object
356 	 */
357 	protected Object unmarshalStaxSource(Source staxSource) throws XmlMappingException {
358 		XMLStreamReader streamReader = StaxUtils.getXMLStreamReader(staxSource);
359 		if (streamReader != null) {
360 			return unmarshalXmlStreamReader(streamReader);
361 		}
362 		else {
363 			XMLEventReader eventReader = StaxUtils.getXMLEventReader(staxSource);
364 			if (eventReader != null) {
365 				return unmarshalXmlEventReader(eventReader);
366 			}
367 			else {
368 				throw new IllegalArgumentException("StaxSource contains neither XMLStreamReader nor XMLEventReader");
369 			}
370 		}
371 	}
372 
373 	/**
374 	 * Template method for handling {@code SAXSource}s.
375 	 * <p>This implementation delegates to {@code unmarshalSaxReader}.
376 	 * @param saxSource the {@code SAXSource}
377 	 * @return the object graph
378 	 * @throws XmlMappingException if the given source cannot be mapped to an object
379 	 * @throws IOException if an I/O Exception occurs
380 	 * @see #unmarshalSaxReader(org.xml.sax.XMLReader, org.xml.sax.InputSource)
381 	 */
382 	protected Object unmarshalSaxSource(SAXSource saxSource) throws XmlMappingException, IOException {
383 		if (saxSource.getXMLReader() == null) {
384 			try {
385 				saxSource.setXMLReader(createXmlReader());
386 			}
387 			catch (SAXException ex) {
388 				throw new UnmarshallingFailureException("Could not create XMLReader for SAXSource", ex);
389 			}
390 		}
391 		if (saxSource.getInputSource() == null) {
392 			saxSource.setInputSource(new InputSource());
393 		}
394 		return unmarshalSaxReader(saxSource.getXMLReader(), saxSource.getInputSource());
395 	}
396 
397 	/**
398 	 * Template method for handling {@code StreamSource}s.
399 	 * <p>This implementation delegates to {@code unmarshalInputStream} or {@code unmarshalReader}.
400 	 * @param streamSource the {@code StreamSource}
401 	 * @return the object graph
402 	 * @throws IOException if an I/O exception occurs
403 	 * @throws XmlMappingException if the given source cannot be mapped to an object
404 	 */
405 	protected Object unmarshalStreamSource(StreamSource streamSource) throws XmlMappingException, IOException {
406 		if (streamSource.getInputStream() != null) {
407 			if (isProcessExternalEntities()) {
408 				return unmarshalInputStream(streamSource.getInputStream());
409 			}
410 			else {
411 				InputSource inputSource = new InputSource(streamSource.getInputStream());
412 				inputSource.setEncoding(getDefaultEncoding());
413 				return unmarshalSaxSource(new SAXSource(inputSource));
414 			}
415 		}
416 		else if (streamSource.getReader() != null) {
417 			if (isProcessExternalEntities()) {
418 				return unmarshalReader(streamSource.getReader());
419 			}
420 			else {
421 				return unmarshalSaxSource(new SAXSource(new InputSource(streamSource.getReader())));
422 			}
423 		}
424 		else {
425 			return unmarshalSaxSource(new SAXSource(new InputSource(streamSource.getSystemId())));
426 		}
427 	}
428 
429 
430 	// Abstract template methods
431 
432 	/**
433 	 * Abstract template method for marshalling the given object graph to a DOM {@code Node}.
434 	 * <p>In practice, node is be a {@code Document} node, a {@code DocumentFragment} node,
435 	 * or a {@code Element} node. In other words, a node that accepts children.
436 	 * @param graph the root of the object graph to marshal
437 	 * @param node the DOM node that will contain the result tree
438 	 * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
439 	 * @see org.w3c.dom.Document
440 	 * @see org.w3c.dom.DocumentFragment
441 	 * @see org.w3c.dom.Element
442 	 */
443 	protected abstract void marshalDomNode(Object graph, Node node)
444 			throws XmlMappingException;
445 
446 	/**
447 	 * Abstract template method for marshalling the given object to a StAX {@code XMLEventWriter}.
448 	 * @param graph the root of the object graph to marshal
449 	 * @param eventWriter the {@code XMLEventWriter} to write to
450 	 * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
451 	 */
452 	protected abstract void marshalXmlEventWriter(Object graph, XMLEventWriter eventWriter)
453 			throws XmlMappingException;
454 
455 	/**
456 	 * Abstract template method for marshalling the given object to a StAX {@code XMLStreamWriter}.
457 	 * @param graph the root of the object graph to marshal
458 	 * @param streamWriter the {@code XMLStreamWriter} to write to
459 	 * @throws XmlMappingException if the given object cannot be marshalled to the DOM node
460 	 */
461 	protected abstract void marshalXmlStreamWriter(Object graph, XMLStreamWriter streamWriter)
462 			throws XmlMappingException;
463 
464 	/**
465 	 * Abstract template method for marshalling the given object graph to a SAX {@code ContentHandler}.
466 	 * @param graph the root of the object graph to marshal
467 	 * @param contentHandler the SAX {@code ContentHandler}
468 	 * @param lexicalHandler the SAX2 {@code LexicalHandler}. Can be {@code null}.
469 	 * @throws XmlMappingException if the given object cannot be marshalled to the handlers
470 	 */
471 	protected abstract void marshalSaxHandlers(
472 			Object graph, ContentHandler contentHandler, LexicalHandler lexicalHandler)
473 			throws XmlMappingException;
474 
475 	/**
476 	 * Abstract template method for marshalling the given object graph to a {@code OutputStream}.
477 	 * @param graph the root of the object graph to marshal
478 	 * @param outputStream the {@code OutputStream} to write to
479 	 * @throws XmlMappingException if the given object cannot be marshalled to the writer
480 	 * @throws IOException if an I/O exception occurs
481 	 */
482 	protected abstract void marshalOutputStream(Object graph, OutputStream outputStream)
483 			throws XmlMappingException, IOException;
484 
485 	/**
486 	 * Abstract template method for marshalling the given object graph to a {@code Writer}.
487 	 * @param graph the root of the object graph to marshal
488 	 * @param writer the {@code Writer} to write to
489 	 * @throws XmlMappingException if the given object cannot be marshalled to the writer
490 	 * @throws IOException if an I/O exception occurs
491 	 */
492 	protected abstract void marshalWriter(Object graph, Writer writer)
493 			throws XmlMappingException, IOException;
494 
495 	/**
496 	 * Abstract template method for unmarshalling from a given DOM {@code Node}.
497 	 * @param node the DOM node that contains the objects to be unmarshalled
498 	 * @return the object graph
499 	 * @throws XmlMappingException if the given DOM node cannot be mapped to an object
500 	 */
501 	protected abstract Object unmarshalDomNode(Node node) throws XmlMappingException;
502 
503 	/**
504 	 * Abstract template method for unmarshalling from a given Stax {@code XMLEventReader}.
505 	 * @param eventReader the {@code XMLEventReader} to read from
506 	 * @return the object graph
507 	 * @throws XmlMappingException if the given event reader cannot be converted to an object
508 	 */
509 	protected abstract Object unmarshalXmlEventReader(XMLEventReader eventReader)
510 			throws XmlMappingException;
511 
512 	/**
513 	 * Abstract template method for unmarshalling from a given Stax {@code XMLStreamReader}.
514 	 * @param streamReader the {@code XMLStreamReader} to read from
515 	 * @return the object graph
516 	 * @throws XmlMappingException if the given stream reader cannot be converted to an object
517 	 */
518 	protected abstract Object unmarshalXmlStreamReader(XMLStreamReader streamReader)
519 			throws XmlMappingException;
520 
521 	/**
522 	 * Abstract template method for unmarshalling using a given SAX {@code XMLReader}
523 	 * and {@code InputSource}.
524 	 * @param xmlReader the SAX {@code XMLReader} to parse with
525 	 * @param inputSource the input source to parse from
526 	 * @return the object graph
527 	 * @throws XmlMappingException if the given reader and input source cannot be converted to an object
528 	 * @throws IOException if an I/O exception occurs
529 	 */
530 	protected abstract Object unmarshalSaxReader(XMLReader xmlReader, InputSource inputSource)
531 			throws XmlMappingException, IOException;
532 
533 	/**
534 	 * Abstract template method for unmarshalling from a given {@code InputStream}.
535 	 * @param inputStream the {@code InputStreamStream} to read from
536 	 * @return the object graph
537 	 * @throws XmlMappingException if the given stream cannot be converted to an object
538 	 * @throws IOException if an I/O exception occurs
539 	 */
540 	protected abstract Object unmarshalInputStream(InputStream inputStream)
541 			throws XmlMappingException, IOException;
542 
543 	/**
544 	 * Abstract template method for unmarshalling from a given {@code Reader}.
545 	 * @param reader the {@code Reader} to read from
546 	 * @return the object graph
547 	 * @throws XmlMappingException if the given reader cannot be converted to an object
548 	 * @throws IOException if an I/O exception occurs
549 	 */
550 	protected abstract Object unmarshalReader(Reader reader)
551 			throws XmlMappingException, IOException;
552 
553 
554 	private static final EntityResolver NO_OP_ENTITY_RESOLVER = new EntityResolver() {
555 		@Override
556 		public InputSource resolveEntity(String publicId, String systemId) {
557 			return new InputSource(new StringReader(""));
558 		}
559 	};
560 
561 }