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