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