View Javadoc

1   package com.atlassian.plugin.util;
2   
3   import java.lang.reflect.Array;
4   import java.lang.reflect.GenericArrayType;
5   import java.lang.reflect.ParameterizedType;
6   import java.lang.reflect.Type;
7   import java.lang.reflect.TypeVariable;
8   import java.util.ArrayList;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Set;
13  import java.util.HashSet;
14  
15  /**
16   * Class utility methods
17   */
18  public class ClassUtils
19  {
20      private ClassUtils()
21      {
22      }
23  
24      /**
25       * Finds all super classes and interfaces for a given class
26       *
27       * @param cls The class to scan
28       * @return The collected related classes found
29       */
30      public static Set<Class> findAllTypes(Class cls)
31      {
32          Set<Class> types = new HashSet<Class>();
33          findAllTypes(cls, types);
34          return types;
35      }
36  
37      /**
38       * Finds all super classes and interfaces for a given class
39       *
40       * @param cls   The class to scan
41       * @param types The collected related classes found
42       */
43      public static void findAllTypes(Class cls, Set<Class> types)
44      {
45          if (cls == null)
46          {
47              return;
48          }
49  
50          // check to ensure it hasn't been scanned yet
51          if (types.contains(cls))
52          {
53              return;
54          }
55  
56          types.add(cls);
57  
58          findAllTypes(cls.getSuperclass(), types);
59          for (int x = 0; x < cls.getInterfaces().length; x++)
60          {
61              findAllTypes(cls.getInterfaces()[x], types);
62          }
63      }
64  
65      /**
66       * Get the underlying class for a type, or null if the type is a variable type.
67       *
68       * @param type the type
69       * @return the underlying class
70       */
71      private static Class<?> getClass(Type type)
72      {
73          if (type instanceof Class)
74          {
75              return (Class) type;
76          }
77          else if (type instanceof ParameterizedType)
78          {
79              return getClass(((ParameterizedType) type).getRawType());
80          }
81          else if (type instanceof GenericArrayType)
82          {
83              Type componentType = ((GenericArrayType) type).getGenericComponentType();
84              Class<?> componentClass = getClass(componentType);
85              if (componentClass != null)
86              {
87                  return Array.newInstance(componentClass, 0).getClass();
88              }
89              else
90              {
91                  return null;
92              }
93          }
94          else
95          {
96              return null;
97          }
98      }
99  
100     /**
101      * Get the actual type arguments a child class has used to extend a generic base class.
102      *
103      * @param baseClass  the base class
104      * @param childClass the child class
105      * @return a list of the raw classes for the actual type arguments.
106      * @throws IllegalArgumentException If the child class is not the base of the baseClass
107      * @since 2.5.0
108      */
109     public static <T> List<Class<?>> getTypeArguments(
110             Class<T> baseClass, Class<? extends T> childClass)
111     {
112         Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
113         Type type = childClass;
114         Class typeClass = getClass(type);
115         // start walking up the inheritance hierarchy until we hit baseClass
116         while (!typeClass.equals(baseClass))
117         {
118             if (type instanceof Class)
119             {
120                 // there is no useful information for us in raw types, so just keep going.
121                 type = ((Class) type).getGenericSuperclass();
122             }
123             else
124             {
125                 ParameterizedType parameterizedType = (ParameterizedType) type;
126                 Class<?> rawType = (Class) parameterizedType.getRawType();
127 
128                 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
129                 TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
130                 for (int i = 0; i < actualTypeArguments.length; i++)
131                 {
132                     resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
133                 }
134 
135                 if (!rawType.equals(baseClass))
136                 {
137                     type = rawType.getGenericSuperclass();
138                 }
139             }
140             typeClass = getClass(type);
141             if (typeClass == null)
142             {
143                 throw new IllegalArgumentException("Unable to find the class for the type " + type);
144             }
145         }
146 
147         // finally, for each actual type argument provided to baseClass, determine (if possible)
148         // the raw class for that type argument.
149         Type[] actualTypeArguments;
150         if (type instanceof Class)
151         {
152             actualTypeArguments = ((Class) type).getTypeParameters();
153         }
154         else
155         {
156             actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
157         }
158         List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
159         // resolve types by chasing down type variables.
160         for (Type baseType : actualTypeArguments)
161         {
162             while (resolvedTypes.containsKey(baseType))
163             {
164                 baseType = resolvedTypes.get(baseType);
165             }
166             typeArgumentsAsClasses.add(getClass(baseType));
167         }
168         return typeArgumentsAsClasses;
169     }
170 }