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