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