001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.converter.jaxp; 018 019 import java.io.ByteArrayInputStream; 020 import java.io.ByteArrayOutputStream; 021 import java.io.File; 022 import java.io.FileInputStream; 023 import java.io.FileNotFoundException; 024 import java.io.IOException; 025 import java.io.InputStream; 026 import java.io.InputStreamReader; 027 import java.io.Reader; 028 import java.io.StringReader; 029 import java.io.StringWriter; 030 import java.nio.ByteBuffer; 031 import java.util.ArrayList; 032 import java.util.List; 033 import java.util.Map; 034 import java.util.Properties; 035 036 import javax.xml.parsers.DocumentBuilder; 037 import javax.xml.parsers.DocumentBuilderFactory; 038 import javax.xml.parsers.ParserConfigurationException; 039 import javax.xml.stream.XMLStreamException; 040 import javax.xml.stream.XMLStreamReader; 041 import javax.xml.transform.OutputKeys; 042 import javax.xml.transform.Result; 043 import javax.xml.transform.Source; 044 import javax.xml.transform.Transformer; 045 import javax.xml.transform.TransformerConfigurationException; 046 import javax.xml.transform.TransformerException; 047 import javax.xml.transform.TransformerFactory; 048 import javax.xml.transform.dom.DOMResult; 049 import javax.xml.transform.dom.DOMSource; 050 import javax.xml.transform.sax.SAXSource; 051 import javax.xml.transform.stax.StAXSource; 052 import javax.xml.transform.stream.StreamResult; 053 import javax.xml.transform.stream.StreamSource; 054 055 import org.w3c.dom.Document; 056 import org.w3c.dom.Element; 057 import org.w3c.dom.Node; 058 import org.w3c.dom.NodeList; 059 060 import org.xml.sax.InputSource; 061 import org.xml.sax.SAXException; 062 063 import org.apache.camel.BytesSource; 064 import org.apache.camel.Converter; 065 import org.apache.camel.Exchange; 066 import org.apache.camel.StringSource; 067 import org.apache.camel.builder.xml.XPathBuilder; 068 import org.apache.camel.util.IOHelper; 069 import org.apache.camel.util.ObjectHelper; 070 071 import org.slf4j.Logger; 072 import org.slf4j.LoggerFactory; 073 074 /** 075 * A helper class to transform to and from various JAXB types such as {@link Source} and {@link Document} 076 * 077 * @version 078 */ 079 @Converter 080 public class XmlConverter { 081 @Deprecated 082 //It will be removed in Camel 3.0, please use the Exchange.DEFAULT_CHARSET 083 public static final String DEFAULT_CHARSET_PROPERTY = "org.apache.camel.default.charset"; 084 085 public static final String OUTPUT_PROPERTIES_PREFIX = "org.apache.camel.xmlconverter.output."; 086 public static final String DOCUMENT_BUILDER_FACTORY_FEATURE = "org.apache.camel.xmlconverter.documentBuilderFactory.feature"; 087 public static String defaultCharset = ObjectHelper.getSystemProperty(Exchange.DEFAULT_CHARSET_PROPERTY, "UTF-8"); 088 089 private static final Logger LOG = LoggerFactory.getLogger(XPathBuilder.class); 090 091 private DocumentBuilderFactory documentBuilderFactory; 092 private TransformerFactory transformerFactory; 093 094 public XmlConverter() { 095 } 096 097 public XmlConverter(DocumentBuilderFactory documentBuilderFactory) { 098 this.documentBuilderFactory = documentBuilderFactory; 099 } 100 101 /** 102 * Returns the default set of output properties for conversions. 103 */ 104 public Properties defaultOutputProperties() { 105 Properties properties = new Properties(); 106 properties.put(OutputKeys.ENCODING, defaultCharset); 107 properties.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); 108 return properties; 109 } 110 111 /** 112 * Converts the given input Source into the required result 113 */ 114 public void toResult(Source source, Result result) throws TransformerException { 115 toResult(source, result, defaultOutputProperties()); 116 } 117 118 /** 119 * Converts the given input Source into the required result 120 */ 121 public void toResult(Source source, Result result, Properties outputProperties) throws TransformerException { 122 if (source == null) { 123 return; 124 } 125 126 Transformer transformer = createTransformer(); 127 if (transformer == null) { 128 throw new TransformerException("Could not create a transformer - JAXP is misconfigured!"); 129 } 130 transformer.setOutputProperties(outputProperties); 131 transformer.transform(source, result); 132 } 133 134 /** 135 * Converts the given NodeList to a boolean 136 */ 137 @Converter 138 public Boolean toBoolean(NodeList list) { 139 return list.getLength() > 0; 140 } 141 142 /** 143 * Converts the given byte[] to a Source 144 */ 145 @Converter 146 public BytesSource toBytesSource(byte[] data) { 147 return new BytesSource(data); 148 } 149 150 /** 151 * Converts the given String to a Source 152 */ 153 @Converter 154 public StringSource toStringSource(String data) { 155 return new StringSource(data); 156 } 157 158 /** 159 * Converts the given Document to a Source 160 * @deprecated use toDOMSource instead 161 */ 162 @Deprecated 163 public DOMSource toSource(Document document) { 164 return new DOMSource(document); 165 } 166 167 /** 168 * Converts the given Node to a Source 169 * @throws TransformerException 170 * @throws ParserConfigurationException 171 * @deprecated use toDOMSource instead 172 */ 173 @Deprecated 174 public Source toSource(Node node) throws ParserConfigurationException, TransformerException { 175 return toDOMSource(node); 176 } 177 178 /** 179 * Converts the given Node to a Source 180 * @throws TransformerException 181 * @throws ParserConfigurationException 182 */ 183 @Converter 184 public DOMSource toDOMSource(Node node) throws ParserConfigurationException, TransformerException { 185 Document document = toDOMDocument(node); 186 return new DOMSource(document); 187 } 188 189 /** 190 * Converts the given Document to a DOMSource 191 */ 192 @Converter 193 public DOMSource toDOMSource(Document document) { 194 return new DOMSource(document); 195 } 196 197 /** 198 * Converts the given String to a Source 199 */ 200 @Converter 201 public Source toSource(String data) { 202 return new StringSource(data); 203 } 204 205 /** 206 * Converts the given input Source into text. 207 * 208 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 209 */ 210 @Deprecated 211 public String toString(Source source) throws TransformerException { 212 return toString(source, null); 213 } 214 215 /** 216 * Converts the given input Source into text 217 */ 218 @Converter 219 public String toString(Source source, Exchange exchange) throws TransformerException { 220 if (source == null) { 221 return null; 222 } else if (source instanceof StringSource) { 223 return ((StringSource) source).getText(); 224 } else if (source instanceof BytesSource) { 225 return new String(((BytesSource) source).getData()); 226 } else { 227 StringWriter buffer = new StringWriter(); 228 if (exchange != null) { 229 // check the camelContext properties first 230 Properties properties = ObjectHelper.getCamelPropertiesWithPrefix(OUTPUT_PROPERTIES_PREFIX, exchange.getContext()); 231 if (properties.size() > 0) { 232 toResult(source, new StreamResult(buffer), properties); 233 return buffer.toString(); 234 } 235 } 236 // using the old way to deal with it 237 toResult(source, new StreamResult(buffer)); 238 return buffer.toString(); 239 } 240 } 241 242 /** 243 * Converts the given input Source into bytes 244 */ 245 @Converter 246 public byte[] toByteArray(Source source, Exchange exchange) throws TransformerException { 247 if (source == null) { 248 return null; 249 } else if (source instanceof BytesSource) { 250 return ((BytesSource)source).getData(); 251 } else { 252 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 253 if (exchange != null) { 254 // check the camelContext properties first 255 Properties properties = ObjectHelper.getCamelPropertiesWithPrefix(OUTPUT_PROPERTIES_PREFIX, 256 exchange.getContext()); 257 if (properties.size() > 0) { 258 toResult(source, new StreamResult(buffer), properties); 259 return buffer.toByteArray(); 260 } 261 } 262 // using the old way to deal with it 263 toResult(source, new StreamResult(buffer)); 264 return buffer.toByteArray(); 265 } 266 } 267 268 /** 269 * Converts the given input Node into text 270 * 271 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 272 */ 273 @Deprecated 274 public String toString(Node node) throws TransformerException { 275 return toString(node, null); 276 } 277 278 /** 279 * Converts the given input Node into text 280 */ 281 @Converter 282 public String toString(Node node, Exchange exchange) throws TransformerException { 283 return toString(new DOMSource(node), exchange); 284 } 285 286 /** 287 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 288 * supported (making it easy to derive from this class to add new kinds of conversion). 289 */ 290 @Converter 291 public DOMSource toDOMSource(Source source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 292 if (source instanceof DOMSource) { 293 return (DOMSource) source; 294 } else if (source instanceof SAXSource) { 295 return toDOMSourceFromSAX((SAXSource) source); 296 } else if (source instanceof StreamSource) { 297 return toDOMSourceFromStream((StreamSource) source); 298 } else if (source instanceof StAXSource) { 299 return toDOMSourceFromStAX((StAXSource)source); 300 } else { 301 return null; 302 } 303 } 304 305 /** 306 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 307 * supported (making it easy to derive from this class to add new kinds of conversion). 308 */ 309 @Converter 310 public DOMSource toDOMSource(String text) throws ParserConfigurationException, IOException, SAXException, TransformerException { 311 Source source = toSource(text); 312 if (source != null) { 313 return toDOMSourceFromStream((StreamSource) source); 314 } else { 315 return null; 316 } 317 } 318 319 /** 320 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 321 * supported (making it easy to derive from this class to add new kinds of conversion). 322 */ 323 @Converter 324 public DOMSource toDOMSource(byte[] bytes) throws IOException, SAXException, ParserConfigurationException { 325 InputStream is = new ByteArrayInputStream(bytes); 326 try { 327 return toDOMSource(is); 328 } finally { 329 IOHelper.close(is); 330 } 331 } 332 333 334 /** 335 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 336 * supported (making it easy to derive from this class to add new kinds of conversion). 337 * 338 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 339 */ 340 @Deprecated 341 public SAXSource toSAXSource(String source) throws IOException, SAXException, TransformerException { 342 return toSAXSource(source, null); 343 } 344 345 /** 346 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 347 * supported (making it easy to derive from this class to add new kinds of conversion). 348 */ 349 @Converter 350 public SAXSource toSAXSource(String source, Exchange exchange) throws IOException, SAXException, TransformerException { 351 return toSAXSource(toSource(source), exchange); 352 } 353 354 /** 355 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 356 * supported (making it easy to derive from this class to add new kinds of conversion). 357 * @throws XMLStreamException 358 */ 359 @Converter 360 public StAXSource toStAXSource(String source, Exchange exchange) throws XMLStreamException { 361 XMLStreamReader r = new StaxConverter().createXMLStreamReader(new StringReader(source)); 362 return new StAXSource(r); 363 } 364 365 /** 366 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 367 * supported (making it easy to derive from this class to add new kinds of conversion). 368 * @throws XMLStreamException 369 */ 370 @Converter 371 public StAXSource toStAXSource(byte[] in, Exchange exchange) throws XMLStreamException { 372 XMLStreamReader r = new StaxConverter().createXMLStreamReader(new ByteArrayInputStream(in), exchange); 373 return new StAXSource(r); 374 } 375 376 /** 377 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 378 * supported (making it easy to derive from this class to add new kinds of conversion). 379 * 380 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 381 */ 382 @Deprecated 383 public SAXSource toSAXSource(InputStream source) throws IOException, SAXException, TransformerException { 384 return toSAXSource(source, null); 385 } 386 387 /** 388 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 389 * supported (making it easy to derive from this class to add new kinds of conversion). 390 */ 391 @Converter 392 public SAXSource toSAXSource(InputStream source, Exchange exchange) throws IOException, SAXException, TransformerException { 393 return toSAXSource(toStreamSource(source), exchange); 394 } 395 396 /** 397 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 398 * supported (making it easy to derive from this class to add new kinds of conversion). 399 */ 400 @Converter 401 public SAXSource toSAXSource(byte[] in, Exchange exchange) throws IOException, SAXException, TransformerException { 402 return toSAXSource(toStreamSource(in, exchange), exchange); 403 } 404 405 /** 406 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 407 * supported (making it easy to derive from this class to add new kinds of conversion). 408 * @throws XMLStreamException 409 */ 410 @Converter 411 public StAXSource toStAXSource(InputStream source, Exchange exchange) throws XMLStreamException { 412 XMLStreamReader r = new StaxConverter().createXMLStreamReader(source, exchange); 413 return new StAXSource(r); 414 } 415 416 /** 417 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 418 * supported (making it easy to derive from this class to add new kinds of conversion). 419 */ 420 @Converter 421 public SAXSource toSAXSource(File file, Exchange exchange) throws IOException, SAXException, TransformerException { 422 InputStream is = IOHelper.buffered(new FileInputStream(file)); 423 return toSAXSource(is, exchange); 424 } 425 426 /** 427 * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not 428 * supported (making it easy to derive from this class to add new kinds of conversion). 429 * @throws FileNotFoundException 430 * @throws XMLStreamException 431 */ 432 @Converter 433 public StAXSource toStAXSource(File file, Exchange exchange) throws FileNotFoundException, XMLStreamException { 434 InputStream is = IOHelper.buffered(new FileInputStream(file)); 435 XMLStreamReader r = new StaxConverter().createXMLStreamReader(is, exchange); 436 return new StAXSource(r); 437 } 438 439 /** 440 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 441 * supported (making it easy to derive from this class to add new kinds of conversion). 442 * 443 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 444 */ 445 @Deprecated 446 public SAXSource toSAXSource(Source source) throws IOException, SAXException, TransformerException { 447 return toSAXSource(source, null); 448 } 449 450 /** 451 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 452 * supported (making it easy to derive from this class to add new kinds of conversion). 453 */ 454 @Converter 455 public SAXSource toSAXSource(Source source, Exchange exchange) throws IOException, SAXException, TransformerException { 456 if (source instanceof SAXSource) { 457 return (SAXSource) source; 458 } else if (source instanceof DOMSource) { 459 return toSAXSourceFromDOM((DOMSource) source, exchange); 460 } else if (source instanceof StreamSource) { 461 return toSAXSourceFromStream((StreamSource) source); 462 } else if (source instanceof StAXSource) { 463 return toSAXSourceFromStAX((StAXSource) source, exchange); 464 } else { 465 return null; 466 } 467 } 468 469 /** 470 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 471 */ 472 @Deprecated 473 public StreamSource toStreamSource(Source source) throws TransformerException { 474 return toStreamSource(source, null); 475 } 476 477 @Converter 478 public StreamSource toStreamSource(Source source, Exchange exchange) throws TransformerException { 479 if (source instanceof StreamSource) { 480 return (StreamSource) source; 481 } else if (source instanceof DOMSource) { 482 return toStreamSourceFromDOM((DOMSource) source, exchange); 483 } else if (source instanceof SAXSource) { 484 return toStreamSourceFromSAX((SAXSource) source, exchange); 485 } else if (source instanceof StAXSource) { 486 return toStreamSourceFromStAX((StAXSource) source, exchange); 487 } else { 488 return null; 489 } 490 } 491 492 @Converter 493 public StreamSource toStreamSource(InputStream in) throws TransformerException { 494 return new StreamSource(in); 495 } 496 497 @Converter 498 public StreamSource toStreamSource(Reader in) throws TransformerException { 499 return new StreamSource(in); 500 } 501 502 @Converter 503 public StreamSource toStreamSource(File in) throws TransformerException { 504 return new StreamSource(in); 505 } 506 507 @Converter 508 public StreamSource toStreamSource(byte[] in, Exchange exchange) throws TransformerException { 509 InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, in); 510 return new StreamSource(is); 511 } 512 513 @Converter 514 public StreamSource toStreamSource(ByteBuffer in, Exchange exchange) throws TransformerException { 515 InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, in); 516 return new StreamSource(is); 517 } 518 519 /** 520 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 521 */ 522 @Deprecated 523 public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException { 524 return toStreamSourceFromSAX(source, null); 525 } 526 527 @Converter 528 public StreamSource toStreamSourceFromSAX(SAXSource source, Exchange exchange) throws TransformerException { 529 InputSource inputSource = source.getInputSource(); 530 if (inputSource != null) { 531 if (inputSource.getCharacterStream() != null) { 532 return new StreamSource(inputSource.getCharacterStream()); 533 } 534 if (inputSource.getByteStream() != null) { 535 return new StreamSource(inputSource.getByteStream()); 536 } 537 } 538 String result = toString(source, exchange); 539 return new StringSource(result); 540 } 541 542 /** 543 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 544 */ 545 @Deprecated 546 public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException { 547 return toStreamSourceFromDOM(source, null); 548 } 549 550 @Converter 551 public StreamSource toStreamSourceFromDOM(DOMSource source, Exchange exchange) throws TransformerException { 552 String result = toString(source, exchange); 553 return new StringSource(result); 554 } 555 @Converter 556 public StreamSource toStreamSourceFromStAX(StAXSource source, Exchange exchange) throws TransformerException { 557 String result = toString(source, exchange); 558 return new StringSource(result); 559 } 560 561 @Converter 562 public SAXSource toSAXSourceFromStream(StreamSource source) { 563 InputSource inputSource; 564 if (source.getReader() != null) { 565 inputSource = new InputSource(source.getReader()); 566 } else { 567 inputSource = new InputSource(source.getInputStream()); 568 } 569 inputSource.setSystemId(source.getSystemId()); 570 inputSource.setPublicId(source.getPublicId()); 571 return new SAXSource(inputSource); 572 } 573 574 /** 575 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 576 */ 577 @Deprecated 578 public Reader toReaderFromSource(Source src) throws TransformerException { 579 return toReaderFromSource(src, null); 580 } 581 582 @Converter 583 public Reader toReaderFromSource(Source src, Exchange exchange) throws TransformerException { 584 StreamSource stSrc = toStreamSource(src, exchange); 585 Reader r = stSrc.getReader(); 586 if (r == null) { 587 r = new InputStreamReader(stSrc.getInputStream()); 588 } 589 return r; 590 } 591 592 @Converter 593 public DOMSource toDOMSource(InputStream is) throws ParserConfigurationException, IOException, SAXException { 594 InputSource source = new InputSource(is); 595 String systemId = source.getSystemId(); 596 DocumentBuilder builder = createDocumentBuilder(); 597 Document document = builder.parse(source); 598 return new DOMSource(document, systemId); 599 } 600 601 @Converter 602 public DOMSource toDOMSource(File file) throws ParserConfigurationException, IOException, SAXException { 603 InputStream is = IOHelper.buffered(new FileInputStream(file)); 604 return toDOMSource(is); 605 } 606 607 @Converter 608 public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException { 609 Document document; 610 String systemId = source.getSystemId(); 611 612 DocumentBuilder builder = createDocumentBuilder(); 613 Reader reader = source.getReader(); 614 if (reader != null) { 615 document = builder.parse(new InputSource(reader)); 616 } else { 617 InputStream inputStream = source.getInputStream(); 618 if (inputStream != null) { 619 InputSource inputsource = new InputSource(inputStream); 620 inputsource.setSystemId(systemId); 621 document = builder.parse(inputsource); 622 } else { 623 throw new IOException("No input stream or reader available on StreamSource: " + source); 624 } 625 } 626 return new DOMSource(document, systemId); 627 } 628 629 /** 630 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 631 */ 632 @Deprecated 633 public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException { 634 return toSAXSourceFromDOM(source, null); 635 } 636 637 @Converter 638 public SAXSource toSAXSourceFromDOM(DOMSource source, Exchange exchange) throws TransformerException { 639 String str = toString(source, exchange); 640 StringReader reader = new StringReader(str); 641 return new SAXSource(new InputSource(reader)); 642 } 643 644 @Converter 645 public SAXSource toSAXSourceFromStAX(StAXSource source, Exchange exchange) throws TransformerException { 646 String str = toString(source, exchange); 647 StringReader reader = new StringReader(str); 648 return new SAXSource(new InputSource(reader)); 649 } 650 651 @Converter 652 public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 653 return new DOMSource(toDOMNodeFromSAX(source)); 654 } 655 656 @Converter 657 public DOMSource toDOMSourceFromStAX(StAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 658 return new DOMSource(toDOMNodeFromStAX(source)); 659 } 660 661 @Converter 662 public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 663 DOMResult result = new DOMResult(); 664 toResult(source, result); 665 return result.getNode(); 666 } 667 668 @Converter 669 public Node toDOMNodeFromStAX(StAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 670 DOMResult result = new DOMResult(); 671 toResult(source, result); 672 return result.getNode(); 673 } 674 675 /** 676 * Convert a NodeList consisting of just 1 node to a DOM Node. 677 * @param nl the NodeList 678 * @return the DOM Node 679 */ 680 @Converter(allowNull = true) 681 public Node toDOMNodeFromSingleNodeList(NodeList nl) { 682 return nl.getLength() == 1 ? nl.item(0) : null; 683 } 684 685 /** 686 * Convert a NodeList consisting of just 1 node to a DOM Document. 687 * Cannot convert NodeList with length > 1 because they require a root node. 688 * @param nl the NodeList 689 * @return the DOM Document 690 */ 691 @Converter(allowNull = true) 692 public Document toDOMDocumentFromSingleNodeList(NodeList nl) throws ParserConfigurationException, TransformerException { 693 if (nl.getLength() == 1) { 694 return toDOMDocument(nl.item(0)); 695 } else if (nl instanceof Node) { 696 // as XML parsers may often have nodes that implement both Node and NodeList then the type converter lookup 697 // may lookup either a type converter from NodeList or Node. So let's fallback and try with Node 698 return toDOMDocument((Node) nl); 699 } else { 700 return null; 701 } 702 } 703 704 /** 705 * Converts the given TRaX Source into a W3C DOM node 706 */ 707 @Converter(allowNull = true) 708 public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 709 DOMSource domSrc = toDOMSource(source); 710 return domSrc != null ? domSrc.getNode() : null; 711 } 712 713 /** 714 * Create a DOM element from the given source. 715 */ 716 @Converter 717 public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 718 Node node = toDOMNode(source); 719 return toDOMElement(node); 720 } 721 722 /** 723 * Create a DOM element from the DOM node. 724 * Simply cast if the node is an Element, or 725 * return the root element if it is a Document. 726 */ 727 @Converter 728 public Element toDOMElement(Node node) throws TransformerException { 729 // If the node is an document, return the root element 730 if (node instanceof Document) { 731 return ((Document) node).getDocumentElement(); 732 // If the node is an element, just cast it 733 } else if (node instanceof Element) { 734 return (Element) node; 735 // Other node types are not handled 736 } else { 737 throw new TransformerException("Unable to convert DOM node to an Element"); 738 } 739 } 740 741 /** 742 * Converts the given data to a DOM document 743 * 744 * @param data is the data to be parsed 745 * @return the parsed document 746 */ 747 @Converter 748 public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException { 749 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 750 return documentBuilder.parse(new ByteArrayInputStream(data)); 751 } 752 753 /** 754 * Converts the given {@link InputStream} to a DOM document 755 * 756 * @param in is the data to be parsed 757 * @return the parsed document 758 */ 759 @Converter 760 public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { 761 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 762 return documentBuilder.parse(in); 763 } 764 765 /** 766 * Converts the given {@link InputStream} to a DOM document 767 * 768 * @param in is the data to be parsed 769 * @return the parsed document 770 */ 771 @Converter 772 public Document toDOMDocument(Reader in) throws IOException, SAXException, ParserConfigurationException { 773 return toDOMDocument(new InputSource(in)); 774 } 775 776 /** 777 * Converts the given {@link InputSource} to a DOM document 778 * 779 * @param in is the data to be parsed 780 * @return the parsed document 781 */ 782 @Converter 783 public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException { 784 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 785 return documentBuilder.parse(in); 786 } 787 788 /** 789 * Converts the given {@link String} to a DOM document 790 * 791 * @param text is the data to be parsed 792 * @return the parsed document 793 */ 794 @Converter 795 public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException { 796 return toDOMDocument(new StringReader(text)); 797 } 798 799 /** 800 * Converts the given {@link File} to a DOM document 801 * 802 * @param file is the data to be parsed 803 * @return the parsed document 804 */ 805 @Converter 806 public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException { 807 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 808 return documentBuilder.parse(file); 809 } 810 811 /** 812 * Create a DOM document from the given source. 813 */ 814 @Converter 815 public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 816 Node node = toDOMNode(source); 817 return toDOMDocument(node); 818 } 819 820 /** 821 * Create a DOM document from the given Node. 822 * 823 * If the node is an document, just cast it, if the node is an root element, retrieve its 824 * owner element or create a new document and import the node. 825 */ 826 @Converter 827 public Document toDOMDocument(final Node node) throws ParserConfigurationException, TransformerException { 828 ObjectHelper.notNull(node, "node"); 829 830 // If the node is the document, just cast it 831 if (node instanceof Document) { 832 return (Document) node; 833 // If the node is an element 834 } else if (node instanceof Element) { 835 Element elem = (Element) node; 836 // If this is the root element, return its owner document 837 if (elem.getOwnerDocument().getDocumentElement() == elem) { 838 return elem.getOwnerDocument(); 839 // else, create a new doc and copy the element inside it 840 } else { 841 Document doc = createDocument(); 842 // import node must not occur concurrent on the same node (must be its owner) 843 // so we need to synchronize on it 844 synchronized (node.getOwnerDocument()) { 845 doc.appendChild(doc.importNode(node, true)); 846 } 847 return doc; 848 } 849 // other element types are not handled 850 } else { 851 throw new TransformerException("Unable to convert DOM node to a Document: " + node); 852 } 853 } 854 855 /** 856 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 857 */ 858 @Deprecated 859 public InputStream toInputStream(DOMSource source) throws TransformerException, IOException { 860 return toInputStream(source, null); 861 } 862 863 @Converter 864 public InputStream toInputStream(DOMSource source, Exchange exchange) throws TransformerException, IOException { 865 return new ByteArrayInputStream(toByteArray(source, exchange)); 866 } 867 868 /** 869 * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters. 870 */ 871 @Deprecated 872 public InputStream toInputStream(Document dom) throws TransformerException, IOException { 873 return toInputStream(dom, null); 874 } 875 876 @Converter 877 public InputStream toInputStream(Document dom, Exchange exchange) throws TransformerException, IOException { 878 return toInputStream(new DOMSource(dom), exchange); 879 } 880 881 @Converter 882 public InputSource toInputSource(InputStream is, Exchange exchange) { 883 return new InputSource(is); 884 } 885 886 @Converter 887 public InputSource toInputSource(File file, Exchange exchange) throws FileNotFoundException { 888 InputStream is = IOHelper.buffered(new FileInputStream(file)); 889 return new InputSource(is); 890 } 891 892 // Properties 893 //------------------------------------------------------------------------- 894 895 public DocumentBuilderFactory getDocumentBuilderFactory() { 896 if (documentBuilderFactory == null) { 897 documentBuilderFactory = createDocumentBuilderFactory(); 898 } 899 return documentBuilderFactory; 900 } 901 902 public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) { 903 this.documentBuilderFactory = documentBuilderFactory; 904 } 905 906 public TransformerFactory getTransformerFactory() { 907 if (transformerFactory == null) { 908 transformerFactory = createTransformerFactory(); 909 } 910 return transformerFactory; 911 } 912 913 public void setTransformerFactory(TransformerFactory transformerFactory) { 914 this.transformerFactory = transformerFactory; 915 } 916 917 // Helper methods 918 //------------------------------------------------------------------------- 919 920 protected void setupFeatures(DocumentBuilderFactory factory) { 921 Properties properties = System.getProperties(); 922 List<String> features = new ArrayList<String>(); 923 for (Map.Entry<Object, Object> prop : properties.entrySet()) { 924 String key = (String) prop.getKey(); 925 if (key.startsWith(XmlConverter.DOCUMENT_BUILDER_FACTORY_FEATURE)) { 926 String uri = ObjectHelper.after(key, ":"); 927 Boolean value = Boolean.valueOf((String)prop.getValue()); 928 try { 929 factory.setFeature(uri, value); 930 features.add("feature " + uri + " value " + value); 931 } catch (ParserConfigurationException e) { 932 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}.", new Object[]{uri, value, e}); 933 } 934 } 935 } 936 if (features.size() > 0) { 937 StringBuffer featureString = new StringBuffer(); 938 // just log the configured feature 939 for (String feature : features) { 940 if (featureString.length() != 0) { 941 featureString.append(", "); 942 } 943 featureString.append(feature); 944 } 945 LOG.info("DocumentBuilderFactory has been set with features {{}}.", featureString.toString()); 946 } 947 948 } 949 950 public DocumentBuilderFactory createDocumentBuilderFactory() { 951 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 952 factory.setNamespaceAware(true); 953 factory.setIgnoringElementContentWhitespace(true); 954 factory.setIgnoringComments(true); 955 try { 956 // Disable the external-general-entities by default 957 factory.setFeature("http://xml.org/sax/features/external-general-entities", false); 958 } catch (ParserConfigurationException e) { 959 LOG.warn("DocumentBuilderFactory doesn't support the feature {} with value {}, due to {}." 960 , new Object[]{"http://xml.org/sax/features/external-general-entities", true, e}); 961 } 962 // setup the feature from the system property 963 setupFeatures(factory); 964 return factory; 965 } 966 967 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 968 DocumentBuilderFactory factory = getDocumentBuilderFactory(); 969 return factory.newDocumentBuilder(); 970 } 971 972 public Document createDocument() throws ParserConfigurationException { 973 DocumentBuilder builder = createDocumentBuilder(); 974 return builder.newDocument(); 975 } 976 977 /** 978 * @deprecated use {@link #createTransformer}, will be removed in Camel 3.0 979 */ 980 @Deprecated 981 public Transformer createTransfomer() throws TransformerConfigurationException { 982 return createTransformer(); 983 } 984 985 public Transformer createTransformer() throws TransformerConfigurationException { 986 TransformerFactory factory = getTransformerFactory(); 987 return factory.newTransformer(); 988 } 989 990 public TransformerFactory createTransformerFactory() { 991 TransformerFactory factory = TransformerFactory.newInstance(); 992 // Enable the Security feature by default 993 try { 994 factory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); 995 } catch (TransformerConfigurationException e) { 996 LOG.warn("TransformerFactory doesn't support the feature {} with value {}, due to {}.", new Object[]{javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, "true", e}); 997 } 998 factory.setErrorListener(new XmlErrorListener()); 999 return factory; 1000 } 1001 1002 }