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