View Javadoc
1   package org.andromda.core.common;
2   
3   import java.lang.reflect.Field;
4   import java.lang.reflect.Method;
5   import java.lang.reflect.Modifier;
6   import java.net.URL;
7   import java.util.ArrayList;
8   import java.util.Arrays;
9   import java.util.Collection;
10  import java.util.LinkedHashSet;
11  import java.util.List;
12  import java.util.Set;
13  import org.apache.commons.collections.CollectionUtils;
14  import org.apache.commons.lang.StringUtils;
15  
16  // TODO: FindBugs: This class has a simple name that is identical to that of its superclass. This can be exceptionally confusing.
17  /**
18   * Contains utilities for dealing with classes.
19   *
20   * @author Chad Brandon
21   * @author Bob Fields
22   */
23  public class ClassUtils
24      extends org.apache.commons.lang.ClassUtils
25  {
26      /**
27       * Creates a new instance of the class having the given <code>className</code>.
28       *
29       * @param className the name of the class to instantiate.
30       * @return Object the new instance
31       */
32      public static Object newInstance(final String className)
33      {
34          try
35          {
36              return loadClass(className).newInstance();
37          }
38          catch (final Throwable throwable)
39          {
40              throw new ClassUtilsException(throwable);
41          }
42      }
43  
44      /**
45       * Creates a new instance of the class given the <code>type</code>.
46       *
47       * @param type the type from which to instantiate the new instance.
48       * @return Object the new instance
49       */
50      public static Object newInstance(final Class type)
51      {
52          try
53          {
54              return type != null ? type.newInstance() : null;
55          }
56          catch (final Throwable throwable)
57          {
58              throw new ClassUtilsException(throwable);
59          }
60      }
61  
62      /**
63       * Loads and returns the class having the className. Will load but normal classes and the classes representing
64       * primitives.
65       *
66       * @param className the name of the class to load.
67       * @return Class the loaded class
68       */
69      public static Class loadClass(String className)
70      {
71          ExceptionUtils.checkEmpty(
72              "className",
73              className);
74          className = StringUtils.trimToNull(className);
75  
76          // get rid of any array notation
77          className = StringUtils.replace(
78                  className,
79                  "[]",
80                  "");
81  
82          final ClassLoader loader = getClassLoader();
83          Class loadedClass;
84          try
85          {
86              // check and see if it's a primitive and if so convert it
87              if (ClassUtils.isPrimitiveType(className))
88              {
89                  loadedClass = getPrimitiveClass(
90                          className,
91                          loader);
92              }
93              else
94              {
95                  loadedClass = loader.loadClass(className);
96              }
97          }
98          catch (final Throwable throwable)
99          {
100             throw new ClassUtilsException(throwable);
101         }
102         return loadedClass;
103     }
104 
105     /**
106      * Gets the appropriate class loader instance.
107      *
108      * @return the class loader.
109      */
110     public static ClassLoader getClassLoader()
111     {
112         ClassLoader loader = Thread.currentThread().getContextClassLoader();
113         if (loader == null)
114         {
115             loader = ClassUtils.class.getClassLoader();
116             Thread.currentThread().setContextClassLoader(loader);
117         }
118         return loader;
119     }
120 
121     /**
122      * <p> Returns the type class name for a Java primitive.
123      * </p>
124      *
125      * @param name a <code>String</code> with the name of the type
126      * @param loader the loader to use.
127      * @return a <code>String</code> with the name of the corresponding
128      *         java.lang wrapper class if <code>name</code> is a Java
129      *         primitive type; <code>false</code> if not
130      */
131     protected static Class getPrimitiveClass(
132         final String name,
133         final ClassLoader loader)
134     {
135         ExceptionUtils.checkEmpty(
136             "name",
137             name);
138         ExceptionUtils.checkNull(
139             "loader",
140             loader);
141 
142         Class primitiveClass = null;
143         if (isPrimitiveType(name) && !"void".equals(name))
144         {
145             final String className;
146             if ("char".equals(name))
147             {
148                 className = "Character";
149             }
150             else if ("int".equals(name))
151             {
152                 className = "Integer";
153             }
154             else
155             {
156                 className = StringUtils.capitalize(name);
157             }
158 
159             try
160             {
161                 if (StringUtils.isNotBlank(className))
162                 {
163                     Field field = loader.loadClass(className).getField("TYPE");
164                     primitiveClass = (Class)field.get(null);
165                 }
166             }
167             catch (final Exception exception)
168             {
169                 throw new ClassUtilsException(exception);
170             }
171         }
172         return primitiveClass;
173     }
174 
175     /**
176      * Returns a collection of all static fields values for the given
177      * <code>clazz</code> and <code>type</code> of field.
178      *
179      * @param clazz  the Class from which to retrieve the static fields
180      * @param type the type of static fields to retrieve, if null all are retrieved
181      * @return Collection the collection of static field values.
182      * @throws IllegalAccessException - if some aspect of this static field prevents it from being added to this collection.
183      */
184     public static Collection<Object> getStaticFieldValues(
185         final Class clazz,
186         final Class type)
187         throws IllegalAccessException
188     {
189         ExceptionUtils.checkNull(
190             "clazz",
191             clazz);
192         final Field[] fields = clazz.getFields();
193         int fieldsNum = fields.length;
194 
195         final List<Object> values = new ArrayList<Object>();
196         Field field;
197         int modifiers;
198         for (int ctr = 0; ctr < fieldsNum; ctr++)
199         {
200             field = fields[ctr];
201             modifiers = field.getModifiers();
202             if (Modifier.isStatic(modifiers))
203             {
204                 if (type != null)
205                 {
206                     if (type == field.getType())
207                     {
208                         values.add(fields[ctr].get(null));
209                     }
210                 }
211                 else
212                 {
213                     values.add(fields[ctr].get(null));
214                 }
215             }
216         }
217         return values;
218     }
219 
220     /**
221      * Retrieves all interfaces for the given <code>className</code> (including <code>className</code>
222      * itself, assuming it's an interface as well).
223      *
224      * @param className the root interface className
225      * @return a list containing all interfaces ordered from the root down.
226      */
227     public static List<Class> getInterfaces(final String className)
228     {
229         final List<Class> interfaces = new ArrayList<Class>();
230         if (StringUtils.isNotBlank(className))
231         {
232             interfaces.addAll(getInterfaces(ClassUtils.loadClass(className.trim())));
233         }
234         return interfaces;
235     }
236 
237     /**
238      * Retrieves all interfaces for the given <code>clazz</code> (including <code>clazz</code>
239      * itself, assuming it's an interface as well).
240      *
241      * @param clazz the root interface class
242      * @return a list containing all interfaces ordered from the root down.
243      */
244     public static List<Class> getInterfaces(final Class clazz)
245     {
246         final List<Class> interfaces = new ArrayList<Class>();
247         if (clazz != null)
248         {
249             interfaces.addAll(ClassUtils.getAllInterfaces(clazz));
250             if (clazz.isInterface())
251             {
252                 interfaces.add(
253                     0,
254                     clazz);
255             }
256         }
257         return interfaces;
258     }
259 
260     /**
261      * Gets the interfaces for the given <code>className</code> in reverse order.
262      *
263      * @param className the name of the class for which to retrieve the interfaces
264      * @return the array containing the reversed interfaces.
265      */
266     public static Class[] getInterfacesReversed(final String className)
267     {
268         final List<Class> interfacesList = getInterfaces(className);
269         Class[] interfaces = interfacesList.toArray(new Class[interfacesList.size()]);
270         if (interfaces.length > 0)
271         {
272             CollectionUtils.reverseArray(interfaces);
273         }
274         return interfaces;
275     }
276 
277     /**
278      * <p>
279      * Checks if a given type name is a Java primitive type. </p>
280      *
281      * @param name a <code>String</code> with the name of the type
282      * @return <code>true</code> if <code>name</code> is a Java primitive type; <code>false</code> if not
283      */
284     protected static boolean isPrimitiveType(final String name)
285     {
286         return "void".equals(name) || "char".equals(name) || "byte".equals(name) || "short".equals(name) ||
287         "int".equals(name) || "long".equals(name) || "float".equals(name) || "double".equals(name) ||
288         "boolean".equals(name);
289     }
290 
291     /**
292      * The suffix for class files.
293      */
294     public static final String CLASS_EXTENSION = ".class";
295 
296     /**
297      * Searches the contents of the <code>directoryUri</code> and returns the first
298      * Class found that is of the given <code>type</code>.
299      *
300      * @param directoryUris the URIs to search, ie. directories or archives.
301      * @param type the type to find.
302      * @return the class or null if not found.
303      */
304     public static Class findClassOfType(
305         final URL[] directoryUris,
306         final Class type)
307     {
308         Class found = null;
309         if (directoryUris != null && directoryUris.length > 0)
310         {
311             for (URL directoryUri : directoryUris)
312             {
313                 final List<String> contents = ResourceUtils.getDirectoryContents(
314                         directoryUri,
315                         false,
316                         null);
317                 for (final String path : contents)
318                 {
319                     if (path.endsWith(CLASS_EXTENSION))
320                     {
321                         final String typeName =
322                                 StringUtils.replace(
323                                         ResourceUtils.normalizePath(path).replace('/', '.'),
324                                         CLASS_EXTENSION,
325                                         "");
326                         try
327                         {
328                             final Class loadedClass = getClassLoader().loadClass(typeName);
329                             if (type.isAssignableFrom(loadedClass))
330                             {
331                                 found = loadedClass;
332                                 break;
333                             }
334                         }
335                         catch (final ClassNotFoundException exception)
336                         {
337                             // - ignore, means the file wasn't a class
338                         }
339                     }
340                 }
341             }
342         }
343         return found;
344     }
345 
346     /**
347      * Loads all methods from the given <code>clazz</code> (this includes
348      * all super class methods, public, private and protected).
349      *
350      * @param clazz the class to retrieve the methods.
351      * @return the loaded methods.
352      */
353     public static List<Method> getAllMethods(final Class clazz)
354     {
355         final Set<Method> methods = new LinkedHashSet<Method>();
356         loadMethods(clazz, methods);
357         return new ArrayList<Method>(methods);
358     }
359 
360     /**
361      * Loads all methods from the given <code>clazz</code> (this includes
362      * all super class methods).
363      *
364      * @param methods the list to load full of methods.
365      * @param clazz the class to retrieve the methods.
366      */
367     private static void loadMethods(
368         final Class clazz,
369         final Set<Method> methods)
370     {
371         methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
372         if (clazz.getSuperclass() != null)
373         {
374             loadMethods(
375                     clazz.getSuperclass(),
376                     methods);
377         }
378     }
379 
380     /**
381      * Indicates whether or not a class of the given <code>type</code>
382      * is present in one of the given <code>directoryUris</code>.
383      *
384      * @param directoryUris the URIs to search, ie. directories or archives.
385      * @param type the type to check.
386      * @return true/false.
387      */
388     public static boolean isClassOfTypePresent(
389         final URL[] directoryUris,
390         final Class type)
391     {
392         return ClassUtils.findClassOfType(directoryUris, type) != null;
393     }
394 }