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.File; 021 import java.io.IOException; 022 import java.io.InputStream; 023 import java.io.InputStreamReader; 024 import java.io.Reader; 025 import java.io.StringReader; 026 import java.io.StringWriter; 027 import java.lang.reflect.Constructor; 028 import java.nio.ByteBuffer; 029 import java.util.Properties; 030 031 import javax.xml.parsers.DocumentBuilder; 032 import javax.xml.parsers.DocumentBuilderFactory; 033 import javax.xml.parsers.ParserConfigurationException; 034 import javax.xml.transform.OutputKeys; 035 import javax.xml.transform.Result; 036 import javax.xml.transform.Source; 037 import javax.xml.transform.Transformer; 038 import javax.xml.transform.TransformerConfigurationException; 039 import javax.xml.transform.TransformerException; 040 import javax.xml.transform.TransformerFactory; 041 import javax.xml.transform.dom.DOMResult; 042 import javax.xml.transform.dom.DOMSource; 043 import javax.xml.transform.sax.SAXSource; 044 import javax.xml.transform.stream.StreamResult; 045 import javax.xml.transform.stream.StreamSource; 046 047 import org.w3c.dom.Document; 048 import org.w3c.dom.Element; 049 import org.w3c.dom.Node; 050 051 import org.xml.sax.InputSource; 052 import org.xml.sax.SAXException; 053 import org.xml.sax.XMLReader; 054 055 import org.apache.camel.Converter; 056 import org.apache.camel.converter.IOConverter; 057 import org.apache.camel.converter.NIOConverter; 058 import org.apache.camel.util.ObjectHelper; 059 060 061 /** 062 * A helper class to transform to and from various JAXB types such as {@link Source} and {@link Document} 063 * 064 * @version $Revision: 13627 $ 065 */ 066 @Converter 067 public class XmlConverter { 068 public static final String DEFAULT_CHARSET_PROPERTY = "org.apache.camel.default.charset"; 069 070 public static String defaultCharset = ObjectHelper.getSystemProperty(DEFAULT_CHARSET_PROPERTY, "UTF-8"); 071 072 /* 073 * When converting a DOM tree to a SAXSource, we try to use Xalan internal DOM parser if 074 * available. Else, transform the DOM tree to a String and build a SAXSource on top of it. 075 */ 076 private static final Class DOM_TO_SAX_CLASS; 077 078 private DocumentBuilderFactory documentBuilderFactory; 079 private TransformerFactory transformerFactory; 080 081 static { 082 // TODO: Use ObjectHelper.loadClass instead 083 Class cl = null; 084 try { 085 cl = Class.forName("org.apache.xalan.xsltc.trax.DOM2SAX"); 086 } catch (Throwable t) { 087 // do nothing here 088 } 089 DOM_TO_SAX_CLASS = cl; 090 } 091 092 093 public XmlConverter() { 094 } 095 096 public XmlConverter(DocumentBuilderFactory documentBuilderFactory) { 097 this.documentBuilderFactory = documentBuilderFactory; 098 } 099 100 /** 101 * Returns the default set of output properties for conversions. 102 */ 103 public Properties defaultOutputProperties() { 104 Properties properties = new Properties(); 105 properties.put(OutputKeys.ENCODING, defaultCharset); 106 properties.put(OutputKeys.OMIT_XML_DECLARATION, "yes"); 107 return properties; 108 } 109 110 /** 111 * Converts the given input Source into the required result 112 */ 113 public void toResult(Source source, Result result) throws TransformerException { 114 toResult(source, result, defaultOutputProperties()); 115 } 116 117 /** 118 * Converts the given input Source into the required result 119 */ 120 public void toResult(Source source, Result result, Properties outputProperties) throws TransformerException { 121 if (source == null) { 122 return; 123 } 124 125 Transformer transformer = createTransfomer(); 126 if (transformer == null) { 127 throw new TransformerException("Could not create a transformer - JAXP is misconfigured!"); 128 } 129 transformer.setOutputProperties(outputProperties); 130 transformer.transform(source, result); 131 } 132 133 /** 134 * Converts the given byte[] to a Source 135 */ 136 @Converter 137 public BytesSource toSource(byte[] data) { 138 return new BytesSource(data); 139 } 140 141 142 /** 143 * Converts the given String to a Source 144 */ 145 @Converter 146 public StringSource toSource(String data) { 147 return new StringSource(data); 148 } 149 150 /** 151 * Converts the given Document to a Source 152 */ 153 @Converter 154 public DOMSource toSource(Document document) { 155 return new DOMSource(document); 156 } 157 158 /** 159 * Converts the given Node to a Source 160 */ 161 @Converter 162 public Source toSource(Node node) { 163 return new DOMSource(node); 164 } 165 166 /** 167 * Converts the given input Source into text 168 */ 169 @Converter 170 public String toString(Source source) throws TransformerException { 171 if (source == null) { 172 return null; 173 } else if (source instanceof StringSource) { 174 return ((StringSource) source).getText(); 175 } else if (source instanceof BytesSource) { 176 return new String(((BytesSource) source).getData()); 177 } else { 178 StringWriter buffer = new StringWriter(); 179 toResult(source, new StreamResult(buffer)); 180 return buffer.toString(); 181 } 182 } 183 184 /** 185 * Converts the given input Node into text 186 */ 187 /* 188 @Converter 189 public String toString(NodeList nodeList) throws TransformerException { 190 StringWriter buffer = new StringWriter(); 191 for (int i = 0, size = nodeList.getLength(); i < size; i++) { 192 Node node = nodeList.item(i); 193 Source source = new DOMSource(node); 194 toResult(source, new StreamResult(buffer)); 195 } 196 return buffer.toString(); 197 } 198 */ 199 200 /** 201 * Converts the given input Node into text 202 */ 203 @Converter 204 public String toString(Node node) throws TransformerException { 205 return toString(new DOMSource(node)); 206 } 207 208 /** 209 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 210 * supported (making it easy to derive from this class to add new kinds of conversion). 211 */ 212 @Converter 213 public DOMSource toDOMSource(Source source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 214 if (source instanceof DOMSource) { 215 return (DOMSource) source; 216 } else if (source instanceof SAXSource) { 217 return toDOMSourceFromSAX((SAXSource) source); 218 } else if (source instanceof StreamSource) { 219 return toDOMSourceFromStream((StreamSource) source); 220 } else { 221 return null; 222 } 223 } 224 225 /** 226 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 227 * supported (making it easy to derive from this class to add new kinds of conversion). 228 */ 229 @Converter 230 public DOMSource toDOMSource(String text) throws ParserConfigurationException, IOException, SAXException, TransformerException { 231 Source source = toSource(text); 232 if (source != null) { 233 return toDOMSourceFromStream((StreamSource) source); 234 } else { 235 return null; 236 } 237 } 238 239 /** 240 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 241 * supported (making it easy to derive from this class to add new kinds of conversion). 242 */ 243 @Converter 244 public SAXSource toSAXSource(String source) throws IOException, SAXException, TransformerException { 245 return toSAXSource(toSource(source)); 246 } 247 248 /** 249 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 250 * supported (making it easy to derive from this class to add new kinds of conversion). 251 */ 252 @Converter 253 public SAXSource toSAXSource(InputStream source) throws IOException, SAXException, TransformerException { 254 return toSAXSource(toStreamSource(source)); 255 } 256 257 /** 258 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 259 * supported (making it easy to derive from this class to add new kinds of conversion). 260 */ 261 @Converter 262 public SAXSource toSAXSource(Source source) throws IOException, SAXException, TransformerException { 263 if (source instanceof SAXSource) { 264 return (SAXSource) source; 265 } else if (source instanceof DOMSource) { 266 return toSAXSourceFromDOM((DOMSource) source); 267 } else if (source instanceof StreamSource) { 268 return toSAXSourceFromStream((StreamSource) source); 269 } else { 270 return null; 271 } 272 } 273 274 @Converter 275 public StreamSource toStreamSource(Source source) throws TransformerException { 276 if (source instanceof StreamSource) { 277 return (StreamSource) source; 278 } else if (source instanceof DOMSource) { 279 return toStreamSourceFromDOM((DOMSource) source); 280 } else if (source instanceof SAXSource) { 281 return toStreamSourceFromSAX((SAXSource) source); 282 } else { 283 return null; 284 } 285 } 286 287 @Converter 288 public StreamSource toStreamSource(InputStream in) throws TransformerException { 289 if (in != null) { 290 return new StreamSource(in); 291 } 292 return null; 293 } 294 295 @Converter 296 public StreamSource toStreamSource(Reader in) throws TransformerException { 297 if (in != null) { 298 return new StreamSource(in); 299 } 300 return null; 301 } 302 303 @Converter 304 public StreamSource toStreamSource(File in) throws TransformerException { 305 if (in != null) { 306 return new StreamSource(in); 307 } 308 return null; 309 } 310 311 @Converter 312 public StreamSource toStreamSource(byte[] in) throws TransformerException { 313 if (in != null) { 314 return new StreamSource(IOConverter.toInputStream(in)); 315 } 316 return null; 317 } 318 319 @Converter 320 public StreamSource toStreamSource(ByteBuffer in) throws TransformerException { 321 if (in != null) { 322 return new StreamSource(NIOConverter.toInputStream(in)); 323 } 324 return null; 325 } 326 327 @Converter 328 public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException { 329 InputSource inputSource = source.getInputSource(); 330 if (inputSource != null) { 331 if (inputSource.getCharacterStream() != null) { 332 return new StreamSource(inputSource.getCharacterStream()); 333 } 334 if (inputSource.getByteStream() != null) { 335 return new StreamSource(inputSource.getByteStream()); 336 } 337 } 338 String result = toString(source); 339 return new StringSource(result); 340 } 341 342 @Converter 343 public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException { 344 String result = toString(source); 345 return new StringSource(result); 346 } 347 348 @Converter 349 public SAXSource toSAXSourceFromStream(StreamSource source) { 350 InputSource inputSource; 351 if (source.getReader() != null) { 352 inputSource = new InputSource(source.getReader()); 353 } else { 354 inputSource = new InputSource(source.getInputStream()); 355 } 356 inputSource.setSystemId(source.getSystemId()); 357 inputSource.setPublicId(source.getPublicId()); 358 return new SAXSource(inputSource); 359 } 360 361 @Converter 362 public Reader toReaderFromSource(Source src) throws TransformerException { 363 StreamSource stSrc = toStreamSource(src); 364 Reader r = stSrc.getReader(); 365 if (r == null) { 366 r = new InputStreamReader(stSrc.getInputStream()); 367 } 368 return r; 369 } 370 371 @Converter 372 public DOMSource toDOMSource(InputStream is) throws ParserConfigurationException, IOException, SAXException { 373 InputSource source = new InputSource(is); 374 String systemId = source.getSystemId(); 375 DocumentBuilder builder = createDocumentBuilder(); 376 Document document = builder.parse(source); 377 return new DOMSource(document, systemId); 378 } 379 380 @Converter 381 public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException { 382 DocumentBuilder builder = createDocumentBuilder(); 383 String systemId = source.getSystemId(); 384 Document document = null; 385 Reader reader = source.getReader(); 386 if (reader != null) { 387 document = builder.parse(new InputSource(reader)); 388 } else { 389 InputStream inputStream = source.getInputStream(); 390 if (inputStream != null) { 391 InputSource inputsource = new InputSource(inputStream); 392 inputsource.setSystemId(systemId); 393 document = builder.parse(inputsource); 394 } else { 395 throw new IOException("No input stream or reader available"); 396 } 397 } 398 return new DOMSource(document, systemId); 399 } 400 401 @Converter 402 public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException { 403 if (DOM_TO_SAX_CLASS != null) { 404 try { 405 Constructor cns = DOM_TO_SAX_CLASS.getConstructor(Node.class); 406 XMLReader converter = (XMLReader) cns.newInstance(source.getNode()); 407 return new SAXSource(converter, new InputSource()); 408 } catch (Exception e) { 409 throw new TransformerException(e); 410 } 411 } else { 412 String str = toString(source); 413 StringReader reader = new StringReader(str); 414 return new SAXSource(new InputSource(reader)); 415 } 416 } 417 418 @Converter 419 public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 420 return new DOMSource(toDOMNodeFromSAX(source)); 421 } 422 423 @Converter 424 public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 425 DOMResult result = new DOMResult(); 426 toResult(source, result); 427 return result.getNode(); 428 } 429 430 /** 431 * Converts the given TRaX Source into a W3C DOM node 432 * @throws SAXException 433 * @throws IOException 434 * @throws ParserConfigurationException 435 */ 436 @Converter 437 public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 438 DOMSource domSrc = toDOMSource(source); 439 return domSrc != null ? domSrc.getNode() : null; 440 } 441 442 /** 443 * Create a DOM element from the given source. 444 */ 445 @Converter 446 public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 447 Node node = toDOMNode(source); 448 return toDOMElement(node); 449 } 450 451 /** 452 * Create a DOM element from the DOM node. 453 * Simply cast if the node is an Element, or 454 * return the root element if it is a Document. 455 */ 456 @Converter 457 public Element toDOMElement(Node node) throws TransformerException { 458 // If the node is an document, return the root element 459 if (node instanceof Document) { 460 return ((Document) node).getDocumentElement(); 461 // If the node is an element, just cast it 462 } else if (node instanceof Element) { 463 return (Element) node; 464 // Other node types are not handled 465 } else { 466 throw new TransformerException("Unable to convert DOM node to an Element"); 467 } 468 } 469 470 /** 471 * Converts the given data to a DOM document 472 * 473 * @param data is the data to be parsed 474 * @return the parsed document 475 */ 476 @Converter 477 public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException { 478 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 479 return documentBuilder.parse(new ByteArrayInputStream(data)); 480 } 481 482 /** 483 * Converts the given {@link InputStream} to a DOM document 484 * 485 * @param in is the data to be parsed 486 * @return the parsed document 487 */ 488 @Converter 489 public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { 490 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 491 return documentBuilder.parse(in); 492 } 493 494 /** 495 * Converts the given {@link InputStream} to a DOM document 496 * 497 * @param in is the data to be parsed 498 * @return the parsed document 499 */ 500 @Converter 501 public Document toDOMDocument(Reader in) throws IOException, SAXException, ParserConfigurationException { 502 return toDOMDocument(new InputSource(in)); 503 } 504 505 /** 506 * Converts the given {@link InputSource} to a DOM document 507 * 508 * @param in is the data to be parsed 509 * @return the parsed document 510 */ 511 @Converter 512 public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException { 513 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 514 return documentBuilder.parse(in); 515 } 516 517 /** 518 * Converts the given {@link String} to a DOM document 519 * 520 * @param text is the data to be parsed 521 * @return the parsed document 522 */ 523 @Converter 524 public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException { 525 return toDOMDocument(new StringReader(text)); 526 } 527 528 /** 529 * Converts the given {@link File} to a DOM document 530 * 531 * @param file is the data to be parsed 532 * @return the parsed document 533 */ 534 @Converter 535 public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException { 536 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 537 return documentBuilder.parse(file); 538 } 539 540 541 /** 542 * Create a DOM document from the given source. 543 */ 544 @Converter 545 public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 546 Node node = toDOMNode(source); 547 return toDOMDocument(node); 548 } 549 550 /** 551 * Create a DOM document from the given Node. 552 * If the node is an document, just cast it, 553 * if the node is an root element, retrieve its 554 * owner element or create a new document and import 555 * the node. 556 */ 557 @Converter 558 public Document toDOMDocument(Node node) throws ParserConfigurationException, TransformerException { 559 // If the node is the document, just cast it 560 if (node instanceof Document) { 561 return (Document) node; 562 // If the node is an element 563 } else if (node instanceof Element) { 564 Element elem = (Element) node; 565 // If this is the root element, return its owner document 566 if (elem.getOwnerDocument().getDocumentElement() == elem) { 567 return elem.getOwnerDocument(); 568 // else, create a new doc and copy the element inside it 569 } else { 570 Document doc = createDocument(); 571 doc.appendChild(doc.importNode(node, true)); 572 return doc; 573 } 574 // other element types are not handled 575 } else { 576 throw new TransformerException("Unable to convert DOM node to a Document"); 577 } 578 } 579 580 // Properties 581 //------------------------------------------------------------------------- 582 public DocumentBuilderFactory getDocumentBuilderFactory() { 583 if (documentBuilderFactory == null) { 584 documentBuilderFactory = createDocumentBuilderFactory(); 585 } 586 return documentBuilderFactory; 587 } 588 589 public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) { 590 this.documentBuilderFactory = documentBuilderFactory; 591 } 592 593 594 // Helper methods 595 //------------------------------------------------------------------------- 596 public DocumentBuilderFactory createDocumentBuilderFactory() { 597 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 598 factory.setNamespaceAware(true); 599 factory.setIgnoringElementContentWhitespace(true); 600 factory.setIgnoringComments(true); 601 return factory; 602 } 603 604 605 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 606 DocumentBuilderFactory factory = getDocumentBuilderFactory(); 607 return factory.newDocumentBuilder(); 608 } 609 610 public Document createDocument() throws ParserConfigurationException { 611 DocumentBuilder builder = createDocumentBuilder(); 612 return builder.newDocument(); 613 } 614 615 public TransformerFactory getTransformerFactory() { 616 if (transformerFactory == null) { 617 transformerFactory = createTransformerFactory(); 618 } 619 return transformerFactory; 620 } 621 622 public void setTransformerFactory(TransformerFactory transformerFactory) { 623 this.transformerFactory = transformerFactory; 624 } 625 626 public Transformer createTransfomer() throws TransformerConfigurationException { 627 TransformerFactory factory = getTransformerFactory(); 628 return factory.newTransformer(); 629 } 630 631 public TransformerFactory createTransformerFactory() { 632 TransformerFactory answer = TransformerFactory.newInstance(); 633 return answer; 634 } 635 636 }