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 }