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}