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