View Javadoc

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