View Javadoc

1   /*
2    * $Id: AbstractXmlTransformer.java 12370 2008-07-17 13:11:17Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.xml.transformer;
12  
13  import org.mule.api.transformer.TransformerException;
14  import org.mule.module.xml.util.XMLUtils;
15  import org.mule.transformer.AbstractTransformer;
16  
17  import java.io.InputStream;
18  import java.io.OutputStream;
19  import java.io.StringWriter;
20  
21  import javax.xml.stream.XMLInputFactory;
22  import javax.xml.stream.XMLOutputFactory;
23  import javax.xml.transform.OutputKeys;
24  import javax.xml.transform.Result;
25  import javax.xml.transform.Source;
26  import javax.xml.transform.Transformer;
27  import javax.xml.transform.TransformerFactory;
28  import javax.xml.transform.TransformerFactoryConfigurationError;
29  import javax.xml.transform.dom.DOMResult;
30  import javax.xml.transform.stream.StreamResult;
31  
32  import org.apache.commons.io.output.ByteArrayOutputStream;
33  import org.dom4j.Document;
34  import org.dom4j.io.DocumentResult;
35  
36  /**
37   * <code>AbstractXmlTransformer</code> offers some XSLT transform on a DOM (or
38   * other XML-ish) object.
39   */
40  public abstract class AbstractXmlTransformer extends AbstractTransformer
41  {
42      private String outputEncoding;
43      private XMLInputFactory xmlInputFactory;
44      private XMLOutputFactory xmlOutputFactory;
45      private boolean useStaxSource = false;
46      
47      public AbstractXmlTransformer()
48      {
49          registerSourceType(String.class);
50          registerSourceType(byte[].class);
51          registerSourceType(javax.xml.transform.Source.class);
52          registerSourceType(org.xml.sax.InputSource.class);
53          registerSourceType(org.dom4j.Document.class);
54          registerSourceType(org.w3c.dom.Document.class);
55          registerSourceType(org.w3c.dom.Element.class);
56          registerSourceType(java.io.InputStream.class);
57          registerSourceType(org.mule.api.transport.OutputHandler.class);
58          registerSourceType(javax.xml.stream.XMLStreamReader.class);
59          registerSourceType(org.mule.module.xml.transformer.DelayedResult.class);
60          setReturnClass(byte[].class);
61          
62          xmlInputFactory = XMLInputFactory.newInstance();
63          xmlOutputFactory = XMLOutputFactory.newInstance();
64      }
65  
66      /** Result callback interface used when processing XML through JAXP */
67      protected static interface ResultHolder
68      {
69  
70          /**
71           * @return A Result to use in a transformation (e.g. writing a DOM to a
72           *         stream)
73           */
74          Result getResult();
75  
76          /** @return The actual result as produced after the call to 'transform'. */
77          Object getResultObject();
78      }
79  
80      /**
81       * @param desiredClass Java class representing the desired format
82       * @return Callback interface representing the desiredClass - or null if the
83       *         return class isn't supported (or is null).
84       */
85      protected static ResultHolder getResultHolder(Class desiredClass)
86      {
87          if (desiredClass == null)
88          {
89              return null;
90          }
91          if (byte[].class.equals(desiredClass) || InputStream.class.isAssignableFrom(desiredClass))
92          {
93              return new ResultHolder()
94              {
95                  ByteArrayOutputStream resultStream = new ByteArrayOutputStream();
96                  StreamResult result = new StreamResult(resultStream);
97  
98                  public Result getResult()
99                  {
100                     return result;
101                 }
102 
103                 public Object getResultObject()
104                 {
105                     return resultStream.toByteArray();
106                 }
107             };
108         }
109         else if (String.class.equals(desiredClass))
110         {
111             return new ResultHolder()
112             {
113                 StringWriter writer = new StringWriter();
114                 StreamResult result = new StreamResult(writer);
115 
116                 public Result getResult()
117                 {
118                     return result;
119                 }
120 
121                 public Object getResultObject()
122                 {
123                     return writer.getBuffer().toString();
124                 }
125             };
126         }
127         else if (org.w3c.dom.Document.class.isAssignableFrom(desiredClass))
128         {
129             return new ResultHolder()
130             {
131                 DOMResult result = new DOMResult();
132 
133                 public Result getResult()
134                 {
135                     return result;
136                 }
137 
138                 public Object getResultObject()
139                 {
140                     return result.getNode();
141                 }
142             };
143         }
144         else if (org.dom4j.io.DocumentResult.class.isAssignableFrom(desiredClass))
145         {
146             return new ResultHolder()
147             {
148                 DocumentResult result = new DocumentResult();
149 
150                 public Result getResult()
151                 {
152                     return result;
153                 }
154 
155                 public Object getResultObject()
156                 {
157                     return result;
158                 }
159             };
160         }
161         else if (org.dom4j.Document.class.isAssignableFrom(desiredClass))
162         {
163             return new ResultHolder()
164             {
165                 DocumentResult result = new DocumentResult();
166 
167                 public Result getResult()
168                 {
169                     return result;
170                 }
171 
172                 public Object getResultObject()
173                 {
174                     return result.getDocument();
175                 }
176             };
177         } 
178         
179         return null;
180     }
181 
182     /**
183      * Converts an XML in-memory representation to a String
184      *
185      * @param obj Object to convert (could be byte[], String, DOM, DOM4J)
186      * @return String including XML header using default (UTF-8) encoding
187      * @throws TransformerFactoryConfigurationError
188      *          On error
189      * @throws javax.xml.transform.TransformerException
190      *          On error
191      * @throws TransformerException 
192      * @deprecated Replaced by convertToText(Object obj, String ouputEncoding)
193      */
194     protected String convertToText(Object obj)
195             throws TransformerFactoryConfigurationError, javax.xml.transform.TransformerException, TransformerException
196     {
197         return convertToText(obj, null);
198     }
199 
200     /**
201      * Converts an XML in-memory representation to a String using a specific encoding.
202      * If using an encoding which cannot represent specific characters, these are
203      * written as entities, even if they can be represented as a Java String.
204      *
205      * @param obj            Object to convert (could be byte[], String, DOM, or DOM4J Document).
206      *                       If the object is a byte[], the character
207      *                       encoding used MUST match the declared encoding standard, or a parse error will occur.
208      * @param outputEncoding Name of the XML encoding to use, e.g. US-ASCII, or null for UTF-8
209      * @return String including XML header using the specified encoding
210      * @throws TransformerFactoryConfigurationError
211      *          On error
212      * @throws javax.xml.transform.TransformerException
213      *          On error
214      * @throws TransformerException 
215      */
216     protected String convertToText(Object obj, String outputEncoding)
217             throws TransformerFactoryConfigurationError, javax.xml.transform.TransformerException, TransformerException
218     {
219         // Catch the direct translations
220         if (obj instanceof String)
221         {
222             return (String) obj;
223         }
224         else if (obj instanceof Document)
225         {
226             return ((Document) obj).asXML();
227         }
228         // No easy fix, so use the transformer.
229         Source src;
230         try
231         {
232             src = XMLUtils.toXmlSource(xmlInputFactory, useStaxSource, obj);
233             if (src == null)
234             {
235                 return null;
236             }
237         }
238         catch (Exception e)
239         {
240             if (e instanceof TransformerException)
241             {
242                 throw (TransformerException) e;
243             }
244             
245             throw new TransformerException(this, e);
246         }
247 
248         StringWriter writer = new StringWriter();
249         StreamResult result = new StreamResult(writer);
250 
251         Transformer idTransformer = TransformerFactory.newInstance().newTransformer();
252         if (outputEncoding != null)
253         {
254             idTransformer.setOutputProperty(OutputKeys.ENCODING, outputEncoding);
255         }
256         idTransformer.transform(src, result);
257         return writer.getBuffer().toString();
258     }
259 
260     /**
261      * Converts an XML in-memory representation to a String using a specific encoding.
262      *
263      * @param obj            Object to convert (could be byte[], String, DOM, or DOM4J Document).
264      *                       If the object is a byte[], the character
265      *                       encoding used MUST match the declared encoding standard, or a parse error will occur.
266      * @param outputEncoding Name of the XML encoding to use, e.g. US-ASCII, or null for UTF-8
267      * @return String including XML header using the specified encoding
268      * @throws TransformerFactoryConfigurationError
269      *          On error
270      * @throws javax.xml.transform.TransformerException
271      *          On error
272      * @throws TransformerException 
273      */
274     protected String convertToBytes(Object obj, String outputEncoding)
275             throws TransformerFactoryConfigurationError, javax.xml.transform.TransformerException, TransformerException
276     {
277         // Always use the transformer, even for byte[] (to get the encoding right!)
278         Source src;
279         try
280         {
281             src = XMLUtils.toXmlSource(xmlInputFactory, useStaxSource, obj);
282             if (src == null)
283             {
284                 return null;
285             }
286         }
287         catch (Exception e)
288         {
289             if (e instanceof TransformerException)
290             {
291                 throw (TransformerException) e;
292             }
293             
294             throw new TransformerException(this, e);
295         }
296 
297         StringWriter writer = new StringWriter();
298         StreamResult result = new StreamResult(writer);
299 
300         Transformer idTransformer = XMLUtils.getTransformer();
301         idTransformer.setOutputProperty(OutputKeys.ENCODING, outputEncoding);
302         idTransformer.transform(src, result);
303         return writer.getBuffer().toString();
304     }
305     
306     protected void writeToStream(Object obj, String outputEncoding, OutputStream output)
307         throws TransformerFactoryConfigurationError, javax.xml.transform.TransformerException,
308         TransformerException
309     {
310         // Always use the transformer, even for byte[] (to get the encoding right!)
311         Source src;
312         try
313         {
314             src = XMLUtils.toXmlSource(xmlInputFactory, useStaxSource, obj);
315             if (src == null)
316             {
317                 return;
318             }
319         }
320         catch (Exception e)
321         {
322             if (e instanceof TransformerException)
323             {
324                 throw (TransformerException) e;
325             }
326             
327             throw new TransformerException(this, e);
328         }
329 
330         StreamResult result = new StreamResult(output);
331 
332         Transformer idTransformer = XMLUtils.getTransformer();
333         idTransformer.setOutputProperty(OutputKeys.ENCODING, outputEncoding);
334         idTransformer.transform(src, result);
335     }
336     
337     /** @return the outputEncoding */
338     public String getOutputEncoding()
339     {
340         return outputEncoding;
341     }
342 
343     /** @param outputEncoding the outputEncoding to set */
344     public void setOutputEncoding(String outputEncoding)
345     {
346         this.outputEncoding = outputEncoding;
347     }
348     
349     public boolean isUseStaxSource()
350     {
351         return useStaxSource;
352     }
353 
354     public void setUseStaxSource(boolean useStaxSource)
355     {
356         this.useStaxSource = useStaxSource;
357     }
358 
359     public XMLInputFactory getXMLInputFactory()
360     {
361         return xmlInputFactory;
362     }
363 
364     public void setXMLInputFactory(XMLInputFactory xmlInputFactory)
365     {
366         this.xmlInputFactory = xmlInputFactory;
367     }
368 
369     public XMLOutputFactory getXMLOutputFactory()
370     {
371         return xmlOutputFactory;
372     }
373 
374     public void setXMLOutputFactory(XMLOutputFactory xmlOutputFactory)
375     {
376         this.xmlOutputFactory = xmlOutputFactory;
377     }
378     
379 }