View Javadoc
1   /*
2    *    Copyright 2009-2023 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             return parentAsType.getActualTypeArguments()[i];
210           }
211         }
212       }
213       if (declaringClass.isAssignableFrom(parentAsClass)) {
214         return resolveTypeVar(typeVar, parentAsType, declaringClass);
215       }
216     } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
217       return resolveTypeVar(typeVar, superclass, declaringClass);
218     }
219     return null;
220   }
221 
222   private static ParameterizedType translateParentTypeVars(ParameterizedType srcType, Class<?> srcClass,
223       ParameterizedType parentType) {
224     Type[] parentTypeArgs = parentType.getActualTypeArguments();
225     Type[] srcTypeArgs = srcType.getActualTypeArguments();
226     TypeVariable<?>[] srcTypeVars = srcClass.getTypeParameters();
227     Type[] newParentArgs = new Type[parentTypeArgs.length];
228     boolean noChange = true;
229     for (int i = 0; i < parentTypeArgs.length; i++) {
230       if (parentTypeArgs[i] instanceof TypeVariable) {
231         for (int j = 0; j < srcTypeVars.length; j++) {
232           if (srcTypeVars[j].equals(parentTypeArgs[i])) {
233             noChange = false;
234             newParentArgs[i] = srcTypeArgs[j];
235           }
236         }
237       } else {
238         newParentArgs[i] = parentTypeArgs[i];
239       }
240     }
241     return noChange ? parentType : new ParameterizedTypeImpl((Class<?>) parentType.getRawType(), null, newParentArgs);
242   }
243 
244   private TypeParameterResolver() {
245   }
246 
247   static class ParameterizedTypeImpl implements ParameterizedType {
248     private final Class<?> rawType;
249 
250     private final Type ownerType;
251 
252     private final Type[] actualTypeArguments;
253 
254     public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
255       this.rawType = rawType;
256       this.ownerType = ownerType;
257       this.actualTypeArguments = actualTypeArguments;
258     }
259 
260     @Override
261     public Type[] getActualTypeArguments() {
262       return actualTypeArguments;
263     }
264 
265     @Override
266     public Type getOwnerType() {
267       return ownerType;
268     }
269 
270     @Override
271     public Type getRawType() {
272       return rawType;
273     }
274 
275     @Override
276     public String toString() {
277       return "ParameterizedTypeImpl [rawType=" + rawType + ", ownerType=" + ownerType + ", actualTypeArguments="
278           + Arrays.toString(actualTypeArguments) + "]";
279     }
280   }
281 
282   static class WildcardTypeImpl implements WildcardType {
283     private final Type[] lowerBounds;
284 
285     private final Type[] upperBounds;
286 
287     WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
288       this.lowerBounds = lowerBounds;
289       this.upperBounds = upperBounds;
290     }
291 
292     @Override
293     public Type[] getLowerBounds() {
294       return lowerBounds;
295     }
296 
297     @Override
298     public Type[] getUpperBounds() {
299       return upperBounds;
300     }
301   }
302 
303   static class GenericArrayTypeImpl implements GenericArrayType {
304     private final Type genericComponentType;
305 
306     GenericArrayTypeImpl(Type genericComponentType) {
307       this.genericComponentType = genericComponentType;
308     }
309 
310     @Override
311     public Type getGenericComponentType() {
312       return genericComponentType;
313     }
314   }
315 }