• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / mybatis-3 / 2741

06 Mar 2025 05:36PM UTC coverage: 87.285% (+0.06%) from 87.226%
2741

Pull #3379

github

web-flow
Merge 949ccd385 into 1a145cdff
Pull Request #3379: Resolve type handler based on `java.lang.reflect.Type` instead of `Class` and respect runtime JDBC type

3834 of 4655 branches covered (82.36%)

627 of 695 new or added lines in 61 files covered. (90.22%)

14 existing lines in 4 files now uncovered.

9913 of 11357 relevant lines covered (87.29%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.42
/src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java
1
/*
2
 *    Copyright 2009-2025 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
import java.util.Objects;
28

29
/**
30
 * @author Iwao AVE!
31
 * @author Vladimir Sitnikov
32
 */
33
public class TypeParameterResolver {
34

35
  public static Type[] resolveClassTypeParams(Class<?> classWithTypeParams, Class<?> childClass) {
36
    TypeVariable<?>[] typeArgs = classWithTypeParams.getTypeParameters();
1✔
37
    Type[] result = new Type[typeArgs.length];
1✔
38
    for (int i = 0; i < typeArgs.length; i++) {
1✔
39
      result[i] = resolveTypeVar(typeArgs[i], childClass, classWithTypeParams);
1✔
40
    }
41
    return result;
1✔
42
  }
43

44
  /**
45
   * Resolve field type.
46
   *
47
   * @param field
48
   *          the field
49
   * @param srcType
50
   *          the src type
51
   *
52
   * @return The field type as {@link Type}. If it has type parameters in the declaration,<br>
53
   *         they will be resolved to the actual runtime {@link Type}s.
54
   */
55
  public static Type resolveFieldType(Field field, Type srcType) {
56
    Type fieldType = field.getGenericType();
1✔
57
    Class<?> declaringClass = field.getDeclaringClass();
1✔
58
    return resolveType(fieldType, srcType, declaringClass);
1✔
59
  }
60

61
  /**
62
   * Resolve return type.
63
   *
64
   * @param method
65
   *          the method
66
   * @param srcType
67
   *          the src type
68
   *
69
   * @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
70
   *         they will be resolved to the actual runtime {@link Type}s.
71
   */
72
  public static Type resolveReturnType(Method method, Type srcType) {
73
    Type returnType = method.getGenericReturnType();
1✔
74
    Class<?> declaringClass = method.getDeclaringClass();
1✔
75
    return resolveType(returnType, srcType, declaringClass);
1✔
76
  }
77

78
  /**
79
   * Resolve param types.
80
   *
81
   * @param method
82
   *          the method
83
   * @param srcType
84
   *          the src type
85
   *
86
   * @return The parameter types of the method as an array of {@link Type}s. If they have type parameters in the
87
   *         declaration,<br>
88
   *         they will be resolved to the actual runtime {@link Type}s.
89
   */
90
  public static Type[] resolveParamTypes(Method method, Type srcType) {
91
    Type[] paramTypes = method.getGenericParameterTypes();
1✔
92
    Class<?> declaringClass = method.getDeclaringClass();
1✔
93
    return resolveTypes(paramTypes, srcType, declaringClass);
1✔
94
  }
95

96
  private static Type[] resolveTypes(Type[] types, Type srcType, Class<?> declaringClass) {
97
    Type[] args = new Type[types.length];
1✔
98
    for (int i = 0; i < types.length; i++) {
1✔
99
      args[i] = resolveType(types[i], srcType, declaringClass);
1✔
100
    }
101
    return args;
1✔
102
  }
103

104
  private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
105
    if (type instanceof TypeVariable) {
1✔
106
      return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
1✔
107
    } else if (type instanceof ParameterizedType) {
1✔
108
      return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
1✔
109
    } else if (type instanceof GenericArrayType) {
1✔
110
      return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
1✔
111
    } else if (type instanceof WildcardType) {
1✔
112
      return resolveWildcardType((WildcardType) type, srcType, declaringClass);
1✔
113
    } else {
114
      return type;
1✔
115
    }
116
  }
117

118
  private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType,
119
      Class<?> declaringClass) {
120
    Type componentType = genericArrayType.getGenericComponentType();
1✔
121
    Type resolvedComponentType = resolveType(componentType, srcType, declaringClass);
1✔
122
    if (resolvedComponentType instanceof Class) {
1✔
123
      return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
1✔
124
    } else {
125
      return new GenericArrayTypeImpl(resolvedComponentType);
1✔
126
    }
127
  }
128

129
  private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType,
130
      Class<?> declaringClass) {
131
    Class<?> rawType = (Class<?>) parameterizedType.getRawType();
1✔
132
    Type[] typeArgs = parameterizedType.getActualTypeArguments();
1✔
133
    Type[] args = resolveTypes(typeArgs, srcType, declaringClass);
1✔
134
    return new ParameterizedTypeImpl(rawType, null, args);
1✔
135
  }
136

137
  private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class<?> declaringClass) {
138
    Type[] lowerBounds = resolveTypes(wildcardType.getLowerBounds(), srcType, declaringClass);
1✔
139
    Type[] upperBounds = resolveTypes(wildcardType.getUpperBounds(), srcType, declaringClass);
1✔
140
    return new WildcardTypeImpl(lowerBounds, upperBounds);
1✔
141
  }
142

143
  private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
144
    Type result;
145
    Class<?> clazz;
146
    if (srcType instanceof Class) {
1✔
147
      clazz = (Class<?>) srcType;
1✔
148
    } else if (srcType instanceof ParameterizedType) {
1!
149
      ParameterizedType parameterizedType = (ParameterizedType) srcType;
1✔
150
      clazz = (Class<?>) parameterizedType.getRawType();
1✔
151
      if (clazz == declaringClass) {
1✔
152
        TypeVariable<?>[] typeVars = declaringClass.getTypeParameters();
1✔
153
        for (int i = 0; i < typeVars.length; i++) {
1✔
154
          if (typeVar.equals(typeVars[i])) {
1✔
155
            return parameterizedType.getActualTypeArguments()[i];
1✔
156
          }
157
        }
158
      }
159
    } else {
1✔
160
      throw new IllegalArgumentException(
×
161
          "The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
×
162
    }
163

164
    if (clazz == declaringClass) {
1✔
165
      Type[] bounds = typeVar.getBounds();
1✔
166
      if (bounds.length > 0) {
1!
167
        return bounds[0];
1✔
168
      }
169
      return Object.class;
×
170
    }
171

172
    Type superclass = clazz.getGenericSuperclass();
1✔
173
    result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
1✔
174
    if (result != null) {
1✔
175
      return result;
1✔
176
    }
177

178
    Type[] superInterfaces = clazz.getGenericInterfaces();
1✔
179
    for (Type superInterface : superInterfaces) {
1!
180
      result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
1✔
181
      if (result != null) {
1✔
182
        return result;
1✔
183
      }
184
    }
185
    return Object.class;
×
186
  }
187

188
  private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz,
189
      Type superclass) {
190
    if (superclass instanceof ParameterizedType) {
1✔
191
      ParameterizedType parentAsType = (ParameterizedType) superclass;
1✔
192
      Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
1✔
193
      TypeVariable<?>[] parentTypeVars = parentAsClass.getTypeParameters();
1✔
194
      if (srcType instanceof ParameterizedType) {
1✔
195
        parentAsType = translateParentTypeVars((ParameterizedType) srcType, clazz, parentAsType);
1✔
196
      }
197
      if (declaringClass == parentAsClass) {
1✔
198
        for (int i = 0; i < parentTypeVars.length; i++) {
1✔
199
          if (typeVar.equals(parentTypeVars[i])) {
1✔
200
            Type actualType = parentAsType.getActualTypeArguments()[i];
1✔
201
            return actualType instanceof TypeVariable<?> ? Object.class : actualType;
1✔
202
          }
203
        }
204
      }
205
      if (declaringClass.isAssignableFrom(parentAsClass)) {
1!
206
        return resolveTypeVar(typeVar, parentAsType, declaringClass);
1✔
207
      }
208
    } else if (superclass instanceof Class && declaringClass.isAssignableFrom((Class<?>) superclass)) {
1✔
209
      return resolveTypeVar(typeVar, superclass, declaringClass);
1✔
210
    }
211
    return null;
1✔
212
  }
213

214
  private static ParameterizedType translateParentTypeVars(ParameterizedType srcType, Class<?> srcClass,
215
      ParameterizedType parentType) {
216
    Type[] parentTypeArgs = parentType.getActualTypeArguments();
1✔
217
    Type[] srcTypeArgs = srcType.getActualTypeArguments();
1✔
218
    TypeVariable<?>[] srcTypeVars = srcClass.getTypeParameters();
1✔
219
    Type[] newParentArgs = new Type[parentTypeArgs.length];
1✔
220
    boolean noChange = true;
1✔
221
    for (int i = 0; i < parentTypeArgs.length; i++) {
1✔
222
      if (parentTypeArgs[i] instanceof TypeVariable) {
1✔
223
        for (int j = 0; j < srcTypeVars.length; j++) {
1✔
224
          if (srcTypeVars[j].equals(parentTypeArgs[i])) {
1✔
225
            noChange = false;
1✔
226
            newParentArgs[i] = srcTypeArgs[j];
1✔
227
          }
228
        }
229
      } else {
230
        newParentArgs[i] = parentTypeArgs[i];
1✔
231
      }
232
    }
233
    return noChange ? parentType : new ParameterizedTypeImpl((Class<?>) parentType.getRawType(), null, newParentArgs);
1!
234
  }
235

236
  private TypeParameterResolver() {
237
    super();
238
  }
239

240
  static class ParameterizedTypeImpl implements ParameterizedType {
241
    private final Class<?> rawType;
242

243
    private final Type ownerType;
244

245
    private final Type[] actualTypeArguments;
246

247
    public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
248
      super();
1✔
249
      this.rawType = rawType;
1✔
250
      this.ownerType = ownerType;
1✔
251
      this.actualTypeArguments = actualTypeArguments;
1✔
252
    }
1✔
253

254
    @Override
255
    public Type[] getActualTypeArguments() {
256
      return actualTypeArguments;
1✔
257
    }
258

259
    @Override
260
    public Type getOwnerType() {
261
      return ownerType;
1✔
262
    }
263

264
    @Override
265
    public Type getRawType() {
266
      return rawType;
1✔
267
    }
268

269
    @Override
270
    public int hashCode() {
271
      return (ownerType == null ? 0 : ownerType.hashCode()) ^ Arrays.hashCode(actualTypeArguments) ^ rawType.hashCode();
1!
272
    }
273

274
    @Override
275
    public boolean equals(Object obj) {
276
      if (!(obj instanceof ParameterizedType)) {
1!
277
        return false;
×
278
      }
279
      ParameterizedType other = (ParameterizedType) obj;
1✔
280
      return rawType.equals(other.getRawType()) && Objects.equals(ownerType, other.getOwnerType())
1!
281
          && Arrays.equals(actualTypeArguments, other.getActualTypeArguments());
1!
282
    }
283

284
    @Override
285
    public String toString() {
286
      StringBuilder s = new StringBuilder().append(rawType.getName()).append("<");
1✔
287
      for (int i = 0; i < actualTypeArguments.length; i++) {
1✔
288
        if (i > 0) {
1✔
289
          s.append(", ");
1✔
290
        }
291
        s.append(actualTypeArguments[i].getTypeName());
1✔
292
      }
293
      return s.append(">").toString();
1✔
294
    }
295
  }
296

297
  static class WildcardTypeImpl implements WildcardType {
298
    private final Type[] lowerBounds;
299

300
    private final Type[] upperBounds;
301

302
    WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
303
      super();
1✔
304
      this.lowerBounds = lowerBounds;
1✔
305
      this.upperBounds = upperBounds;
1✔
306
    }
1✔
307

308
    @Override
309
    public Type[] getLowerBounds() {
310
      return lowerBounds;
1✔
311
    }
312

313
    @Override
314
    public Type[] getUpperBounds() {
315
      return upperBounds;
1✔
316
    }
317

318
    @Override
319
    public int hashCode() {
320
      final int prime = 31;
1✔
321
      int result = 1;
1✔
322
      result = prime * result + Arrays.hashCode(lowerBounds);
1✔
323
      result = prime * result + Arrays.hashCode(upperBounds);
1✔
324
      return result;
1✔
325
    }
326

327
    @Override
328
    public boolean equals(Object obj) {
329
      if (this == obj) {
×
330
        return true;
×
331
      }
332
      if (!(obj instanceof WildcardTypeImpl)) {
×
333
        return false;
×
334
      }
335
      WildcardTypeImpl other = (WildcardTypeImpl) obj;
×
336
      return Arrays.equals(lowerBounds, other.lowerBounds) && Arrays.equals(upperBounds, other.upperBounds);
×
337
    }
338

339
    @Override
340
    public String toString() {
341
      StringBuilder s = new StringBuilder().append("?");
1✔
342
      if (lowerBounds.length > 0) {
1!
NEW
343
        s.append(" super ").append(lowerBounds[0].getTypeName());
×
344
      } else if (upperBounds.length > 0 && upperBounds[0] != Object.class) {
1!
345
        s.append(" extends ").append(upperBounds[0].getTypeName());
1✔
346
      }
347
      return s.toString();
1✔
348
    }
349
  }
350

351
  static class GenericArrayTypeImpl implements GenericArrayType {
352
    private final Type genericComponentType;
353

354
    GenericArrayTypeImpl(Type genericComponentType) {
355
      super();
1✔
356
      this.genericComponentType = genericComponentType;
1✔
357
    }
1✔
358

359
    @Override
360
    public Type getGenericComponentType() {
361
      return genericComponentType;
1✔
362
    }
363

364
    @Override
365
    public int hashCode() {
366
      return Objects.hash(genericComponentType);
×
367
    }
368

369
    @Override
370
    public boolean equals(Object obj) {
371
      if (this == obj) {
×
372
        return true;
×
373
      }
374
      if (!(obj instanceof GenericArrayTypeImpl)) {
×
375
        return false;
×
376
      }
377
      GenericArrayTypeImpl other = (GenericArrayTypeImpl) obj;
×
378
      return Objects.equals(genericComponentType, other.genericComponentType);
×
379
    }
380

381
    @Override
382
    public String toString() {
383
      return new StringBuilder().append(genericComponentType.toString()).append("[]").toString();
1✔
384
    }
385
  }
386
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc