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