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