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