View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.commons.util;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import javax.faces.FacesException;
25  import javax.faces.context.FacesContext;
26  import javax.el.ExpressionFactory;
27  import java.io.InputStream;
28  import java.io.IOException;
29  import java.lang.reflect.Array;
30  import java.util.*;
31  
32  
33  /**
34   * @author Manfred Geiler (latest modification by $Author: skitching $)
35   * @author Anton Koinov
36   * @version $Revision: 676268 $ $Date: 2008-07-13 02:45:56 -0500 (Sun, 13 Jul 2008) $
37   */
38  public final class ClassUtils
39  {
40      //~ Static fields/initializers -----------------------------------------------------------------
41  
42      private static final Log log                  = LogFactory.getLog(ClassUtils.class);
43  
44      public static final Class BOOLEAN_ARRAY_CLASS = boolean[].class;
45      public static final Class BYTE_ARRAY_CLASS    = byte[].class;
46      public static final Class CHAR_ARRAY_CLASS    = char[].class;
47      public static final Class SHORT_ARRAY_CLASS   = short[].class;
48      public static final Class INT_ARRAY_CLASS     = int[].class;
49      public static final Class LONG_ARRAY_CLASS    = long[].class;
50      public static final Class FLOAT_ARRAY_CLASS   = float[].class;
51      public static final Class DOUBLE_ARRAY_CLASS  = double[].class;
52      public static final Class OBJECT_ARRAY_CLASS  = Object[].class;
53      public static final Class BOOLEAN_OBJECT_ARRAY_CLASS = Boolean[].class;
54      public static final Class BYTE_OBJECT_ARRAY_CLASS = Byte[].class;
55      public static final Class CHARACTER_OBJECT_ARRAY_CLASS = Character[].class;
56      public static final Class SHORT_OBJECT_ARRAY_CLASS = Short[].class;
57      public static final Class INTEGER_OBJECT_ARRAY_CLASS = Integer[].class;
58      public static final Class LONG_OBJECT_ARRAY_CLASS = Long[].class;
59      public static final Class FLOAT_OBJECT_ARRAY_CLASS = Float[].class;
60      public static final Class DOUBLE_OBJECT_ARRAY_CLASS = Double[].class;
61      public static final Class STRING_OBJECT_ARRAY_CLASS = String[].class;
62  
63      public static final Map COMMON_TYPES = new HashMap(64);
64      static
65      {
66          COMMON_TYPES.put("byte", Byte.TYPE);
67          COMMON_TYPES.put("char", Character.TYPE);
68          COMMON_TYPES.put("double", Double.TYPE);
69          COMMON_TYPES.put("float", Float.TYPE);
70          COMMON_TYPES.put("int", Integer.TYPE);
71          COMMON_TYPES.put("long", Long.TYPE);
72          COMMON_TYPES.put("short", Short.TYPE);
73          COMMON_TYPES.put("boolean", Boolean.TYPE);
74          COMMON_TYPES.put("void", Void.TYPE);
75          COMMON_TYPES.put("java.lang.Object", Object.class);
76          COMMON_TYPES.put("java.lang.Boolean", Boolean.class);
77          COMMON_TYPES.put("java.lang.Byte", Byte.class);
78          COMMON_TYPES.put("java.lang.Character", Character.class);
79          COMMON_TYPES.put("java.lang.Short", Short.class);
80          COMMON_TYPES.put("java.lang.Integer", Integer.class);
81          COMMON_TYPES.put("java.lang.Long", Long.class);
82          COMMON_TYPES.put("java.lang.Float", Float.class);
83          COMMON_TYPES.put("java.lang.Double", Double.class);
84          COMMON_TYPES.put("java.lang.String", String.class);
85  
86          COMMON_TYPES.put("byte[]", BYTE_ARRAY_CLASS);
87          COMMON_TYPES.put("char[]", CHAR_ARRAY_CLASS);
88          COMMON_TYPES.put("double[]", DOUBLE_ARRAY_CLASS);
89          COMMON_TYPES.put("float[]", FLOAT_ARRAY_CLASS);
90          COMMON_TYPES.put("int[]", INT_ARRAY_CLASS);
91          COMMON_TYPES.put("long[]", LONG_ARRAY_CLASS);
92          COMMON_TYPES.put("short[]", SHORT_ARRAY_CLASS);
93          COMMON_TYPES.put("boolean[]", BOOLEAN_ARRAY_CLASS);
94          COMMON_TYPES.put("java.lang.Object[]", OBJECT_ARRAY_CLASS);
95          COMMON_TYPES.put("java.lang.Boolean[]", BOOLEAN_OBJECT_ARRAY_CLASS);
96          COMMON_TYPES.put("java.lang.Byte[]", BYTE_OBJECT_ARRAY_CLASS);
97          COMMON_TYPES.put("java.lang.Character[]", CHARACTER_OBJECT_ARRAY_CLASS);
98          COMMON_TYPES.put("java.lang.Short[]", SHORT_OBJECT_ARRAY_CLASS);
99          COMMON_TYPES.put("java.lang.Integer[]", INTEGER_OBJECT_ARRAY_CLASS);
100         COMMON_TYPES.put("java.lang.Long[]", LONG_OBJECT_ARRAY_CLASS);
101         COMMON_TYPES.put("java.lang.Float[]", FLOAT_OBJECT_ARRAY_CLASS);
102         COMMON_TYPES.put("java.lang.Double[]", DOUBLE_OBJECT_ARRAY_CLASS);
103         COMMON_TYPES.put("java.lang.String[]", STRING_OBJECT_ARRAY_CLASS);
104         // array of void is not a valid type
105     }
106 
107     /** utility class, do not instantiate */
108     private ClassUtils()
109     {
110         // utility class, disable instantiation
111     }
112 
113     //~ Methods ------------------------------------------------------------------------------------
114 
115     /**
116      * Tries a Class.loadClass with the context class loader of the current thread first and
117      * automatically falls back to the ClassUtils class loader (i.e. the loader of the
118      * myfaces.jar lib) if necessary.
119      *
120      * @param type fully qualified name of a non-primitive non-array class
121      * @return the corresponding Class
122      * @throws NullPointerException if type is null
123      * @throws ClassNotFoundException
124      */
125     public static Class classForName(String type)
126         throws ClassNotFoundException
127     {
128         if (type == null) throw new NullPointerException("type");
129         try
130         {
131             // Try WebApp ClassLoader first
132             return Class.forName(type,
133                                  false, // do not initialize for faster startup
134                                  Thread.currentThread().getContextClassLoader());
135         }
136         catch (ClassNotFoundException ignore)
137         {
138             // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)
139             return Class.forName(type,
140                                  false, // do not initialize for faster startup
141                                  ClassUtils.class.getClassLoader());
142         }
143     }
144 
145 
146     /**
147      * Same as {@link #classForName(String)}, but throws a RuntimeException
148      * (FacesException) instead of a ClassNotFoundException.
149      *
150      * @return the corresponding Class
151      * @throws NullPointerException if type is null
152      * @throws FacesException if class not found
153      */
154     public static Class simpleClassForName(String type)
155     {
156         try
157         {
158             return classForName(type);
159         }
160         catch (ClassNotFoundException e)
161         {
162             log.error("Class " + type + " not found", e);
163             throw new FacesException(e);
164         }
165     }
166 
167 
168     /**
169      * Similar as {@link #classForName(String)}, but also supports primitive types
170      * and arrays as specified for the JavaType element in the JavaServer Faces Config DTD.
171      *
172      * @param type fully qualified class name or name of a primitive type, both optionally
173      *             followed by "[]" to indicate an array type
174      * @return the corresponding Class
175      * @throws NullPointerException if type is null
176      * @throws ClassNotFoundException
177      */
178     public static Class javaTypeToClass(String type)
179         throws ClassNotFoundException
180     {
181         if (type == null) throw new NullPointerException("type");
182 
183         // try common types and arrays of common types first
184         Class clazz = (Class) COMMON_TYPES.get(type);
185         if (clazz != null)
186         {
187             return clazz;
188         }
189 
190         int len = type.length();
191         if (len > 2 && type.charAt(len - 1) == ']' && type.charAt(len - 2) == '[')
192         {
193             String componentType = type.substring(0, len - 2);
194             Class componentTypeClass = classForName(componentType);
195             return Array.newInstance(componentTypeClass, 0).getClass();
196         }
197 
198         return classForName(type);
199         
200     }
201 
202 
203     /**
204      * Same as {@link #javaTypeToClass(String)}, but throws a RuntimeException
205      * (FacesException) instead of a ClassNotFoundException.
206      *
207      * @return the corresponding Class
208      * @throws NullPointerException if type is null
209      * @throws FacesException if class not found
210      */
211     public static Class simpleJavaTypeToClass(String type)
212     {
213         try
214         {
215             return javaTypeToClass(type);
216         }
217         catch (ClassNotFoundException e)
218         {
219             log.error("Class " + type + " not found", e);
220             throw new FacesException(e);
221         }
222     }
223 
224     public static InputStream getResourceAsStream(String resource)
225     {
226         InputStream stream = Thread.currentThread().getContextClassLoader()
227                                 .getResourceAsStream(resource);
228         if (stream == null)
229         {
230             // fallback
231             stream = ClassUtils.class.getClassLoader().getResourceAsStream(resource);
232         }
233         return stream;
234     }
235 
236     /**
237      * @param resource       Name of resource(s) to find in classpath
238      * @param defaultObject  The default object to use to determine the class loader (if none associated with current thread.)
239      * @return Iterator over URL Objects
240      */
241     public static Iterator getResources(String resource, Object defaultObject)
242     {
243         try
244         {
245             Enumeration resources = getCurrentLoader(defaultObject).getResources(resource);
246             List lst = new ArrayList();
247             while (resources.hasMoreElements())
248             {
249                 lst.add(resources.nextElement());
250             }
251             return lst.iterator();
252         }
253         catch (IOException e)
254         {
255             log.error(e.getMessage(), e);
256             throw new FacesException(e);
257         }
258     }
259 
260 
261     public static Object newInstance(String type)
262         throws FacesException
263     {
264         if (type == null) return null;
265         return newInstance(simpleClassForName(type));
266     }
267 
268     public static Object newInstance(String type, Class expectedType) throws FacesException
269     {
270         return newInstance(type, expectedType == null ? null : new Class[] {expectedType});
271     }
272 
273     public static Object newInstance(String type, Class[] expectedTypes)
274     {
275         if (type == null)
276             return null;        
277         
278         Class clazzForName = simpleClassForName(type);
279         
280         if(expectedTypes != null)
281         {
282             for (int i = 0, size = expectedTypes.length; i < size; i++)
283             {
284                 if (!expectedTypes[i].isAssignableFrom(clazzForName))
285                 {
286                     throw new FacesException("'" + type + "' does not implement expected type '" + expectedTypes[i]
287                             + "'");
288                 }
289             }
290         }
291         
292         return newInstance(clazzForName);
293     }
294 
295     public static Object newInstance(Class clazz)
296         throws FacesException
297     {
298         try
299         {
300             return clazz.newInstance();
301         }
302         catch(NoClassDefFoundError e)
303         {
304             log.error("Class : "+clazz.getName()+" not found.",e);
305             throw new FacesException(e);
306         }
307         catch (InstantiationException e)
308         {
309             log.error(e.getMessage(), e);
310             throw new FacesException(e);
311         }
312         catch (IllegalAccessException e)
313         {
314             log.error(e.getMessage(), e);
315             throw new FacesException(e);
316         }
317     }
318 
319     public static Object convertToType(Object value, Class desiredClass)
320     {
321         if (value == null) return null;
322 
323         try
324         {
325             ExpressionFactory expFactory = FacesContext.getCurrentInstance().getApplication().getExpressionFactory();
326             return expFactory.coerceToType(value, desiredClass);
327         }
328         catch (Exception e)
329         {
330             String message = "Cannot coerce " + value.getClass().getName()
331                              + " to " + desiredClass.getName();
332             log.error(message, e);
333             throw new FacesException(message, e);
334         }
335     }
336 
337     /**
338      * Gets the ClassLoader associated with the current thread.  Returns the class loader associated with
339      * the specified default object if no context loader is associated with the current thread.
340      *
341      * @param defaultObject The default object to use to determine the class loader (if none associated with current thread.)
342      * @return ClassLoader
343      */
344     protected static ClassLoader getCurrentLoader(Object defaultObject)
345     {
346         ClassLoader loader = Thread.currentThread().getContextClassLoader();
347         if(loader == null)
348         {
349             loader = defaultObject.getClass().getClassLoader();
350         }
351         return loader;
352     }
353 }