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: 660275 $ 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 // TODO: Use ObjectHelper.loadClass instead 082 Class cl = null; 083 try { 084 cl = Class.forName("org.apache.xalan.xsltc.trax.DOM2SAX"); 085 } catch (Throwable t) { 086 // do nothing here 087 } 088 DOM_TO_SAX_CLASS = cl; 089 } 090 091 092 public XmlConverter() { 093 } 094 095 public XmlConverter(DocumentBuilderFactory documentBuilderFactory) { 096 this.documentBuilderFactory = documentBuilderFactory; 097 } 098 099 /** 100 * Converts the given input Source into the required result 101 */ 102 public void toResult(Source source, Result result) throws TransformerException { 103 if (source == null) { 104 return; 105 } 106 Transformer transformer = createTransfomer(); 107 if (transformer == null) { 108 throw new TransformerException("Could not create a transformer - JAXP is misconfigured!"); 109 } 110 transformer.setOutputProperty(OutputKeys.ENCODING, defaultCharset); 111 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 112 transformer.transform(source, result); 113 } 114 115 /** 116 * Converts the given byte[] to a Source 117 */ 118 @Converter 119 public BytesSource toSource(byte[] data) { 120 return new BytesSource(data); 121 } 122 123 124 /** 125 * Converts the given String to a Source 126 */ 127 @Converter 128 public StringSource toSource(String data) { 129 return new StringSource(data); 130 } 131 132 /** 133 * Converts the given Document to a Source 134 */ 135 @Converter 136 public DOMSource toSource(Document document) { 137 return new DOMSource(document); 138 } 139 140 /** 141 * Converts the given Node to a Source 142 */ 143 @Converter 144 public Source toSource(Node node) { 145 return new DOMSource(node); 146 } 147 148 /** 149 * Converts the given input Source into text 150 */ 151 @Converter 152 public String toString(Source source) throws TransformerException { 153 if (source == null) { 154 return null; 155 } else if (source instanceof StringSource) { 156 return ((StringSource) source).getText(); 157 } else if (source instanceof BytesSource) { 158 return new String(((BytesSource) source).getData()); 159 } else { 160 StringWriter buffer = new StringWriter(); 161 toResult(source, new StreamResult(buffer)); 162 return buffer.toString(); 163 } 164 } 165 166 /** 167 * Converts the given input Node into text 168 */ 169 /* 170 @Converter 171 public String toString(NodeList nodeList) throws TransformerException { 172 StringWriter buffer = new StringWriter(); 173 for (int i = 0, size = nodeList.getLength(); i < size; i++) { 174 Node node = nodeList.item(i); 175 Source source = new DOMSource(node); 176 toResult(source, new StreamResult(buffer)); 177 } 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) throws TransformerException { 295 if (in != null) { 296 return new StreamSource(IOConverter.toInputStream(in)); 297 } 298 return null; 299 } 300 301 @Converter 302 public StreamSource toStreamSource(ByteBuffer in) throws TransformerException { 303 if (in != null) { 304 return new StreamSource(NIOConverter.toInputStream(in)); 305 } 306 return null; 307 } 308 309 @Converter 310 public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException { 311 InputSource inputSource = source.getInputSource(); 312 if (inputSource != null) { 313 if (inputSource.getCharacterStream() != null) { 314 return new StreamSource(inputSource.getCharacterStream()); 315 } 316 if (inputSource.getByteStream() != null) { 317 return new StreamSource(inputSource.getByteStream()); 318 } 319 } 320 String result = toString(source); 321 return new StringSource(result); 322 } 323 324 @Converter 325 public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException { 326 String result = toString(source); 327 return new StringSource(result); 328 } 329 330 @Converter 331 public SAXSource toSAXSourceFromStream(StreamSource source) { 332 InputSource inputSource; 333 if (source.getReader() != null) { 334 inputSource = new InputSource(source.getReader()); 335 } else { 336 inputSource = new InputSource(source.getInputStream()); 337 } 338 inputSource.setSystemId(source.getSystemId()); 339 inputSource.setPublicId(source.getPublicId()); 340 return new SAXSource(inputSource); 341 } 342 343 @Converter 344 public Reader toReaderFromSource(Source src) throws TransformerException { 345 StreamSource stSrc = toStreamSource(src); 346 Reader r = stSrc.getReader(); 347 if (r == null) { 348 r = new InputStreamReader(stSrc.getInputStream()); 349 } 350 return r; 351 } 352 353 @Converter 354 public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException { 355 DocumentBuilder builder = createDocumentBuilder(); 356 String systemId = source.getSystemId(); 357 Document document = null; 358 Reader reader = source.getReader(); 359 if (reader != null) { 360 document = builder.parse(new InputSource(reader)); 361 } else { 362 InputStream inputStream = source.getInputStream(); 363 if (inputStream != null) { 364 InputSource inputsource = new InputSource(inputStream); 365 inputsource.setSystemId(systemId); 366 document = builder.parse(inputsource); 367 } else { 368 throw new IOException("No input stream or reader available"); 369 } 370 } 371 return new DOMSource(document, systemId); 372 } 373 374 @Converter 375 public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException { 376 if (DOM_TO_SAX_CLASS != null) { 377 try { 378 Constructor cns = DOM_TO_SAX_CLASS.getConstructor(Node.class); 379 XMLReader converter = (XMLReader) cns.newInstance(source.getNode()); 380 return new SAXSource(converter, new InputSource()); 381 } catch (Exception e) { 382 throw new TransformerException(e); 383 } 384 } else { 385 String str = toString(source); 386 StringReader reader = new StringReader(str); 387 return new SAXSource(new InputSource(reader)); 388 } 389 } 390 391 @Converter 392 public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 393 return new DOMSource(toDOMNodeFromSAX(source)); 394 } 395 396 @Converter 397 public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 398 DOMResult result = new DOMResult(); 399 toResult(source, result); 400 return result.getNode(); 401 } 402 403 /** 404 * Converts the given TRaX Source into a W3C DOM node 405 * @throws SAXException 406 * @throws IOException 407 * @throws ParserConfigurationException 408 */ 409 @Converter 410 public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 411 DOMSource domSrc = toDOMSource(source); 412 return domSrc != null ? domSrc.getNode() : null; 413 } 414 415 /** 416 * Create a DOM element from the given source. 417 */ 418 @Converter 419 public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 420 Node node = toDOMNode(source); 421 return toDOMElement(node); 422 } 423 424 /** 425 * Create a DOM element from the DOM node. 426 * Simply cast if the node is an Element, or 427 * return the root element if it is a Document. 428 */ 429 @Converter 430 public Element toDOMElement(Node node) throws TransformerException { 431 // If the node is an document, return the root element 432 if (node instanceof Document) { 433 return ((Document) node).getDocumentElement(); 434 // If the node is an element, just cast it 435 } else if (node instanceof Element) { 436 return (Element) node; 437 // Other node types are not handled 438 } else { 439 throw new TransformerException("Unable to convert DOM node to an Element"); 440 } 441 } 442 443 /** 444 * Converts the given data to a DOM document 445 * 446 * @param data is the data to be parsed 447 * @return the parsed document 448 */ 449 @Converter 450 public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException { 451 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 452 return documentBuilder.parse(new ByteArrayInputStream(data)); 453 } 454 455 /** 456 * Converts the given {@link InputStream} to a DOM document 457 * 458 * @param in is the data to be parsed 459 * @return the parsed document 460 */ 461 @Converter 462 public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { 463 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 464 return documentBuilder.parse(in); 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(Reader in) throws IOException, SAXException, ParserConfigurationException { 475 return toDOMDocument(new InputSource(in)); 476 } 477 478 /** 479 * Converts the given {@link InputSource} to a DOM document 480 * 481 * @param in is the data to be parsed 482 * @return the parsed document 483 */ 484 @Converter 485 public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException { 486 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 487 return documentBuilder.parse(in); 488 } 489 490 /** 491 * Converts the given {@link String} to a DOM document 492 * 493 * @param text is the data to be parsed 494 * @return the parsed document 495 */ 496 @Converter 497 public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException { 498 return toDOMDocument(new StringReader(text)); 499 } 500 501 /** 502 * Converts the given {@link File} to a DOM document 503 * 504 * @param file is the data to be parsed 505 * @return the parsed document 506 */ 507 @Converter 508 public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException { 509 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 510 return documentBuilder.parse(file); 511 } 512 513 514 /** 515 * Create a DOM document from the given source. 516 */ 517 @Converter 518 public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 519 Node node = toDOMNode(source); 520 return toDOMDocument(node); 521 } 522 523 /** 524 * Create a DOM document from the given Node. 525 * If the node is an document, just cast it, 526 * if the node is an root element, retrieve its 527 * owner element or create a new document and import 528 * the node. 529 */ 530 @Converter 531 public Document toDOMDocument(Node node) throws ParserConfigurationException, TransformerException { 532 // If the node is the document, just cast it 533 if (node instanceof Document) { 534 return (Document) node; 535 // If the node is an element 536 } else if (node instanceof Element) { 537 Element elem = (Element) node; 538 // If this is the root element, return its owner document 539 if (elem.getOwnerDocument().getDocumentElement() == elem) { 540 return elem.getOwnerDocument(); 541 // else, create a new doc and copy the element inside it 542 } else { 543 Document doc = createDocument(); 544 doc.appendChild(doc.importNode(node, true)); 545 return doc; 546 } 547 // other element types are not handled 548 } else { 549 throw new TransformerException("Unable to convert DOM node to a Document"); 550 } 551 } 552 553 // Properties 554 //------------------------------------------------------------------------- 555 public DocumentBuilderFactory getDocumentBuilderFactory() { 556 if (documentBuilderFactory == null) { 557 documentBuilderFactory = createDocumentBuilderFactory(); 558 } 559 return documentBuilderFactory; 560 } 561 562 public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) { 563 this.documentBuilderFactory = documentBuilderFactory; 564 } 565 566 567 // Helper methods 568 //------------------------------------------------------------------------- 569 public DocumentBuilderFactory createDocumentBuilderFactory() { 570 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 571 factory.setNamespaceAware(true); 572 factory.setIgnoringElementContentWhitespace(true); 573 factory.setIgnoringComments(true); 574 return factory; 575 } 576 577 578 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 579 DocumentBuilderFactory factory = getDocumentBuilderFactory(); 580 return factory.newDocumentBuilder(); 581 } 582 583 public Document createDocument() throws ParserConfigurationException { 584 DocumentBuilder builder = createDocumentBuilder(); 585 return builder.newDocument(); 586 } 587 588 public TransformerFactory getTransformerFactory() { 589 if (transformerFactory == null) { 590 transformerFactory = createTransformerFactory(); 591 } 592 return transformerFactory; 593 } 594 595 public void setTransformerFactory(TransformerFactory transformerFactory) { 596 this.transformerFactory = transformerFactory; 597 } 598 599 public Transformer createTransfomer() throws TransformerConfigurationException { 600 TransformerFactory factory = getTransformerFactory(); 601 return factory.newTransformer(); 602 } 603 604 public TransformerFactory createTransformerFactory() { 605 TransformerFactory answer = TransformerFactory.newInstance(); 606 return answer; 607 } 608 609 }