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    import java.util.Properties;
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    
051    import org.xml.sax.InputSource;
052    import org.xml.sax.SAXException;
053    import org.xml.sax.XMLReader;
054    
055    import org.apache.camel.Converter;
056    import org.apache.camel.Exchange;
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: 752464 $
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            Class cl = null;
082            try {
083                cl = ObjectHelper.loadClass("org.apache.xalan.xsltc.trax.DOM2SAX");
084            } catch (Exception e) {
085                // ignore
086            }
087            DOM_TO_SAX_CLASS = cl;
088        }
089    
090    
091        public XmlConverter() {
092        }
093    
094        public XmlConverter(DocumentBuilderFactory documentBuilderFactory) {
095            this.documentBuilderFactory = documentBuilderFactory;
096        }
097    
098        /**
099         * Returns the default set of output properties for conversions.
100         */
101        public Properties defaultOutputProperties() {
102            Properties properties = new Properties();
103            properties.put(OutputKeys.ENCODING, defaultCharset);
104            properties.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
105            return properties;
106        }
107    
108        /**
109         * Converts the given input Source into the required result
110         */
111        public void toResult(Source source, Result result) throws TransformerException {
112            toResult(source, result, defaultOutputProperties());
113        }   
114        
115        /**
116         * Converts the given input Source into the required result
117         */
118        public void toResult(Source source, Result result, Properties outputProperties) throws TransformerException {
119            if (source == null) {
120                return;
121            }
122            
123            Transformer transformer = createTransfomer();
124            if (transformer == null) {
125                throw new TransformerException("Could not create a transformer - JAXP is misconfigured!");
126            }
127            transformer.setOutputProperties(outputProperties);
128            transformer.transform(source, result);
129        } 
130        
131        /**
132         * Converts the given byte[] to a Source
133         */
134        @Converter
135        public BytesSource toSource(byte[] data) {
136            return new BytesSource(data);
137        }
138    
139    
140        /**
141         * Converts the given String to a Source
142         */
143        @Converter
144        public StringSource toSource(String data) {
145            return new StringSource(data);
146        }
147    
148        /**
149         * Converts the given Document to a Source
150         */
151        @Converter
152        public DOMSource toSource(Document document) {
153            return new DOMSource(document);
154        }
155    
156        /**
157         * Converts the given Node to a Source
158         */
159        @Converter
160        public Source toSource(Node node) {
161            return new DOMSource(node);
162        }
163    
164        /**
165         * Converts the given input Source into text
166         */
167        @Converter
168        public String toString(Source source) throws TransformerException {
169            if (source == null) {
170                return null;
171            } else if (source instanceof StringSource) {
172                return ((StringSource) source).getText();
173            } else if (source instanceof BytesSource) {
174                return new String(((BytesSource) source).getData());
175            } else {
176                StringWriter buffer = new StringWriter();
177                toResult(source, new StreamResult(buffer));
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, Exchange exchange) throws TransformerException {
295            if (in != null) {
296                InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, in);
297                return new StreamSource(is);
298            }
299            return null;
300        }
301    
302        @Converter
303        public StreamSource toStreamSource(ByteBuffer in, Exchange exchange) throws TransformerException {
304            if (in != null) {
305                InputStream is = exchange.getContext().getTypeConverter().convertTo(InputStream.class, in);
306                return new StreamSource(is);
307            }
308            return null;
309        }
310    
311        @Converter
312        public StreamSource toStreamSourceFromSAX(SAXSource source) throws TransformerException {
313            InputSource inputSource = source.getInputSource();
314            if (inputSource != null) {
315                if (inputSource.getCharacterStream() != null) {
316                    return new StreamSource(inputSource.getCharacterStream());
317                }
318                if (inputSource.getByteStream() != null) {
319                    return new StreamSource(inputSource.getByteStream());
320                }
321            }
322            String result = toString(source);
323            return new StringSource(result);
324        }
325    
326        @Converter
327        public StreamSource toStreamSourceFromDOM(DOMSource source) throws TransformerException {
328            String result = toString(source);
329            return new StringSource(result);
330        }
331    
332        @Converter
333        public SAXSource toSAXSourceFromStream(StreamSource source) {
334            InputSource inputSource;
335            if (source.getReader() != null) {
336                inputSource = new InputSource(source.getReader());
337            } else {
338                inputSource = new InputSource(source.getInputStream());
339            }
340            inputSource.setSystemId(source.getSystemId());
341            inputSource.setPublicId(source.getPublicId());
342            return new SAXSource(inputSource);
343        }
344    
345        @Converter
346        public Reader toReaderFromSource(Source src) throws TransformerException {
347            StreamSource stSrc = toStreamSource(src);
348            Reader r = stSrc.getReader();
349            if (r == null) {
350                r = new InputStreamReader(stSrc.getInputStream());
351            }
352            return r;
353        }
354    
355        @Converter
356        public DOMSource toDOMSourceFromStream(StreamSource source) throws ParserConfigurationException, IOException, SAXException {
357            DocumentBuilder builder = createDocumentBuilder();
358            String systemId = source.getSystemId();
359            Document document = null;
360            Reader reader = source.getReader();
361            if (reader != null) {
362                document = builder.parse(new InputSource(reader));
363            } else {
364                InputStream inputStream = source.getInputStream();
365                if (inputStream != null) {
366                    InputSource inputsource = new InputSource(inputStream);
367                    inputsource.setSystemId(systemId);
368                    document = builder.parse(inputsource);
369                } else {
370                    throw new IOException("No input stream or reader available");
371                }
372            }
373            return new DOMSource(document, systemId);
374        }
375    
376        @Converter
377        public SAXSource toSAXSourceFromDOM(DOMSource source) throws TransformerException {
378            if (DOM_TO_SAX_CLASS != null) {
379                try {
380                    Constructor cns = DOM_TO_SAX_CLASS.getConstructor(Node.class);
381                    XMLReader converter = (XMLReader) cns.newInstance(source.getNode());
382                    return new SAXSource(converter, new InputSource());
383                } catch (Exception e) {
384                    throw new TransformerException(e);
385                }
386            } else {
387                String str = toString(source);
388                StringReader reader = new StringReader(str);
389                return new SAXSource(new InputSource(reader));
390            }
391        }
392    
393        @Converter
394        public DOMSource toDOMSourceFromSAX(SAXSource source) throws IOException, SAXException, ParserConfigurationException, TransformerException {
395            return new DOMSource(toDOMNodeFromSAX(source));
396        }
397    
398        @Converter
399        public Node toDOMNodeFromSAX(SAXSource source) throws ParserConfigurationException, IOException, SAXException, TransformerException {
400            DOMResult result = new DOMResult();
401            toResult(source, result);
402            return result.getNode();
403        }
404    
405        /**
406         * Converts the given TRaX Source into a W3C DOM node
407         */
408        @Converter
409        public Node toDOMNode(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException {
410            DOMSource domSrc = toDOMSource(source);
411            return domSrc != null ? domSrc.getNode() :  null;
412        }
413    
414        /**
415         * Create a DOM element from the given source.
416         */
417        @Converter
418        public Element toDOMElement(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException {
419            Node node = toDOMNode(source);
420            return toDOMElement(node);
421        }
422    
423        /**
424         * Create a DOM element from the DOM node.
425         * Simply cast if the node is an Element, or
426         * return the root element if it is a Document.
427         */
428        @Converter
429        public Element toDOMElement(Node node) throws TransformerException {
430            // If the node is an document, return the root element
431            if (node instanceof Document) {
432                return ((Document) node).getDocumentElement();
433            // If the node is an element, just cast it
434            } else if (node instanceof Element) {
435                return (Element) node;
436            // Other node types are not handled
437            } else {
438                throw new TransformerException("Unable to convert DOM node to an Element");
439            }
440        }
441    
442        /**
443         * Converts the given data to a DOM document
444         *
445         * @param data is the data to be parsed
446         * @return the parsed document
447         */
448        @Converter
449        public Document toDOMDocument(byte[] data) throws IOException, SAXException, ParserConfigurationException {
450            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
451            return documentBuilder.parse(new ByteArrayInputStream(data));
452        }
453    
454        /**
455         * Converts the given {@link InputStream} to a DOM document
456         *
457         * @param in is the data to be parsed
458         * @return the parsed document
459         */
460        @Converter
461        public Document toDOMDocument(InputStream in) throws IOException, SAXException, ParserConfigurationException {
462            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
463            return documentBuilder.parse(in);
464        }
465    
466        /**
467         * Converts the given {@link InputStream} to a DOM document
468         *
469         * @param in is the data to be parsed
470         * @return the parsed document
471         */
472        @Converter
473        public Document toDOMDocument(Reader in) throws IOException, SAXException, ParserConfigurationException {
474            return toDOMDocument(new InputSource(in));
475        }
476    
477        /**
478         * Converts the given {@link InputSource} to a DOM document
479         *
480         * @param in is the data to be parsed
481         * @return the parsed document
482         */
483        @Converter
484        public Document toDOMDocument(InputSource in) throws IOException, SAXException, ParserConfigurationException {
485            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
486            return documentBuilder.parse(in);
487        }
488    
489        /**
490         * Converts the given {@link String} to a DOM document
491         *
492         * @param text is the data to be parsed
493         * @return the parsed document
494         */
495        @Converter
496        public Document toDOMDocument(String text) throws IOException, SAXException, ParserConfigurationException {
497            return toDOMDocument(new StringReader(text));
498        }
499    
500        /**
501         * Converts the given {@link File} to a DOM document
502         *
503         * @param file is the data to be parsed
504         * @return the parsed document
505         */
506        @Converter
507        public Document toDOMDocument(File file) throws IOException, SAXException, ParserConfigurationException {
508            DocumentBuilder documentBuilder = getDocumentBuilderFactory().newDocumentBuilder();
509            return documentBuilder.parse(file);
510        }
511    
512        /**
513         * Create a DOM document from the given source.
514         */
515        @Converter
516        public Document toDOMDocument(Source source) throws TransformerException, ParserConfigurationException, IOException, SAXException {
517            Node node = toDOMNode(source);
518            return toDOMDocument(node);
519        }
520    
521        /**
522         * Create a DOM document from the given Node.
523         * If the node is an document, just cast it,
524         * if the node is an root element, retrieve its
525         * owner element or create a new document and import
526         * the node.
527         */
528        @Converter
529        public Document toDOMDocument(Node node) throws ParserConfigurationException, TransformerException {
530            // If the node is the document, just cast it
531            if (node instanceof Document) {
532                return (Document) node;
533            // If the node is an element
534            } else if (node instanceof Element) {
535                Element elem = (Element) node;
536                // If this is the root element, return its owner document
537                if (elem.getOwnerDocument().getDocumentElement() == elem) {
538                    return elem.getOwnerDocument();
539                // else, create a new doc and copy the element inside it
540                } else {
541                    Document doc = createDocument();
542                    doc.appendChild(doc.importNode(node, true));
543                    return doc;
544                }
545            // other element types are not handled
546            } else {
547                throw new TransformerException("Unable to convert DOM node to a Document");
548            }
549        }
550    
551        // Properties
552        //-------------------------------------------------------------------------
553        public DocumentBuilderFactory getDocumentBuilderFactory() {
554            if (documentBuilderFactory == null) {
555                documentBuilderFactory = createDocumentBuilderFactory();
556            }
557            return documentBuilderFactory;
558        }
559    
560        public void setDocumentBuilderFactory(DocumentBuilderFactory documentBuilderFactory) {
561            this.documentBuilderFactory = documentBuilderFactory;
562        }
563    
564    
565        // Helper methods
566        //-------------------------------------------------------------------------
567        public DocumentBuilderFactory createDocumentBuilderFactory() {
568            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
569            factory.setNamespaceAware(true);
570            factory.setIgnoringElementContentWhitespace(true);
571            factory.setIgnoringComments(true);
572            return factory;
573        }
574    
575    
576        public DocumentBuilder createDocumentBuilder() throws ParserConfigurationException {
577            DocumentBuilderFactory factory = getDocumentBuilderFactory();
578            return factory.newDocumentBuilder();
579        }
580    
581        public Document createDocument() throws ParserConfigurationException {
582            DocumentBuilder builder = createDocumentBuilder();
583            return builder.newDocument();
584        }
585    
586        public TransformerFactory getTransformerFactory() {
587            if (transformerFactory == null) {
588                transformerFactory = createTransformerFactory();
589            }
590            return transformerFactory;
591        }
592    
593        public void setTransformerFactory(TransformerFactory transformerFactory) {
594            this.transformerFactory = transformerFactory;
595        }
596    
597        public Transformer createTransfomer() throws TransformerConfigurationException {
598            TransformerFactory factory = getTransformerFactory();
599            return factory.newTransformer();
600        }
601    
602        public TransformerFactory createTransformerFactory() {
603            TransformerFactory answer = TransformerFactory.newInstance();
604            return answer;
605        }
606    
607    }