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