View Javadoc
1   /*
2    *    Copyright 2009-2024 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    *       https://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     *
41     * @return The field type as {@link Type}. If it has type parameters in the declaration,<br>
42     *         they will be resolved to the actual runtime {@link Type}s.
43     */
44    public static Type resolveFieldType(Field field, Type srcType) {
45      Type fieldType = field.getGenericType();
46      Class<?> declaringClass = field.getDeclaringClass();
47      return resolveType(fieldType, srcType, declaringClass);
48    }
49  
50    /**
51     * Resolve return type.
52     *
53     * @param method
54     *          the method
55     * @param srcType
56     *          the src type
57     *
58     * @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
59     *         they will be resolved to the actual runtime {@link Type}s.
60     */
61    public static Type resolveReturnType(Method method, Type srcType) {
62      Type returnType = method.getGenericReturnType();
63      Class<?> declaringClass = method.getDeclaringClass();
64      return resolveType(returnType, srcType, declaringClass);
65    }
66  
67    /**
68     * Resolve param types.
69     *
70     * @param method
71     *          the method
72     * @param srcType
73     *          the src type
74     *
75     * @return The parameter types of the method as an array of {@link Type}s. If they have type parameters in the
76     *         declaration,<br>
77     *         they will be resolved to the actual runtime {@link Type}s.
78     */
79    public static Type[] resolveParamTypes(Method method, Type srcType) {
80      Type[] paramTypes = method.getGenericParameterTypes();
81      Class<?> declaringClass = method.getDeclaringClass();
82      Type[] result = new Type[paramTypes.length];
83      for (int i = 0; i < paramTypes.length; i++) {
84        result[i] = resolveType(paramTypes[i], srcType, declaringClass);
85      }
86      return result;
87    }
88  
89    private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
90      if (type instanceof TypeVariable) {
91        return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
92      }
93      if (type instanceof ParameterizedType) {
94        return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
95      } else if (type instanceof GenericArrayType) {
96        return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
97      } else {
98        return type;
99      }
100   }
101 
102   private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType,
103       Class<?> declaringClass) {
104     Type componentType = genericArrayType.getGenericComponentType();
105     Type resolvedComponentType = null;
106     if (componentType instanceof TypeVariable) {
107       resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
108     } else if (componentType instanceof GenericArrayType) {
109       resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
110     } else if (componentType instanceof ParameterizedType) {
111       resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
112     }
113     if (resolvedComponentType instanceof Class) {
114       return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
115     }
116     return new GenericArrayTypeImpl(resolvedComponentType);
117   }
118 
119   private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType,
120       Class<?> declaringClass) {
121     Class<?> rawType = (Class<?>) parameterizedType.getRawType();
122     Type[] typeArgs = parameterizedType.getActualTypeArguments();
123     Type[] args = new Type[typeArgs.length];
124     for (int i = 0; i < typeArgs.length; i++) {
125       if (typeArgs[i] instanceof TypeVariable) {
126         args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
127       } else if (typeArgs[i] instanceof ParameterizedType) {
128         args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
129       } else if (typeArgs[i] instanceof WildcardType) {
130         args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
131       } else {
132         args[i] = typeArgs[i];
133       }
134     }
135     return new ParameterizedTypeImpl(rawType, null, args);
136   }
137 
138   private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class<?> declaringClass) {
139     Type[] lowerBounds = resolveWildcardTypeBounds(wildcardType.getLowerBounds(), srcType, declaringClass);
140     Type[] upperBounds = resolveWildcardTypeBounds(wildcardType.getUpperBounds(), srcType, declaringClass);
141     return new WildcardTypeImpl(lowerBounds, upperBounds);
142   }
143 
144   private static Type[] resolveWildcardTypeBounds(Type[] bounds, Type srcType, Class<?> declaringClass) {
145     Type[] result = new Type[bounds.length];
146     for (int i = 0; i < bounds.length; i++) {
147       if (bounds[i] instanceof TypeVariable) {
148         result[i] = resolveTypeVar((TypeVariable<?>) bounds[i], srcType, declaringClass);
149       } else if (bounds[i] instanceof ParameterizedType) {
150         result[i] = resolveParameterizedType((ParameterizedType) bounds[i], srcType, declaringClass);
151       } else if (bounds[i] instanceof WildcardType) {
152         result[i] = resolveWildcardType((WildcardType) bounds[i], srcType, declaringClass);
153       } else {
154         result[i] = bounds[i];
155       }
156     }
157     return result;
158   }
159 
160   private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
161     Type result;
162     Class<?> clazz;
163     if (srcType instanceof Class) {
164       clazz = (Class<?>) srcType;
165     } else if (srcType instanceof ParameterizedType) {
166       ParameterizedType parameterizedType = (ParameterizedType) srcType;
167       clazz = (Class<?>) parameterizedType.getRawType();
168     } else {
169       throw new IllegalArgumentException(
170           "The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
171     }
172 
173     if (clazz == declaringClass) {
174       Type[] bounds = typeVar.getBounds();
175       if (bounds.length > 0) {
176         return bounds[0];
177       }
178       return Object.class;
179     }
180 
181     Type superclass = clazz.getGenericSuperclass();
182     result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
183     if (result != null) {
184       return result;
185     }
186 
187     Type[] superInterfaces = clazz.getGenericInterfaces();
188     for (Type superInterface : superInterfaces) {
189       result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
190       if (result != null) {
191         return result;
192       }
193     }
194     return Object.class;
195   }
196 
197   private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz,
198       Type superclass) {
199     if (superclass instanceof ParameterizedType) {
200       ParameterizedType parentAsType = (ParameterizedType) superclass;
201       Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
202       TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
203       if (srcType instanceof ParameterizedType) {
204         parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
205       }
206       if (declaringClass == parentAsClass) {
207         for (int i = 0; i < parentTypeVars.length; i++) {
208           if (typeVar.equals(parentTypeVars[i])) {
209             Type actualType = parentAsType.getActualTypeArguments()[i];
210             return actualType instanceof TypeVariable<?> ? Object.class : actualType;
211           }
212         }
213       }
214       if (declaringClass.isAssignableFrom(parentAsClass)) {
215         return resolveTypeVar(typeVar, parentAsType, declaringClass);
216       }
217     } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
218       return resolveTypeVar(typeVar, superclass, declaringClass);
219     }
220     return null;
221   }
222 
223   private static ParameterizedType translateParentTypeVars(ParameterizedType srcType, Class<?> srcClass,
224       ParameterizedType parentType) {
225     Type[] parentTypeArgs = parentType.getActualTypeArguments();
226     Type[] srcTypeArgs = srcType.getActualTypeArguments();
227     TypeVariable<?>[] srcTypeVars = srcClass.getTypeParameters();
228     Type[] newParentArgs = new Type[parentTypeArgs.length];
229     boolean noChange = true;
230     for (int i = 0; i < parentTypeArgs.length; i++) {
231       if (parentTypeArgs[i] instanceof TypeVariable) {
232         for (int j = 0; j < srcTypeVars.length; j++) {
233           if (srcTypeVars[j].equals(parentTypeArgs[i])) {
234             noChange = false;
235             newParentArgs[i] = srcTypeArgs[j];
236           }
237         }
238       } else {
239         newParentArgs[i] = parentTypeArgs[i];
240       }
241     }
242     return noChange ? parentType : new ParameterizedTypeImpl((Class<?>) parentType.getRawType(), null, newParentArgs);
243   }
244 
245   private TypeParameterResolver() {
246   }
247 
248   static class ParameterizedTypeImpl implements ParameterizedType {
249     private final Class<?> rawType;
250 
251     private final Type ownerType;
252 
253     private final Type[] actualTypeArguments;
254 
255     public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
256       this.rawType = rawType;
257       this.ownerType = ownerType;
258       this.actualTypeArguments = actualTypeArguments;
259     }
260 
261     @Override
262     public Type[] getActualTypeArguments() {
263       return actualTypeArguments;
264     }
265 
266     @Override
267     public Type getOwnerType() {
268       return ownerType;
269     }
270 
271     @Override
272     public Type getRawType() {
273       return rawType;
274     }
275 
276     @Override
277     public String toString() {
278       return "ParameterizedTypeImpl [rawType=" + rawType + ", ownerType=" + ownerType + ", actualTypeArguments="
279           + Arrays.toString(actualTypeArguments) + "]";
280     }
281   }
282 
283   static class WildcardTypeImpl implements WildcardType {
284     private final Type[] lowerBounds;
285 
286     private final Type[] upperBounds;
287 
288     WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
289       this.lowerBounds = lowerBounds;
290       this.upperBounds = upperBounds;
291     }
292 
293     @Override
294     public Type[] getLowerBounds() {
295       return lowerBounds;
296     }
297 
298     @Override
299     public Type[] getUpperBounds() {
300       return upperBounds;
301     }
302   }
303 
304   static class GenericArrayTypeImpl implements GenericArrayType {
305     private final Type genericComponentType;
306 
307     GenericArrayTypeImpl(Type genericComponentType) {
308       this.genericComponentType = genericComponentType;
309     }
310 
311     @Override
312     public Type getGenericComponentType() {
313       return genericComponentType;
314     }
315   }
316 }