View Javadoc

1   /*
2    * $Id: AbstractXmlTransformer.java 19250 2010-08-30 16:53:14Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.AbstractMessageTransformer;
16  import org.mule.transformer.types.DataTypeFactory;
17  
18  import java.io.InputStream;
19  import java.io.OutputStream;
20  import java.io.StringWriter;
21  
22  import javax.xml.stream.XMLInputFactory;
23  import javax.xml.stream.XMLOutputFactory;
24  import javax.xml.transform.OutputKeys;
25  import javax.xml.transform.Result;
26  import javax.xml.transform.Source;
27  import javax.xml.transform.Transformer;
28  import javax.xml.transform.TransformerFactory;
29  import javax.xml.transform.TransformerFactoryConfigurationError;
30  import javax.xml.transform.dom.DOMResult;
31  import javax.xml.transform.stream.StreamResult;
32  
33  import org.apache.commons.io.output.ByteArrayOutputStream;
34  import org.dom4j.Document;
35  import org.dom4j.io.DocumentResult;
36  
37  /**
38   * <code>AbstractXmlTransformer</code> offers some XSLT transform on a DOM (or
39   * other XML-ish) object.
40   */
41  public abstract class AbstractXmlTransformer extends AbstractMessageTransformer
42  {
43      private String outputEncoding;
44      private XMLInputFactory xmlInputFactory;
45      private XMLOutputFactory xmlOutputFactory;
46      private boolean useStaxSource = false;
47      
48      public AbstractXmlTransformer()
49      {
50          registerSourceType(DataTypeFactory.STRING);
51          registerSourceType(DataTypeFactory.BYTE_ARRAY);
52          registerSourceType(DataTypeFactory.create(javax.xml.transform.Source.class));
53          registerSourceType(DataTypeFactory.create(org.xml.sax.InputSource.class));
54          registerSourceType(DataTypeFactory.create(org.dom4j.Document.class));
55          registerSourceType(DataTypeFactory.create(org.w3c.dom.Document.class));
56          registerSourceType(DataTypeFactory.create(org.w3c.dom.Element.class));
57          registerSourceType(DataTypeFactory.create(java.io.InputStream.class));
58          registerSourceType(DataTypeFactory.create(org.mule.api.transport.OutputHandler.class));
59          registerSourceType(DataTypeFactory.create(javax.xml.stream.XMLStreamReader.class));
60          registerSourceType(DataTypeFactory.create(org.mule.module.xml.transformer.DelayedResult.class));
61          setReturnDataType(DataTypeFactory.BYTE_ARRAY);
62          
63          xmlInputFactory = XMLInputFactory.newInstance();
64          xmlOutputFactory = XMLOutputFactory.newInstance();
65      }
66  
67      /** Result callback interface used when processing XML through JAXP */
68      protected static interface ResultHolder
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     @Deprecated
195     protected String convertToText(Object obj) throws Exception
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) throws Exception
217     {
218         // Catch the direct translations
219         if (obj instanceof String)
220         {
221             return (String) obj;
222         }
223         else if (obj instanceof Document)
224         {
225             return ((Document) obj).asXML();
226         }
227         // No easy fix, so use the transformer.
228         Source src = XMLUtils.toXmlSource(xmlInputFactory, useStaxSource, obj);
229         if (src == null)
230         {
231             return null;
232         }
233 
234         StringWriter writer = new StringWriter();
235         StreamResult result = new StreamResult(writer);
236 
237         Transformer idTransformer = TransformerFactory.newInstance().newTransformer();
238         if (outputEncoding != null)
239         {
240             idTransformer.setOutputProperty(OutputKeys.ENCODING, outputEncoding);
241         }
242         idTransformer.transform(src, result);
243         return writer.getBuffer().toString();
244     }
245 
246     /**
247      * Converts an XML in-memory representation to a String using a specific encoding.
248      *
249      * @param obj            Object to convert (could be byte[], String, DOM, or DOM4J Document).
250      *                       If the object is a byte[], the character
251      *                       encoding used MUST match the declared encoding standard, or a parse error will occur.
252      * @param outputEncoding Name of the XML encoding to use, e.g. US-ASCII, or null for UTF-8
253      * @return String including XML header using the specified encoding
254      * @throws TransformerFactoryConfigurationError
255      *          On error
256      * @throws javax.xml.transform.TransformerException
257      *          On error
258      * @throws TransformerException
259      */
260     protected String convertToBytes(Object obj, String outputEncoding) throws Exception
261     {
262         // Always use the transformer, even for byte[] (to get the encoding right!)
263         Source src = XMLUtils.toXmlSource(xmlInputFactory, useStaxSource, obj);
264         if (src == null)
265         {
266             return null;
267         }
268 
269         StringWriter writer = new StringWriter();
270         StreamResult result = new StreamResult(writer);
271 
272         Transformer idTransformer = XMLUtils.getTransformer();
273         idTransformer.setOutputProperty(OutputKeys.ENCODING, outputEncoding);
274         idTransformer.transform(src, result);
275         return writer.getBuffer().toString();
276     }
277     
278     protected void writeToStream(Object obj, String outputEncoding, OutputStream output) throws Exception
279     {
280         // Always use the transformer, even for byte[] (to get the encoding right!)
281         Source src = XMLUtils.toXmlSource(xmlInputFactory, useStaxSource, obj);
282         if (src == null)
283         {
284             return;
285         }
286 
287         StreamResult result = new StreamResult(output);
288 
289         Transformer idTransformer = XMLUtils.getTransformer();
290         idTransformer.setOutputProperty(OutputKeys.ENCODING, outputEncoding);
291         idTransformer.transform(src, result);
292     }
293     
294     /** @return the outputEncoding */
295     public String getOutputEncoding()
296     {
297         return outputEncoding;
298     }
299 
300     /** @param outputEncoding the outputEncoding to set */
301     public void setOutputEncoding(String outputEncoding)
302     {
303         this.outputEncoding = outputEncoding;
304     }
305     
306     public boolean isUseStaxSource()
307     {
308         return useStaxSource;
309     }
310 
311     public void setUseStaxSource(boolean useStaxSource)
312     {
313         this.useStaxSource = useStaxSource;
314     }
315 
316     public XMLInputFactory getXMLInputFactory()
317     {
318         return xmlInputFactory;
319     }
320 
321     public void setXMLInputFactory(XMLInputFactory xmlInputFactory)
322     {
323         this.xmlInputFactory = xmlInputFactory;
324     }
325 
326     public XMLOutputFactory getXMLOutputFactory()
327     {
328         return xmlOutputFactory;
329     }
330 
331     public void setXMLOutputFactory(XMLOutputFactory xmlOutputFactory)
332     {
333         this.xmlOutputFactory = xmlOutputFactory;
334     }
335     
336 }