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