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 }