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: 752464 $ 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 Node into text 184 */ 185 @Converter 186 public String toString(Node node) throws TransformerException { 187 return toString(new DOMSource(node)); 188 } 189 190 /** 191 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 192 * supported (making it easy to derive from this class to add new kinds of conversion). 193 */ 194 @Converter 195 public DOMSource toDOMSource(Source source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 196 if (source instanceof DOMSource) { 197 return (DOMSource) source; 198 } else if (source instanceof SAXSource) { 199 return toDOMSourceFromSAX((SAXSource) source); 200 } else if (source instanceof StreamSource) { 201 return toDOMSourceFromStream((StreamSource) source); 202 } else { 203 return null; 204 } 205 } 206 207 /** 208 * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not 209 * supported (making it easy to derive from this class to add new kinds of conversion). 210 */ 211 @Converter 212 public DOMSource toDOMSource(String text) throws ParserConfigurationException, IOException, SAXException, TransformerException { 213 Source source = toSource(text); 214 if (source != null) { 215 return toDOMSourceFromStream((StreamSource) source); 216 } else { 217 return null; 218 } 219 } 220 221 /** 222 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 223 * supported (making it easy to derive from this class to add new kinds of conversion). 224 */ 225 @Converter 226 public SAXSource toSAXSource(String source) throws IOException, SAXException, TransformerException { 227 return toSAXSource(toSource(source)); 228 } 229 230 /** 231 * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not 232 * supported (making it easy to derive from this class to add new kinds of conversion). 233 */ 234 @Converter 235 public SAXSource toSAXSource(InputStream source) throws IOException, SAXException, TransformerException { 236 return toSAXSource(toStreamSource(source)); 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(Source source) throws IOException, SAXException, TransformerException { 245 if (source instanceof SAXSource) { 246 return (SAXSource) source; 247 } else if (source instanceof DOMSource) { 248 return toSAXSourceFromDOM((DOMSource) source); 249 } else if (source instanceof StreamSource) { 250 return toSAXSourceFromStream((StreamSource) source); 251 } else { 252 return null; 253 } 254 } 255 256 @Converter 257 public StreamSource toStreamSource(Source source) throws TransformerException { 258 if (source instanceof StreamSource) { 259 return (StreamSource) source; 260 } else if (source instanceof DOMSource) { 261 return toStreamSourceFromDOM((DOMSource) source); 262 } else if (source instanceof SAXSource) { 263 return toStreamSourceFromSAX((SAXSource) source); 264 } else { 265 return null; 266 } 267 } 268 269 @Converter 270 public StreamSource toStreamSource(InputStream in) throws TransformerException { 271 if (in != null) { 272 return new StreamSource(in); 273 } 274 return null; 275 } 276 277 @Converter 278 public StreamSource toStreamSource(Reader in) throws TransformerException { 279 if (in != null) { 280 return new StreamSource(in); 281 } 282 return null; 283 } 284 285 @Converter 286 public StreamSource toStreamSource(File in) throws TransformerException { 287 if (in != null) { 288 return new StreamSource(in); 289 } 290 return null; 291 } 292 293 @Converter 294 public StreamSource toStreamSource(byte[] in, Exchange exchange) throws TransformerException { 295 if (in != null) { 296 InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, in); 297 return new StreamSource(is); 298 } 299 return null; 300 } 301 302 @Converter 303 public StreamSource toStreamSource(ByteBuffer in, Exchange exchange) throws TransformerException { 304 if (in != null) { 305 InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, in); 306 return new StreamSource(is); 307 } 308 return null; 309 } 310 311 @Converter 312 public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException { 313 InputSource inputSource = source.getInputSource(); 314 if (inputSource != null) { 315 if (inputSource.getCharacterStream() != null) { 316 return new StreamSource(inputSource.getCharacterStream()); 317 } 318 if (inputSource.getByteStream() != null) { 319 return new StreamSource(inputSource.getByteStream()); 320 } 321 } 322 String result = toString(source); 323 return new StringSource(result); 324 } 325 326 @Converter 327 public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException { 328 String result = toString(source); 329 return new StringSource(result); 330 } 331 332 @Converter 333 public SAXSource toSAXSourceFromStream(StreamSource source) { 334 InputSource inputSource; 335 if (source.getReader() != null) { 336 inputSource = new InputSource(source.getReader()); 337 } else { 338 inputSource = new InputSource(source.getInputStream()); 339 } 340 inputSource.setSystemId(source.getSystemId()); 341 inputSource.setPublicId(source.getPublicId()); 342 return new SAXSource(inputSource); 343 } 344 345 @Converter 346 public Reader toReaderFromSource(Source src) throws TransformerException { 347 StreamSource stSrc = toStreamSource(src); 348 Reader r = stSrc.getReader(); 349 if (r == null) { 350 r = new InputStreamReader(stSrc.getInputStream()); 351 } 352 return r; 353 } 354 355 @Converter 356 public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException { 357 DocumentBuilder builder = createDocumentBuilder(); 358 String systemId = source.getSystemId(); 359 Document document = null; 360 Reader reader = source.getReader(); 361 if (reader != null) { 362 document = builder.parse(new InputSource(reader)); 363 } else { 364 InputStream inputStream = source.getInputStream(); 365 if (inputStream != null) { 366 InputSource inputsource = new InputSource(inputStream); 367 inputsource.setSystemId(systemId); 368 document = builder.parse(inputsource); 369 } else { 370 throw new IOException("No input stream or reader available"); 371 } 372 } 373 return new DOMSource(document, systemId); 374 } 375 376 @Converter 377 public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException { 378 if (DOM_TO_SAX_CLASS != null) { 379 try { 380 Constructor cns = DOM_TO_SAX_CLASS.getConstructor(Node.class); 381 XMLReader converter = (XMLReader) cns.newInstance(source.getNode()); 382 return new SAXSource(converter, new InputSource()); 383 } catch (Exception e) { 384 throw new TransformerException(e); 385 } 386 } else { 387 String str = toString(source); 388 StringReader reader = new StringReader(str); 389 return new SAXSource(new InputSource(reader)); 390 } 391 } 392 393 @Converter 394 public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 395 return new DOMSource(toDOMNodeFromSAX(source)); 396 } 397 398 @Converter 399 public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 400 DOMResult result = new DOMResult(); 401 toResult(source, result); 402 return result.getNode(); 403 } 404 405 /** 406 * Converts the given TRaX Source into a W3C DOM node 407 */ 408 @Converter 409 public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 410 DOMSource domSrc = toDOMSource(source); 411 return domSrc != null ? domSrc.getNode() : null; 412 } 413 414 /** 415 * Create a DOM element from the given source. 416 */ 417 @Converter 418 public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 419 Node node = toDOMNode(source); 420 return toDOMElement(node); 421 } 422 423 /** 424 * Create a DOM element from the DOM node. 425 * Simply cast if the node is an Element, or 426 * return the root element if it is a Document. 427 */ 428 @Converter 429 public Element toDOMElement(Node node) throws TransformerException { 430 // If the node is an document, return the root element 431 if (node instanceof Document) { 432 return ((Document) node).getDocumentElement(); 433 // If the node is an element, just cast it 434 } else if (node instanceof Element) { 435 return (Element) node; 436 // Other node types are not handled 437 } else { 438 throw new TransformerException("Unable to convert DOM node to an Element"); 439 } 440 } 441 442 /** 443 * Converts the given data to a DOM document 444 * 445 * @param data is the data to be parsed 446 * @return the parsed document 447 */ 448 @Converter 449 public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException { 450 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 451 return documentBuilder.parse(new ByteArrayInputStream(data)); 452 } 453 454 /** 455 * Converts the given {@link InputStream} to a DOM document 456 * 457 * @param in is the data to be parsed 458 * @return the parsed document 459 */ 460 @Converter 461 public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { 462 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 463 return documentBuilder.parse(in); 464 } 465 466 /** 467 * Converts the given {@link InputStream} to a DOM document 468 * 469 * @param in is the data to be parsed 470 * @return the parsed document 471 */ 472 @Converter 473 public Document toDOMDocument(Reader in) throws IOException, SAXException, ParserConfigurationException { 474 return toDOMDocument(new InputSource(in)); 475 } 476 477 /** 478 * Converts the given {@link InputSource} to a DOM document 479 * 480 * @param in is the data to be parsed 481 * @return the parsed document 482 */ 483 @Converter 484 public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException { 485 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 486 return documentBuilder.parse(in); 487 } 488 489 /** 490 * Converts the given {@link String} to a DOM document 491 * 492 * @param text is the data to be parsed 493 * @return the parsed document 494 */ 495 @Converter 496 public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException { 497 return toDOMDocument(new StringReader(text)); 498 } 499 500 /** 501 * Converts the given {@link File} to a DOM document 502 * 503 * @param file is the data to be parsed 504 * @return the parsed document 505 */ 506 @Converter 507 public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException { 508 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 509 return documentBuilder.parse(file); 510 } 511 512 /** 513 * Create a DOM document from the given source. 514 */ 515 @Converter 516 public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 517 Node node = toDOMNode(source); 518 return toDOMDocument(node); 519 } 520 521 /** 522 * Create a DOM document from the given Node. 523 * If the node is an document, just cast it, 524 * if the node is an root element, retrieve its 525 * owner element or create a new document and import 526 * the node. 527 */ 528 @Converter 529 public Document toDOMDocument(Node node) throws ParserConfigurationException, TransformerException { 530 // If the node is the document, just cast it 531 if (node instanceof Document) { 532 return (Document) node; 533 // If the node is an element 534 } else if (node instanceof Element) { 535 Element elem = (Element) node; 536 // If this is the root element, return its owner document 537 if (elem.getOwnerDocument().getDocumentElement() == elem) { 538 return elem.getOwnerDocument(); 539 // else, create a new doc and copy the element inside it 540 } else { 541 Document doc = createDocument(); 542 doc.appendChild(doc.importNode(node, true)); 543 return doc; 544 } 545 // other element types are not handled 546 } else { 547 throw new TransformerException("Unable to convert DOM node to a Document"); 548 } 549 } 550 551 // Properties 552 //------------------------------------------------------------------------- 553 public DocumentBuilderFactory getDocumentBuilderFactory() { 554 if (documentBuilderFactory == null) { 555 documentBuilderFactory = createDocumentBuilderFactory(); 556 } 557 return documentBuilderFactory; 558 } 559 560 public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) { 561 this.documentBuilderFactory = documentBuilderFactory; 562 } 563 564 565 // Helper methods 566 //------------------------------------------------------------------------- 567 public DocumentBuilderFactory createDocumentBuilderFactory() { 568 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 569 factory.setNamespaceAware(true); 570 factory.setIgnoringElementContentWhitespace(true); 571 factory.setIgnoringComments(true); 572 return factory; 573 } 574 575 576 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 577 DocumentBuilderFactory factory = getDocumentBuilderFactory(); 578 return factory.newDocumentBuilder(); 579 } 580 581 public Document createDocument() throws ParserConfigurationException { 582 DocumentBuilder builder = createDocumentBuilder(); 583 return builder.newDocument(); 584 } 585 586 public TransformerFactory getTransformerFactory() { 587 if (transformerFactory == null) { 588 transformerFactory = createTransformerFactory(); 589 } 590 return transformerFactory; 591 } 592 593 public void setTransformerFactory(TransformerFactory transformerFactory) { 594 this.transformerFactory = transformerFactory; 595 } 596 597 public Transformer createTransfomer() throws TransformerConfigurationException { 598 TransformerFactory factory = getTransformerFactory(); 599 return factory.newTransformer(); 600 } 601 602 public TransformerFactory createTransformerFactory() { 603 TransformerFactory answer = TransformerFactory.newInstance(); 604 return answer; 605 } 606 607 }