001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.camel.converter.jaxp; 019 020 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.io.ByteArrayInputStream; 028 import java.io.File; 029 import java.lang.reflect.Constructor; 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 import org.apache.camel.util.ObjectHelper; 054 import org.apache.camel.Converter; 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: 525890 $ 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 SAXSource} 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 SAXSource toSAXSource(Source source) throws IOException, SAXException, TransformerException { 188 if (source instanceof SAXSource) { 189 return (SAXSource) source; 190 } 191 else if (source instanceof DOMSource) { 192 return toSAXSourceFromDOM((DOMSource) source); 193 } 194 else if (source instanceof StreamSource) { 195 return toSAXSourceFromStream((StreamSource) source); 196 } 197 else { 198 return null; 199 } 200 } 201 202 @Converter 203 public StreamSource toStreamSource(Source source) throws TransformerException { 204 if (source instanceof StreamSource) { 205 return (StreamSource) source; 206 } else if (source instanceof DOMSource) { 207 return toStreamSourceFromDOM((DOMSource) source); 208 } else if (source instanceof SAXSource) { 209 return toStreamSourceFromSAX((SAXSource) source); 210 } else { 211 return null; 212 } 213 } 214 215 @Converter 216 public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException { 217 InputSource inputSource = source.getInputSource(); 218 if (inputSource != null) { 219 if (inputSource.getCharacterStream() != null) { 220 return new StreamSource(inputSource.getCharacterStream()); 221 } 222 if (inputSource.getByteStream() != null) { 223 return new StreamSource(inputSource.getByteStream()); 224 } 225 } 226 String result = toString(source); 227 return new StringSource(result); 228 } 229 230 @Converter 231 public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException { 232 String result = toString(source); 233 return new StringSource(result); 234 } 235 236 @Converter 237 public SAXSource toSAXSourceFromStream(StreamSource source) { 238 InputSource inputSource; 239 if (source.getReader() != null) { 240 inputSource = new InputSource(source.getReader()); 241 } else { 242 inputSource = new InputSource(source.getInputStream()); 243 } 244 inputSource.setSystemId(source.getSystemId()); 245 inputSource.setPublicId(source.getPublicId()); 246 return new SAXSource(inputSource); 247 } 248 249 @Converter 250 public Reader toReaderFromSource(Source src) throws TransformerException { 251 StreamSource stSrc = toStreamSource(src); 252 Reader r = stSrc.getReader(); 253 if (r == null) { 254 r = new InputStreamReader(stSrc.getInputStream()); 255 } 256 return r; 257 } 258 259 @Converter 260 public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException { 261 DocumentBuilder builder = createDocumentBuilder(); 262 String systemId = source.getSystemId(); 263 Document document = null; 264 Reader reader = source.getReader(); 265 if (reader != null) { 266 document = builder.parse(new InputSource(reader)); 267 } else { 268 InputStream inputStream = source.getInputStream(); 269 if (inputStream != null) { 270 InputSource inputsource = new InputSource(inputStream); 271 inputsource.setSystemId(systemId); 272 document = builder.parse(inputsource); 273 } 274 else { 275 throw new IOException("No input stream or reader available"); 276 } 277 } 278 return new DOMSource(document, systemId); 279 } 280 281 @Converter 282 public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException { 283 if (dom2SaxClass != null) { 284 try { 285 Constructor cns = dom2SaxClass.getConstructor(new Class[] { Node.class }); 286 XMLReader converter = (XMLReader) cns.newInstance(new Object[] { source.getNode() }); 287 return new SAXSource(converter, new InputSource()); 288 } catch (Exception e) { 289 throw new TransformerException(e); 290 } 291 } else { 292 String str = toString(source); 293 StringReader reader = new StringReader(str); 294 return new SAXSource(new InputSource(reader)); 295 } 296 } 297 298 @Converter 299 public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException { 300 return new DOMSource(toDOMNodeFromSAX(source)); 301 } 302 303 @Converter 304 public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException { 305 DOMResult result = new DOMResult(); 306 toResult(source, result); 307 return result.getNode(); 308 } 309 310 /** 311 * Converts the given TRaX Source into a W3C DOM node 312 * @throws SAXException 313 * @throws IOException 314 * @throws ParserConfigurationException 315 */ 316 @Converter 317 public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 318 DOMSource domSrc = toDOMSource(source); 319 return domSrc != null ? domSrc.getNode() : null; 320 } 321 322 /** 323 * Create a DOM element from the given source. 324 * 325 * @param source 326 * @return 327 * @throws TransformerException 328 * @throws ParserConfigurationException 329 * @throws IOException 330 * @throws SAXException 331 */ 332 @Converter 333 public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 334 Node node = toDOMNode(source); 335 return toDOMElement(node); 336 } 337 338 /** 339 * Create a DOM element from the DOM node. 340 * Simply cast if the node is an Element, or 341 * return the root element if it is a Document. 342 * 343 * @param node 344 * @return 345 * @throws TransformerException 346 */ 347 @Converter 348 public Element toDOMElement(Node node) throws TransformerException { 349 // If the node is an document, return the root element 350 if (node instanceof Document) { 351 return ((Document) node).getDocumentElement(); 352 // If the node is an element, just cast it 353 } else if (node instanceof Element) { 354 return (Element) node; 355 // Other node types are not handled 356 } else { 357 throw new TransformerException("Unable to convert DOM node to an Element"); 358 } 359 } 360 361 /** 362 * Converts the given data to a DOM document 363 * 364 * @param data is the data to be parsed 365 * @return the parsed document 366 */ 367 @Converter 368 public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException { 369 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 370 return documentBuilder.parse(new ByteArrayInputStream(data)); 371 } 372 373 /** 374 * Converts the given {@link InputStream} to a DOM document 375 * 376 * @param in is the data to be parsed 377 * @return the parsed document 378 */ 379 @Converter 380 public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException { 381 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 382 return documentBuilder.parse(in); 383 } 384 385 /** 386 * Converts the given {@link InputSource} to a DOM document 387 * 388 * @param in is the data to be parsed 389 * @return the parsed document 390 */ 391 @Converter 392 public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException { 393 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 394 return documentBuilder.parse(in); 395 } 396 397 /** 398 * Converts the given {@link String} to a DOM document 399 * 400 * @param text is the data to be parsed 401 * @return the parsed document 402 */ 403 @Converter 404 public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException { 405 return toDOMDocument(text.getBytes()); 406 } 407 408 /** 409 * Converts the given {@link File} to a DOM document 410 * 411 * @param file is the data to be parsed 412 * @return the parsed document 413 */ 414 @Converter 415 public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException { 416 DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); 417 return documentBuilder.parse(file); 418 } 419 420 421 /** 422 * Create a DOM document from the given source. 423 * 424 * @param source 425 * @return 426 * @throws TransformerException 427 * @throws ParserConfigurationException 428 * @throws IOException 429 * @throws SAXException 430 */ 431 @Converter 432 public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException { 433 Node node = toDOMNode(source); 434 return toDOMDocument(node); 435 } 436 437 /** 438 * Create a DOM document from the given Node. 439 * If the node is an document, just cast it, 440 * if the node is an root element, retrieve its 441 * owner element or create a new document and import 442 * the node. 443 * 444 * @param node 445 * @return 446 * @throws ParserConfigurationException 447 * @throws TransformerException 448 */ 449 @Converter 450 public Document toDOMDocument(Node node) throws ParserConfigurationException, TransformerException { 451 // If the node is the document, just cast it 452 if (node instanceof Document) { 453 return (Document) node; 454 // If the node is an element 455 } else if (node instanceof Element) { 456 Element elem = (Element) node; 457 // If this is the root element, return its owner document 458 if (elem.getOwnerDocument().getDocumentElement() == elem) { 459 return elem.getOwnerDocument(); 460 // else, create a new doc and copy the element inside it 461 } else { 462 Document doc = createDocument(); 463 doc.appendChild(doc.importNode(node, true)); 464 return doc; 465 } 466 // other element types are not handled 467 } else { 468 throw new TransformerException("Unable to convert DOM node to a Document"); 469 } 470 } 471 472 // Properties 473 //------------------------------------------------------------------------- 474 public DocumentBuilderFactory getDocumentBuilderFactory() { 475 if (documentBuilderFactory == null) { 476 documentBuilderFactory = createDocumentBuilderFactory(); 477 } 478 return documentBuilderFactory; 479 } 480 481 public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) { 482 this.documentBuilderFactory = documentBuilderFactory; 483 } 484 485 486 // Helper methods 487 //------------------------------------------------------------------------- 488 public DocumentBuilderFactory createDocumentBuilderFactory() { 489 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 490 factory.setNamespaceAware(true); 491 factory.setIgnoringElementContentWhitespace(true); 492 factory.setIgnoringComments(true); 493 return factory; 494 } 495 496 497 public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException { 498 DocumentBuilderFactory factory = getDocumentBuilderFactory(); 499 return factory.newDocumentBuilder(); 500 } 501 502 public Document createDocument() throws ParserConfigurationException { 503 DocumentBuilder builder = createDocumentBuilder(); 504 return builder.newDocument(); 505 } 506 507 public TransformerFactory getTransformerFactory() { 508 if (transformerFactory == null) { 509 transformerFactory = createTransformerFactory(); 510 } 511 return transformerFactory; 512 } 513 514 public void setTransformerFactory(TransformerFactory transformerFactory) { 515 this.transformerFactory = transformerFactory; 516 } 517 518 public Transformer createTransfomer() throws TransformerConfigurationException { 519 TransformerFactory factory = getTransformerFactory(); 520 return factory.newTransformer(); 521 } 522 523 public TransformerFactory createTransformerFactory() { 524 TransformerFactory answer = TransformerFactory.newInstance(); 525 return answer; 526 } 527 528 }