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