View Javadoc

1   /*
2    * $Id: DataTypeFactory.java 20773 2010-12-16 07:05:53Z 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  package org.mule.transformer.types;
11  
12  import org.mule.api.MuleMessage;
13  import org.mule.api.config.MuleProperties;
14  import org.mule.api.transformer.DataType;
15  import org.mule.util.generics.GenericsUtils;
16  import org.mule.util.generics.MethodParameter;
17  
18  import java.io.InputStream;
19  import java.lang.ref.WeakReference;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.Proxy;
23  import java.util.Collection;
24  
25  import javax.activation.DataHandler;
26  import javax.activation.DataSource;
27  
28  import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
29  
30  /**
31   * Factory class used to create {@link DataType} objects based on the parameter types passed into
32   * the factory methods.
33   *
34   * @since 3.0
35   */
36  public class DataTypeFactory
37  {
38      public static final DataType<String> TEXT_STRING = new SimpleDataType<String>(String.class, MimeTypes.TEXT);
39      public static final DataType<String> XML_STRING = new SimpleDataType<String>(String.class, MimeTypes.XML);
40      public static final DataType<String> JSON_STRING = new SimpleDataType<String>(String.class, MimeTypes.JSON);
41      public static final DataType<String> HTML_STRING = new SimpleDataType<String>(String.class, MimeTypes.HTML);
42      public static final DataType<String> ATOM_STRING = new SimpleDataType<String>(String.class, MimeTypes.ATOM);
43      public static final DataType<String> RSS_STRING = new SimpleDataType<String>(String.class, MimeTypes.RSS);
44  
45      //Common Java types
46      public static final DataType<String> STRING = new SimpleDataType<String>(String.class);
47      public static final DataType<String> OBJECT = new SimpleDataType<String>(Object.class);
48      public static final DataType<String> BYTE_ARRAY = new SimpleDataType<String>(byte[].class);
49      public static final DataType<String> INPUT_STREAM = new SimpleDataType<String>(InputStream.class);
50      public static final DataType<String> MULE_MESSAGE = new SimpleDataType<String>(MuleMessage.class);
51  
52      public static <T> DataType<T> create(Class<T> type)
53      {
54          return create(type, MimeTypes.ANY);
55      }
56  
57      public static <T> DataType<T> createImmutable(Class<T> type)
58      {
59          return new ImmutableDataType<T>(create(type, MimeTypes.ANY));
60      }
61  
62      public static <T> DataType<T> createWithEncoding(Class<T> type, String encoding)
63      {
64          DataType<T> dataType = create(type);
65          dataType.setEncoding(encoding);
66          return dataType;
67      }
68  
69      public static <T> DataType<T> create(Class<T> type, String mimeType)
70      {
71          if (Collection.class.isAssignableFrom(type))
72          {
73              Class<? extends Collection<?>> collectionType = (Class<? extends Collection<?>>)type;
74              Class<?> itemType = GenericsUtils.getCollectionType(collectionType);
75              if (itemType == null)
76              {
77                  return new CollectionDataType(collectionType, mimeType);
78              }
79              else
80              {
81                  return new CollectionDataType(collectionType, itemType, mimeType);
82              }
83          }
84  
85          // Special case where proxies are used for testing
86          if (isProxyClass(type))
87          {
88              return new SimpleDataType<T>(type.getInterfaces()[0], mimeType);
89          }
90  
91          return new SimpleDataType<T>(type, mimeType);
92      }
93  
94      public static <T> DataType create(Class<? extends Collection> collClass, Class<T> itemType)
95      {
96          return create(collClass, itemType, null);
97      }
98  
99      public static <T> DataType create(Class<? extends Collection> collClass, Class<T> itemType, String mimeType)
100     {
101         return new CollectionDataType(collClass, itemType, mimeType);
102     }
103 
104     /**
105      * Will create a {@link org.mule.api.transformer.DataType} object from an object instance. This method will check
106      * if the object o is a {@link org.mule.api.MuleMessage} instance and will take the type from the message payload
107      * and check if a mime type is set on the message and used that when constructing the {@link org.mule.api.transformer.DataType}
108      * object.
109      *
110      * @param o an object instance.  This can be a {@link org.mule.api.MuleMessage}, a collection, a proxy instance or any other
111      *          object
112      * @return a data type that represents the object type.
113      */
114     public static DataType<?> createFromObject(Object o)
115     {
116         Class<?> type = o.getClass();
117         String mime = null;
118         if (o instanceof DataType)
119         {
120             return (DataType<?>)o;
121         }
122         else if (o instanceof MuleMessage)
123         {
124             MuleMessage mm = (MuleMessage) o;
125             type = mm.getPayload().getClass();
126             //TODO better mime handling, see MULE-4639
127             //case insensitive
128             mime = mm.getInboundProperty(MuleProperties.CONTENT_TYPE_PROPERTY);
129             if (mime == null)
130             {
131                 //case insensitive
132                 mime = mm.getInboundProperty("ContentType");
133             }
134         }
135         else if (o instanceof DataHandler)
136         {
137             mime = ((DataHandler) o).getContentType();
138         }
139         else if (o instanceof DataSource)
140         {
141             mime = ((DataSource) o).getContentType();
142         }
143 
144         if (mime != null)
145         {
146             int i = mime.indexOf(";");
147             mime = (i > -1 ? mime.substring(0, i) : mime);
148             //TODO set the charset on the DataType when the field is introduced BL-140
149         }
150         else
151         {
152             mime = MimeTypes.ANY;
153         }
154 
155         return create(type, mime);
156     }
157 
158     public static DataType<?> createFromReturnType(Method m)
159     {
160         return createFromReturnType(m, null);
161     }
162 
163     public static DataType<?> createFromReturnType(Method m, String mimeType)
164     {
165         if (Collection.class.isAssignableFrom(m.getReturnType()))
166         {
167             Class<? extends Collection> cType = (Class<? extends Collection>) m.getReturnType();
168             Class itemType = GenericsUtils.getCollectionReturnType(m);
169 
170             if (itemType != null)
171             {
172                 return new CollectionDataType(cType, itemType, mimeType);
173             }
174             else
175             {
176                 return new CollectionDataType(cType, mimeType);
177             }
178         }
179         else
180         {
181             return new SimpleDataType(m.getReturnType(), mimeType);
182         }
183     }
184 
185     public static DataType createFromParameterType(Method m, int paramIndex)
186     {
187         return createFromParameterType(m, paramIndex, null);
188     }
189 
190     public static DataType createFromParameterType(Method m, int paramIndex, String mimeType)
191     {
192         if (Collection.class.isAssignableFrom(m.getParameterTypes()[paramIndex]))
193         {
194             Class<? extends Collection> cType = (Class<? extends Collection>) m.getParameterTypes()[paramIndex];
195             Class itemType = GenericsUtils.getCollectionParameterType(new MethodParameter(m, paramIndex));
196 
197             if (itemType != null)
198             {
199                 return new CollectionDataType(cType, itemType, mimeType);
200             }
201             else
202             {
203                 return new CollectionDataType(cType, mimeType);
204             }
205         }
206         else
207         {
208             return new SimpleDataType(m.getParameterTypes()[paramIndex], mimeType);
209         }
210     }
211 
212     public static DataType<?> createFromField(Field f)
213     {
214         return createFromField(f, null);
215     }
216 
217     public static DataType<?> createFromField(Field f, String mimeType)
218     {
219         if (Collection.class.isAssignableFrom(f.getType()))
220         {
221             Class<? extends Collection> cType = (Class<? extends Collection>) f.getType();
222             Class itemType = GenericsUtils.getCollectionFieldType(f);
223 
224             if (itemType != null)
225             {
226                 return new CollectionDataType(cType, itemType, mimeType);
227             }
228             else
229             {
230                 return new CollectionDataType(cType, mimeType);
231             }
232         }
233         else
234         {
235             return new SimpleDataType(f.getType(), mimeType);
236         }
237     }
238 
239 
240     private static ConcurrentHashMap proxyClassCache = new ConcurrentHashMap();
241     /**
242      * Cache which classes are proxies.  Very experimental
243      */
244     protected static<T> boolean isProxyClass(Class<T> type)
245     {
246         /**
247          * map value
248          */
249         class ProxyIndicator
250         {
251             private final WeakReference<Class> targetClassRef;
252             private final boolean isProxy;
253 
254             ProxyIndicator(Class targetClass, boolean proxy)
255             {
256                 this.targetClassRef = new WeakReference<Class>(targetClass);
257                 isProxy = proxy;
258             }
259 
260             public Class getTargetClass()
261             {
262                 return targetClassRef.get();
263             }
264 
265             public boolean isProxy()
266             {
267                 return isProxy;
268             }
269         }
270 
271         String typeName = type.getName();
272         ProxyIndicator indicator = (ProxyIndicator) proxyClassCache.get(typeName);
273         if (indicator != null)
274         {
275             Class classInMap = indicator.getTargetClass();
276             if (classInMap == type)
277             {
278                 return indicator.isProxy();
279             }
280             else if (classInMap != null)
281             {
282                 // We have duplicate class names from different active classloaders.  Skip the optimization for this one
283                 return Proxy.isProxyClass(type);
284             }
285         }
286         // Either there's no indicator in the map or there's one that is due to be replaced
287         boolean isProxy = Proxy.isProxyClass(type);
288         proxyClassCache.put(typeName, new ProxyIndicator(type, isProxy));
289         return isProxy;
290     }
291 }