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