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