View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.reflection;
17  
18  import java.lang.reflect.Array;
19  import java.lang.reflect.Field;
20  import java.lang.reflect.GenericArrayType;
21  import java.lang.reflect.Method;
22  import java.lang.reflect.ParameterizedType;
23  import java.lang.reflect.Type;
24  import java.lang.reflect.TypeVariable;
25  import java.lang.reflect.WildcardType;
26  import java.util.Arrays;
27  
28  /**
29   * @author Iwao AVE!
30   */
31  public class TypeParameterResolver {
32  
33    /**
34     * Resolve field type.
35     *
36     * @param field
37     *          the field
38     * @param srcType
39     *          the src type
40     * @return The field type as {@link Type}. If it has type parameters in the declaration,<br>
41     *         they will be resolved to the actual runtime {@link Type}s.
42     */
43    public static Type resolveFieldType(Field field, Type srcType) {
44      Type fieldType = field.getGenericType();
45      Class<?> declaringClass = field.getDeclaringClass();
46      return resolveType(fieldType, srcType, declaringClass);
47    }
48  
49    /**
50     * Resolve return type.
51     *
52     * @param method
53     *          the method
54     * @param srcType
55     *          the src type
56     * @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
57     *         they will be resolved to the actual runtime {@link Type}s.
58     */
59    public static Type resolveReturnType(Method method, Type srcType) {
60      Type returnType = method.getGenericReturnType();
61      Class<?> declaringClass = method.getDeclaringClass();
62      return resolveType(returnType, srcType, declaringClass);
63    }
64  
65    /**
66     * Resolve param types.
67     *
68     * @param method
69     *          the method
70     * @param srcType
71     *          the src type
72     * @return The parameter types of the method as an array of {@link Type}s. If they have type parameters in the
73     *         declaration,<br>
74     *         they will be resolved to the actual runtime {@link Type}s.
75     */
76    public static Type[] resolveParamTypes(Method method, Type srcType) {
77      Type[] paramTypes = method.getGenericParameterTypes();
78      Class<?> declaringClass = method.getDeclaringClass();
79      Type[] result = new Type[paramTypes.length];
80      for (int i = 0; i < paramTypes.length; i++) {
81        result[i] = resolveType(paramTypes[i], srcType, declaringClass);
82      }
83      return result;
84    }
85  
86    private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
87      if (type instanceof TypeVariable) {
88        return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
89      } else if (type instanceof ParameterizedType) {
90        return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
91      } else if (type instanceof GenericArrayType) {
92        return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
93      } else {
94        return type;
95      }
96    }
97  
98    private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass) {
99      Type componentType = genericArrayType.getGenericComponentType();
100     Type resolvedComponentType = null;
101     if (componentType instanceof TypeVariable) {
102       resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
103     } else if (componentType instanceof GenericArrayType) {
104       resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
105     } else if (componentType instanceof ParameterizedType) {
106       resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
107     }
108     if (resolvedComponentType instanceof Class) {
109       return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
110     } else {
111       return new GenericArrayTypeImpl(resolvedComponentType);
112     }
113   }
114 
115   private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
116     Class<?> rawType = (Class<?>) parameterizedType.getRawType();
117     Type[] typeArgs = parameterizedType.getActualTypeArguments();
118     Type[] args = new Type[typeArgs.length];
119     for (int i = 0; i < typeArgs.length; i++) {
120       if (typeArgs[i] instanceof TypeVariable) {
121         args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
122       } else if (typeArgs[i] instanceof ParameterizedType) {
123         args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
124       } else if (typeArgs[i] instanceof WildcardType) {
125         args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
126       } else {
127         args[i] = typeArgs[i];
128       }
129     }
130     return new ParameterizedTypeImpl(rawType, null, args);
131   }
132 
133   private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class<?> declaringClass) {
134     Type[] lowerBounds = resolveWildcardTypeBounds(wildcardType.getLowerBounds(), srcType, declaringClass);
135     Type[] upperBounds = resolveWildcardTypeBounds(wildcardType.getUpperBounds(), srcType, declaringClass);
136     return new WildcardTypeImpl(lowerBounds, upperBounds);
137   }
138 
139   private static Type[] resolveWildcardTypeBounds(Type[] bounds, Type srcType, Class<?> declaringClass) {
140     Type[] result = new Type[bounds.length];
141     for (int i = 0; i < bounds.length; i++) {
142       if (bounds[i] instanceof TypeVariable) {
143         result[i] = resolveTypeVar((TypeVariable<?>) bounds[i], srcType, declaringClass);
144       } else if (bounds[i] instanceof ParameterizedType) {
145         result[i] = resolveParameterizedType((ParameterizedType) bounds[i], srcType, declaringClass);
146       } else if (bounds[i] instanceof WildcardType) {
147         result[i] = resolveWildcardType((WildcardType) bounds[i], srcType, declaringClass);
148       } else {
149         result[i] = bounds[i];
150       }
151     }
152     return result;
153   }
154 
155   private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
156     Type result;
157     Class<?> clazz;
158     if (srcType instanceof Class) {
159       clazz = (Class<?>) srcType;
160     } else if (srcType instanceof ParameterizedType) {
161       ParameterizedType parameterizedType = (ParameterizedType) srcType;
162       clazz = (Class<?>) parameterizedType.getRawType();
163     } else {
164       throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
165     }
166 
167     if (clazz == declaringClass) {
168       Type[] bounds = typeVar.getBounds();
169       if (bounds.length > 0) {
170         return bounds[0];
171       }
172       return Object.class;
173     }
174 
175     Type superclass = clazz.getGenericSuperclass();
176     result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
177     if (result != null) {
178       return result;
179     }
180 
181     Type[] superInterfaces = clazz.getGenericInterfaces();
182     for (Type superInterface : superInterfaces) {
183       result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
184       if (result != null) {
185         return result;
186       }
187     }
188     return Object.class;
189   }
190 
191   private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
192     if (superclass instanceof ParameterizedType) {
193       ParameterizedType parentAsType = (ParameterizedType) superclass;
194       Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
195       TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
196       if (srcType instanceof ParameterizedType) {
197         parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
198       }
199       if (declaringClass == parentAsClass) {
200         for (int i = 0; i < parentTypeVars.length; i++) {
201           if (typeVar.equals(parentTypeVars[i])) {
202             return parentAsType.getActualTypeArguments()[i];
203           }
204         }
205       }
206       if (declaringClass.isAssignableFrom(parentAsClass)) {
207         return resolveTypeVar(typeVar, parentAsType, declaringClass);
208       }
209     } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
210       return resolveTypeVar(typeVar, superclass, declaringClass);
211     }
212     return null;
213   }
214 
215   private static ParameterizedType translateParentTypeVars(ParameterizedType srcType, Class<?> srcClass, ParameterizedType parentType) {
216     Type[] parentTypeArgs = parentType.getActualTypeArguments();
217     Type[] srcTypeArgs = srcType.getActualTypeArguments();
218     TypeVariable<?>[] srcTypeVars = srcClass.getTypeParameters();
219     Type[] newParentArgs = new Type[parentTypeArgs.length];
220     boolean noChange = true;
221     for (int i = 0; i < parentTypeArgs.length; i++) {
222       if (parentTypeArgs[i] instanceof TypeVariable) {
223         for (int j = 0; j < srcTypeVars.length; j++) {
224           if (srcTypeVars[j].equals(parentTypeArgs[i])) {
225             noChange = false;
226             newParentArgs[i] = srcTypeArgs[j];
227           }
228         }
229       } else {
230         newParentArgs[i] = parentTypeArgs[i];
231       }
232     }
233     return noChange ? parentType : new ParameterizedTypeImpl((Class<?>)parentType.getRawType(), null, newParentArgs);
234   }
235 
236   private TypeParameterResolver() {
237     super();
238   }
239 
240   static class ParameterizedTypeImpl implements ParameterizedType {
241     private Class<?> rawType;
242 
243     private Type ownerType;
244 
245     private Type[] actualTypeArguments;
246 
247     public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
248       super();
249       this.rawType = rawType;
250       this.ownerType = ownerType;
251       this.actualTypeArguments = actualTypeArguments;
252     }
253 
254     @Override
255     public Type[] getActualTypeArguments() {
256       return actualTypeArguments;
257     }
258 
259     @Override
260     public Type getOwnerType() {
261       return ownerType;
262     }
263 
264     @Override
265     public Type getRawType() {
266       return rawType;
267     }
268 
269     @Override
270     public String toString() {
271       return "ParameterizedTypeImpl [rawType=" + rawType + ", ownerType=" + ownerType + ", actualTypeArguments=" + Arrays.toString(actualTypeArguments) + "]";
272     }
273   }
274 
275   static class WildcardTypeImpl implements WildcardType {
276     private Type[] lowerBounds;
277 
278     private Type[] upperBounds;
279 
280     WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
281       super();
282       this.lowerBounds = lowerBounds;
283       this.upperBounds = upperBounds;
284     }
285 
286     @Override
287     public Type[] getLowerBounds() {
288       return lowerBounds;
289     }
290 
291     @Override
292     public Type[] getUpperBounds() {
293       return upperBounds;
294     }
295   }
296 
297   static class GenericArrayTypeImpl implements GenericArrayType {
298     private Type genericComponentType;
299 
300     GenericArrayTypeImpl(Type genericComponentType) {
301       super();
302       this.genericComponentType = genericComponentType;
303     }
304 
305     @Override
306     public Type getGenericComponentType() {
307       return genericComponentType;
308     }
309   }
310 }