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.FileInputStream;
022    import java.io.FileNotFoundException;
023    import java.io.IOException;
024    import java.io.InputStream;
025    import java.io.InputStreamReader;
026    import java.io.Reader;
027    import java.io.StringReader;
028    import java.io.StringWriter;
029    import java.lang.reflect.Constructor;
030    import java.nio.ByteBuffer;
031    import java.util.Properties;
032    
033    import javax.xml.parsers.DocumentBuilder;
034    import javax.xml.parsers.DocumentBuilderFactory;
035    import javax.xml.parsers.ParserConfigurationException;
036    import javax.xml.stream.XMLStreamException;
037    import javax.xml.stream.XMLStreamReader;
038    import javax.xml.transform.OutputKeys;
039    import javax.xml.transform.Result;
040    import javax.xml.transform.Source;
041    import javax.xml.transform.Transformer;
042    import javax.xml.transform.TransformerConfigurationException;
043    import javax.xml.transform.TransformerException;
044    import javax.xml.transform.TransformerFactory;
045    import javax.xml.transform.dom.DOMResult;
046    import javax.xml.transform.dom.DOMSource;
047    import javax.xml.transform.sax.SAXSource;
048    import javax.xml.transform.stax.StAXSource;
049    import javax.xml.transform.stream.StreamResult;
050    import javax.xml.transform.stream.StreamSource;
051    
052    import org.w3c.dom.Document;
053    import org.w3c.dom.Element;
054    import org.w3c.dom.Node;
055    import org.w3c.dom.NodeList;
056    
057    import org.xml.sax.InputSource;
058    import org.xml.sax.SAXException;
059    import org.xml.sax.XMLReader;
060    
061    import org.apache.camel.BytesSource;
062    import org.apache.camel.Converter;
063    import org.apache.camel.Exchange;
064    import org.apache.camel.StringSource;
065    import org.apache.camel.util.IOHelper;
066    import org.apache.camel.util.ObjectHelper;
067    
068    /**
069     * A helper class to transform to and from various JAXB types such as {@link Source} and {@link Document}
070     *
071     * @version 
072     */
073    @Converter
074    public class XmlConverter {
075        @Deprecated
076        //It will be removed in Camel 3.0, please use the Exchange.DEFAULT_CHARSET 
077        public static final String DEFAULT_CHARSET_PROPERTY = "org.apache.camel.default.charset";
078        
079        public static final String OUTPUT_PROPERTIES_PREFIX = "org.apache.camel.xmlconverter.output.";
080        public static String defaultCharset = ObjectHelper.getSystemProperty(Exchange.DEFAULT_CHARSET_PROPERTY, "UTF-8");
081    
082        /*
083         * When converting a DOM tree to a SAXSource, we try to use Xalan internal DOM parser if
084         * available. Else, transform the DOM tree to a String and build a SAXSource on top of it.
085         */
086        private static final Class<?> DOM_TO_SAX_CLASS;
087    
088        private DocumentBuilderFactory documentBuilderFactory;
089        private TransformerFactory transformerFactory;
090    
091        static {
092            Class<?> cl = null;
093            try {
094                // will not warn the user if the class could not be found
095                cl = ObjectHelper.loadClass("org.apache.xalan.xsltc.trax.DOM2SAX", XmlConverter.class.getClassLoader(), false);
096            } catch (Exception e) {
097                // ignore
098            }
099            DOM_TO_SAX_CLASS = cl;
100        }
101    
102    
103        public XmlConverter() {
104        }
105    
106        public XmlConverter(DocumentBuilderFactory documentBuilderFactory) {
107            this.documentBuilderFactory = documentBuilderFactory;
108        }
109    
110        /**
111         * Returns the default set of output properties for conversions.
112         */
113        public Properties defaultOutputProperties() {
114            Properties properties = new Properties();
115            properties.put(OutputKeys.ENCODING, defaultCharset);
116            properties.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
117            return properties;
118        }
119    
120        /**
121         * Converts the given input Source into the required result
122         */
123        public void toResult(Source source, Result result) throws TransformerException {
124            toResult(source, result, defaultOutputProperties());
125        }
126    
127        /**
128         * Converts the given input Source into the required result
129         */
130        public void toResult(Source source, Result result, Properties outputProperties) throws TransformerException {
131            if (source == null) {
132                return;
133            }
134    
135            Transformer transformer = createTransformer();
136            if (transformer == null) {
137                throw new TransformerException("Could not create a transformer - JAXP is misconfigured!");
138            }
139            transformer.setOutputProperties(outputProperties);
140            transformer.transform(source, result);
141        }
142    
143        /**
144         * Converts the given NodeList to a boolean
145         */
146        @Converter
147        public Boolean toBoolean(NodeList list) {
148            return list.getLength() > 0;
149        }
150    
151        /**
152         * Converts the given byte[] to a Source
153         */
154        @Converter
155        public BytesSource toBytesSource(byte[] data) {
156            return new BytesSource(data);
157        }
158    
159    
160        /**
161         * Converts the given String to a Source
162         */
163        @Converter
164        public StringSource toStringSource(String data) {
165            return new StringSource(data);
166        }
167    
168        /**
169         * Converts the given Document to a Source
170         * @deprecated use toDOMSource instead
171         */
172        @Deprecated
173        public DOMSource toSource(Document document) {
174            return new DOMSource(document);
175        }
176    
177        /**
178         * Converts the given Node to a Source
179         * @throws TransformerException 
180         * @throws ParserConfigurationException 
181         * @deprecated  use toDOMSource instead
182         */
183        @Deprecated
184        public Source toSource(Node node) throws ParserConfigurationException, TransformerException {
185            return toDOMSource(node);
186        }
187    
188        /**
189         * Converts the given Node to a Source
190         * @throws TransformerException 
191         * @throws ParserConfigurationException 
192         */
193        @Converter
194        public DOMSource toDOMSource(Node node) throws ParserConfigurationException, TransformerException {
195            Document document = toDOMDocument(node);
196            return new DOMSource(document);
197        }
198        
199        /**
200         * Converts the given Document to a DOMSource
201         */
202        @Converter
203        public DOMSource toDOMSource(Document document) {
204            return new DOMSource(document);
205        }
206    
207        /**
208         * Converts the given String to a Source
209         */
210        @Converter
211        public Source toSource(String data) {
212            return new StringSource(data);
213        }
214    
215        /**
216         * Converts the given input Source into text.
217         *
218         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
219         */
220        @Deprecated
221        public String toString(Source source) throws TransformerException {
222            return toString(source, null);
223        }
224    
225        /**
226         * Converts the given input Source into text
227         */
228        @Converter
229        public String toString(Source source, Exchange exchange) throws TransformerException {
230            if (source == null) {
231                return null;
232            } else if (source instanceof StringSource) {
233                return ((StringSource) source).getText();
234            } else if (source instanceof BytesSource) {
235                return new String(((BytesSource) source).getData());
236            } else {
237                StringWriter buffer = new StringWriter();           
238                if (exchange != null) {
239                    // check the camelContext properties first
240                    Properties properties = ObjectHelper.getCamelPropertiesWithPrefix(OUTPUT_PROPERTIES_PREFIX, exchange.getContext());
241                    if (properties.size() > 0) {
242                        toResult(source, new StreamResult(buffer), properties);
243                        return buffer.toString();
244                    }            
245                }
246                // using the old way to deal with it
247                toResult(source, new StreamResult(buffer));            
248                return buffer.toString();
249            }
250        }
251    
252        /**
253         * Converts the given input Source into bytes
254         */
255        @Converter
256        public byte[] toByteArray(Source source, Exchange exchange) throws TransformerException {
257            String answer = toString(source, exchange);
258            if (exchange != null) {
259                return exchange.getContext().getTypeConverter().convertTo(byte[].class, exchange, answer);
260            } else {
261                return answer.getBytes();
262            }
263        }
264        
265        /**
266         * Converts the given input Node into text
267         *
268         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
269         */
270        @Deprecated
271        public String toString(Node node) throws TransformerException {
272            return toString(node, null);
273        }
274        
275        /**
276         * Converts the given input Node into text
277         */
278        @Converter
279        public String toString(Node node, Exchange exchange) throws TransformerException {
280            return toString(new DOMSource(node), exchange);
281        }
282    
283        /**
284         * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not
285         * supported (making it easy to derive from this class to add new kinds of conversion).
286         */
287        @Converter
288        public DOMSource toDOMSource(Source source) throws ParserConfigurationException, IOException, SAXException, TransformerException {
289            if (source instanceof DOMSource) {
290                return (DOMSource) source;
291            } else if (source instanceof SAXSource) {
292                return toDOMSourceFromSAX((SAXSource) source);
293            } else if (source instanceof StreamSource) {
294                return toDOMSourceFromStream((StreamSource) source);
295            } else if (source instanceof StAXSource) {
296                return toDOMSourceFromStAX((StAXSource)source);
297            } else {
298                return null;
299            }
300        }
301    
302        /**
303         * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not
304         * supported (making it easy to derive from this class to add new kinds of conversion).
305         */
306        @Converter
307        public DOMSource toDOMSource(String text) throws ParserConfigurationException, IOException, SAXException, TransformerException {
308            Source source = toSource(text);
309            if (source != null) {
310                return toDOMSourceFromStream((StreamSource) source);
311            } else {
312                return null;
313            }
314        }
315    
316        /**
317         * Converts the source instance to a {@link DOMSource} or returns null if the conversion is not
318         * supported (making it easy to derive from this class to add new kinds of conversion).
319         */
320        @Converter
321        public DOMSource toDOMSource(byte[] bytes) throws IOException, SAXException, ParserConfigurationException {
322            InputStream is = new ByteArrayInputStream(bytes);
323            try {
324                return toDOMSource(is);
325            } finally {
326                IOHelper.close(is);
327            }
328        }
329    
330    
331        /**
332         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
333         * supported (making it easy to derive from this class to add new kinds of conversion).
334         *
335         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
336         */
337        @Deprecated
338        public SAXSource toSAXSource(String source) throws IOException, SAXException, TransformerException {
339            return toSAXSource(source, null);
340        }
341        
342        /**
343         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
344         * supported (making it easy to derive from this class to add new kinds of conversion).
345         */
346        @Converter
347        public SAXSource toSAXSource(String source, Exchange exchange) throws IOException, SAXException, TransformerException {
348            return toSAXSource(toSource(source), exchange);
349        }
350    
351        /**
352         * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not
353         * supported (making it easy to derive from this class to add new kinds of conversion).
354         * @throws XMLStreamException 
355         */
356        @Converter
357        public StAXSource toStAXSource(String source, Exchange exchange) throws XMLStreamException {
358            XMLStreamReader r = new StaxConverter().createXMLStreamReader(new StringReader(source));
359            return new StAXSource(r);
360        }    
361        
362        /**
363         * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not
364         * supported (making it easy to derive from this class to add new kinds of conversion).
365         * @throws XMLStreamException
366         */
367        @Converter
368        public StAXSource toStAXSource(byte[] in, Exchange exchange) throws XMLStreamException {
369            XMLStreamReader r = new StaxConverter().createXMLStreamReader(new ByteArrayInputStream(in), exchange);
370            return new StAXSource(r);
371        }
372    
373        /**
374         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
375         * supported (making it easy to derive from this class to add new kinds of conversion).
376         *
377         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
378         */
379        @Deprecated
380        public SAXSource toSAXSource(InputStream source) throws IOException, SAXException, TransformerException {
381            return toSAXSource(source, null);
382        }
383        
384        /**
385         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
386         * supported (making it easy to derive from this class to add new kinds of conversion).
387         */
388        @Converter
389        public SAXSource toSAXSource(InputStream source, Exchange exchange) throws IOException, SAXException, TransformerException {
390            return toSAXSource(toStreamSource(source), exchange);
391        }
392    
393        /**
394         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
395         * supported (making it easy to derive from this class to add new kinds of conversion).
396         */
397        @Converter
398        public SAXSource toSAXSource(byte[] in, Exchange exchange) throws IOException, SAXException, TransformerException {
399            return toSAXSource(toStreamSource(in, exchange), exchange);
400        }
401    
402        /**
403         * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not
404         * supported (making it easy to derive from this class to add new kinds of conversion).
405         * @throws XMLStreamException 
406         */
407        @Converter
408        public StAXSource toStAXSource(InputStream source, Exchange exchange) throws XMLStreamException {
409            XMLStreamReader r = new StaxConverter().createXMLStreamReader(source, exchange);
410            return new StAXSource(r);
411        }
412        
413        /**
414         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
415         * supported (making it easy to derive from this class to add new kinds of conversion).
416         */
417        @Converter
418        public SAXSource toSAXSource(File file, Exchange exchange) throws IOException, SAXException, TransformerException {
419            InputStream is = IOHelper.buffered(new FileInputStream(file));
420            return toSAXSource(is, exchange);
421        }
422    
423        /**
424         * Converts the source instance to a {@link StAXSource} or returns null if the conversion is not
425         * supported (making it easy to derive from this class to add new kinds of conversion).
426         * @throws FileNotFoundException 
427         * @throws XMLStreamException 
428         */
429        @Converter
430        public StAXSource toStAXSource(File file, Exchange exchange) throws FileNotFoundException, XMLStreamException {
431            InputStream is = IOHelper.buffered(new FileInputStream(file));
432            XMLStreamReader r = new StaxConverter().createXMLStreamReader(is, exchange);
433            return new StAXSource(r);
434        }
435    
436        /**
437         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
438         * supported (making it easy to derive from this class to add new kinds of conversion).
439         *
440         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
441         */
442        @Deprecated
443        public SAXSource toSAXSource(Source source) throws IOException, SAXException, TransformerException {
444            return toSAXSource(source, null);
445        }
446        
447        /**
448         * Converts the source instance to a {@link SAXSource} or returns null if the conversion is not
449         * supported (making it easy to derive from this class to add new kinds of conversion).
450         */
451        @Converter
452        public SAXSource toSAXSource(Source source, Exchange exchange) throws IOException, SAXException, TransformerException {
453            if (source instanceof SAXSource) {
454                return (SAXSource) source;
455            } else if (source instanceof DOMSource) {
456                return toSAXSourceFromDOM((DOMSource) source, exchange);
457            } else if (source instanceof StreamSource) {
458                return toSAXSourceFromStream((StreamSource) source);
459            } else if (source instanceof StAXSource) {
460                return toSAXSourceFromStAX((StAXSource) source, exchange);
461            } else {
462                return null;
463            }
464        }
465    
466        /**
467         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
468         */
469        @Deprecated
470        public StreamSource toStreamSource(Source source) throws TransformerException {
471            return toStreamSource(source, null);
472        }
473        
474        @Converter
475        public StreamSource toStreamSource(Source source, Exchange exchange) throws TransformerException {
476            if (source instanceof StreamSource) {
477                return (StreamSource) source;
478            } else if (source instanceof DOMSource) {
479                return toStreamSourceFromDOM((DOMSource) source, exchange);
480            } else if (source instanceof SAXSource) {
481                return toStreamSourceFromSAX((SAXSource) source, exchange);
482            } else if (source instanceof StAXSource) {
483                return toStreamSourceFromStAX((StAXSource) source, exchange);
484            } else {
485                return null;
486            }
487        }
488    
489        @Converter
490        public StreamSource toStreamSource(InputStream in) throws TransformerException {
491            if (in != null) {
492                return new StreamSource(in);
493            }
494            return null;
495        }
496    
497        @Converter
498        public StreamSource toStreamSource(Reader in) throws TransformerException {
499            if (in != null) {
500                return new StreamSource(in);
501            }
502            return null;
503        }
504    
505        @Converter
506        public StreamSource toStreamSource(File in) throws TransformerException {
507            if (in != null) {
508                return new StreamSource(in);
509            }
510            return null;
511        }
512    
513        @Converter
514        public StreamSource toStreamSource(byte[] in, Exchange exchange) throws TransformerException {
515            if (in != null) {
516                InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, in);
517                return new StreamSource(is);
518            }
519            return null;
520        }
521    
522        @Converter
523        public StreamSource toStreamSource(ByteBuffer in, Exchange exchange) throws TransformerException {
524            if (in != null) {
525                InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, exchange, in);
526                return new StreamSource(is);
527            }
528            return null;
529        }
530    
531        /**
532         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
533         */
534        @Deprecated
535        public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException {
536            return toStreamSourceFromSAX(source, null);
537        }
538        
539        @Converter
540        public StreamSource toStreamSourceFromSAX(SAXSource source, Exchange exchange) throws TransformerException {
541            InputSource inputSource = source.getInputSource();
542            if (inputSource != null) {
543                if (inputSource.getCharacterStream() != null) {
544                    return new StreamSource(inputSource.getCharacterStream());
545                }
546                if (inputSource.getByteStream() != null) {
547                    return new StreamSource(inputSource.getByteStream());
548                }
549            }
550            String result = toString(source, exchange);
551            return new StringSource(result);
552        }
553    
554        /**
555         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
556         */
557        @Deprecated
558        public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException {
559            return toStreamSourceFromDOM(source, null);
560        }
561        
562        @Converter
563        public StreamSource toStreamSourceFromDOM(DOMSource source, Exchange exchange) throws TransformerException {
564            String result = toString(source, exchange);
565            return new StringSource(result);
566        }
567        @Converter
568        public StreamSource toStreamSourceFromStAX(StAXSource source, Exchange exchange) throws TransformerException {
569            String result = toString(source, exchange);
570            return new StringSource(result);
571        }
572    
573        @Converter
574        public SAXSource toSAXSourceFromStream(StreamSource source) {
575            InputSource inputSource;
576            if (source.getReader() != null) {
577                inputSource = new InputSource(source.getReader());
578            } else {
579                inputSource = new InputSource(source.getInputStream());
580            }
581            inputSource.setSystemId(source.getSystemId());
582            inputSource.setPublicId(source.getPublicId());
583            return new SAXSource(inputSource);
584        }
585    
586        /**
587         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
588         */
589        @Deprecated
590        public Reader toReaderFromSource(Source src) throws TransformerException {
591            return toReaderFromSource(src, null);
592        }
593        
594        @Converter
595        public Reader toReaderFromSource(Source src, Exchange exchange) throws TransformerException {
596            StreamSource stSrc = toStreamSource(src, exchange);
597            Reader r = stSrc.getReader();
598            if (r == null) {
599                r = new InputStreamReader(stSrc.getInputStream());
600            }
601            return r;
602        }
603    
604        @Converter
605        public DOMSource toDOMSource(InputStream is) throws ParserConfigurationException, IOException, SAXException {
606            InputSource source = new InputSource(is);
607            String systemId = source.getSystemId();
608            DocumentBuilder builder = createDocumentBuilder();
609            Document document = builder.parse(source);
610            return new DOMSource(document, systemId);
611        }
612    
613        @Converter
614        public DOMSource toDOMSource(File file) throws ParserConfigurationException, IOException, SAXException {
615            InputStream is = IOHelper.buffered(new FileInputStream(file));
616            return toDOMSource(is);
617        }
618    
619        @Converter
620        public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException {
621            Document document;
622            String systemId = source.getSystemId();
623    
624            DocumentBuilder builder = createDocumentBuilder();
625            Reader reader = source.getReader();
626            if (reader != null) {
627                document = builder.parse(new InputSource(reader));
628            } else {
629                InputStream inputStream = source.getInputStream();
630                if (inputStream != null) {
631                    InputSource inputsource = new InputSource(inputStream);
632                    inputsource.setSystemId(systemId);
633                    document = builder.parse(inputsource);
634                } else {
635                    throw new IOException("No input stream or reader available on StreamSource: " + source);
636                }
637            }
638            return new DOMSource(document, systemId);
639        }
640        
641        /**
642         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
643         */
644        @Deprecated
645        public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException {
646            return toSAXSourceFromDOM(source, null);
647        }
648        
649        @Converter
650        public SAXSource toSAXSourceFromDOM(DOMSource source, Exchange exchange) throws TransformerException {
651            if (DOM_TO_SAX_CLASS != null) {
652                try {
653                    Constructor<?> cns = DOM_TO_SAX_CLASS.getConstructor(Node.class);
654                    XMLReader converter = (XMLReader) cns.newInstance(source.getNode());
655                    return new SAXSource(converter, new InputSource());
656                } catch (Exception e) {
657                    throw new TransformerException(e);
658                }
659            } else {
660                String str = toString(source, exchange);
661                StringReader reader = new StringReader(str);
662                return new SAXSource(new InputSource(reader));
663            }
664        }
665    
666        @Converter
667        public SAXSource toSAXSourceFromStAX(StAXSource source, Exchange exchange) throws TransformerException {
668            String str = toString(source, exchange);
669            StringReader reader = new StringReader(str);
670            return new SAXSource(new InputSource(reader));
671        }
672    
673        @Converter
674        public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException {
675            return new DOMSource(toDOMNodeFromSAX(source));
676        }
677    
678        @Converter
679        public DOMSource toDOMSourceFromStAX(StAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException {
680            return new DOMSource(toDOMNodeFromStAX(source));
681        }
682    
683        @Converter
684        public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException {
685            DOMResult result = new DOMResult();
686            toResult(source, result);
687            return result.getNode();
688        }
689    
690        @Converter
691        public Node toDOMNodeFromStAX(StAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException {
692            DOMResult result = new DOMResult();
693            toResult(source, result);
694            return result.getNode();
695        }
696        
697        /**
698         * Convert a NodeList consisting of just 1 node to a DOM Node.
699         * @param nl the NodeList
700         * @return the DOM Node
701         */
702        @Converter
703        public Node toDOMNodeFromSingleNodeList(NodeList nl) {
704            return nl.getLength() == 1 ? nl.item(0) : null;
705        }
706        
707        /**
708         * Convert a NodeList consisting of just 1 node to a DOM Document.
709         * Cannot convert NodeList with length > 1 because they require a root node.
710         * @param nl the NodeList
711         * @return the DOM Document
712         */
713        @Converter
714        public Document toDOMDocumentFromSingleNodeList(NodeList nl) throws ParserConfigurationException, TransformerException {
715            return nl.getLength() == 1 ? toDOMDocument(nl.item(0)) : null;
716        }
717    
718        /**
719         * Converts the given TRaX Source into a W3C DOM node
720         */
721        @Converter
722        public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException {
723            DOMSource domSrc = toDOMSource(source);
724            return domSrc != null ? domSrc.getNode() : null;
725        }
726    
727        /**
728         * Create a DOM element from the given source.
729         */
730        @Converter
731        public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException {
732            Node node = toDOMNode(source);
733            return toDOMElement(node);
734        }
735    
736        /**
737         * Create a DOM element from the DOM node.
738         * Simply cast if the node is an Element, or
739         * return the root element if it is a Document.
740         */
741        @Converter
742        public Element toDOMElement(Node node) throws TransformerException {
743            // If the node is an document, return the root element
744            if (node instanceof Document) {
745                return ((Document) node).getDocumentElement();
746                // If the node is an element, just cast it
747            } else if (node instanceof Element) {
748                return (Element) node;
749                // Other node types are not handled
750            } else {
751                throw new TransformerException("Unable to convert DOM node to an Element");
752            }
753        }
754    
755        /**
756         * Converts the given data to a DOM document
757         *
758         * @param data is the data to be parsed
759         * @return the parsed document
760         */
761        @Converter
762        public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException {
763            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
764            return documentBuilder.parse(new ByteArrayInputStream(data));
765        }
766    
767        /**
768         * Converts the given {@link InputStream} to a DOM document
769         *
770         * @param in is the data to be parsed
771         * @return the parsed document
772         */
773        @Converter
774        public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException {
775            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
776            return documentBuilder.parse(in);
777        }
778    
779        /**
780         * Converts the given {@link InputStream} to a DOM document
781         *
782         * @param in is the data to be parsed
783         * @return the parsed document
784         */
785        @Converter
786        public Document toDOMDocument(Reader in) throws IOException, SAXException, ParserConfigurationException {
787            return toDOMDocument(new InputSource(in));
788        }
789    
790        /**
791         * Converts the given {@link InputSource} to a DOM document
792         *
793         * @param in is the data to be parsed
794         * @return the parsed document
795         */
796        @Converter
797        public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException {
798            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
799            return documentBuilder.parse(in);
800        }
801    
802        /**
803         * Converts the given {@link String} to a DOM document
804         *
805         * @param text is the data to be parsed
806         * @return the parsed document
807         */
808        @Converter
809        public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException {
810            return toDOMDocument(new StringReader(text));
811        }
812    
813        /**
814         * Converts the given {@link File} to a DOM document
815         *
816         * @param file is the data to be parsed
817         * @return the parsed document
818         */
819        @Converter
820        public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException {
821            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
822            return documentBuilder.parse(file);
823        }
824    
825        /**
826         * Create a DOM document from the given source.
827         */
828        @Converter
829        public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException {
830            Node node = toDOMNode(source);
831            return toDOMDocument(node);
832        }
833    
834        /**
835         * Create a DOM document from the given Node.
836         *
837         * If the node is an document, just cast it, if the node is an root element, retrieve its
838         * owner element or create a new document and import the node.
839         */
840        @Converter
841        public Document toDOMDocument(final Node node) throws ParserConfigurationException, TransformerException {
842            ObjectHelper.notNull(node, "node");
843    
844            // If the node is the document, just cast it
845            if (node instanceof Document) {
846                return (Document) node;
847                // If the node is an element
848            } else if (node instanceof Element) {
849                Element elem = (Element) node;
850                // If this is the root element, return its owner document
851                if (elem.getOwnerDocument().getDocumentElement() == elem) {
852                    return elem.getOwnerDocument();
853                    // else, create a new doc and copy the element inside it
854                } else {
855                    Document doc = createDocument();
856                    // import node must not occur concurrent on the same node (must be its owner)
857                    // so we need to synchronize on it
858                    synchronized (node.getOwnerDocument()) {
859                        doc.appendChild(doc.importNode(node, true));
860                    }
861                    return doc;
862                }
863                // other element types are not handled
864            } else {
865                throw new TransformerException("Unable to convert DOM node to a Document: " + node);
866            }
867        }
868    
869        /**
870         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
871         */
872        @Deprecated
873        public InputStream toInputStream(DOMSource source) throws TransformerException, IOException {
874            return toInputStream(source, null);
875        }
876        
877        @Converter
878        public InputStream toInputStream(DOMSource source, Exchange exchange) throws TransformerException, IOException {
879            String s = toString(source, exchange);
880            return new ByteArrayInputStream(s.getBytes());
881        }
882    
883        /**
884         * @deprecated will be removed in Camel 3.0. Use the method which has 2 parameters.
885         */
886        @Deprecated
887        public InputStream toInputStream(Document dom) throws TransformerException, IOException {
888            return toInputStream(dom, null);
889        }
890        
891        @Converter
892        public InputStream toInputStream(Document dom, Exchange exchange) throws TransformerException, IOException {
893            String s = toString(dom, exchange);
894            return new ByteArrayInputStream(s.getBytes());
895        }
896    
897        @Converter
898        public InputSource toInputSource(InputStream is, Exchange exchange) {
899            return new InputSource(is);
900        }
901    
902        @Converter
903        public InputSource toInputSource(File file, Exchange exchange) throws FileNotFoundException {
904            InputStream is = IOHelper.buffered(new FileInputStream(file));
905            return new InputSource(is);
906        }
907    
908        // Properties
909        //-------------------------------------------------------------------------
910        public DocumentBuilderFactory getDocumentBuilderFactory() {
911            if (documentBuilderFactory == null) {
912                documentBuilderFactory = createDocumentBuilderFactory();
913            }
914            return documentBuilderFactory;
915        }
916    
917        public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) {
918            this.documentBuilderFactory = documentBuilderFactory;
919        }
920    
921    
922        // Helper methods
923        //-------------------------------------------------------------------------
924        public DocumentBuilderFactory createDocumentBuilderFactory() {
925            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
926            factory.setNamespaceAware(true);
927            factory.setIgnoringElementContentWhitespace(true);
928            factory.setIgnoringComments(true);
929            return factory;
930        }
931    
932    
933        public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
934            DocumentBuilderFactory factory = getDocumentBuilderFactory();
935            return factory.newDocumentBuilder();
936        }
937    
938        public Document createDocument() throws ParserConfigurationException {
939            DocumentBuilder builder = createDocumentBuilder();
940            return builder.newDocument();
941        }
942    
943        public TransformerFactory getTransformerFactory() {
944            if (transformerFactory == null) {
945                transformerFactory = createTransformerFactory();
946            }
947            return transformerFactory;
948        }
949    
950        public void setTransformerFactory(TransformerFactory transformerFactory) {
951            this.transformerFactory = transformerFactory;
952        }
953    
954        /**
955         * @deprecated use {@link #createTransformer}, will be removed in Camel 3.0
956         */
957        @Deprecated
958        public Transformer createTransfomer() throws TransformerConfigurationException {
959            return createTransformer();
960        }
961    
962        public Transformer createTransformer() throws TransformerConfigurationException {
963            TransformerFactory factory = getTransformerFactory();
964            return factory.newTransformer();
965        }
966    
967        public TransformerFactory createTransformerFactory() {
968            TransformerFactory factory = TransformerFactory.newInstance();
969            factory.setErrorListener(new XmlErrorListener());
970            return factory;
971        }
972    
973    }