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