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