• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 Mockito contributors
3  * This program is made available under the terms of the MIT License.
4  */
5 package org.mockito.internal.util.reflection;
6 
7 import java.lang.reflect.ParameterizedType;
8 import java.lang.reflect.Type;
9 
10 /**
11  * Attempts to extract generic type of given target base class or target interface
12  */
13 public class GenericTypeExtractor {
14 
15     /**
16      * Extract generic type of root class either from the target base class or from target base interface.
17      * Examples:
18      *  <p>
19      *  1. Foo implements IFoo[Integer]:
20      *      genericTypeOf(Foo.class, Object.class, IFoo.class) returns Integer
21      *  <p>
22      *  2. Foo extends BaseFoo[String]:
23      *      genericTypeOf(Foo.class, BaseFoo.class, IFoo.class) returns String
24      *  <p>
25      *  3. Foo extends BaseFoo; BaseFoo implements IFoo[String]:
26      *      genericTypeOf(Foo.class, BaseFoo.class, Object.class) returns String
27      *  <p>
28      *  Does not support nested generics, only supports single type parameter.
29      *
30      * @param rootClass - the root class that the search begins from
31      * @param targetBaseClass - if one of the classes in the root class' hierarchy extends this base class
32      *                        it will be used for generic type extraction
33      * @param targetBaseInterface - if one of the interfaces in the root class' hierarchy implements this interface
34      *                            it will be used for generic type extraction
35      * @return generic interface if found, Object.class if not found.
36      */
genericTypeOf(Class<?> rootClass, Class<?> targetBaseClass, Class<?> targetBaseInterface)37     public static Class<?> genericTypeOf(Class<?> rootClass, Class<?> targetBaseClass, Class<?> targetBaseInterface) {
38         //looking for candidates in the hierarchy of rootClass
39         Class<?> match = rootClass;
40         while(match != Object.class) {
41             //check the super class first
42             if (match.getSuperclass() == targetBaseClass) {
43                 return extractGeneric(match.getGenericSuperclass());
44             }
45             //check the interfaces (recursively)
46             Type genericInterface = findGenericInteface(match, targetBaseInterface);
47             if (genericInterface != null) {
48                 return extractGeneric(genericInterface);
49             }
50             //recurse the hierarchy
51             match = match.getSuperclass();
52         }
53         return Object.class;
54     }
55 
56     /**
57      * Finds generic interface implementation based on the source class and the target interface.
58      * Returns null if not found. Recurses the interface hierarchy.
59      */
findGenericInteface(Class<?> sourceClass, Class<?> targetBaseInterface)60     private static Type findGenericInteface(Class<?> sourceClass, Class<?> targetBaseInterface) {
61         for (int i = 0; i < sourceClass.getInterfaces().length; i++) {
62             Class<?> inter = sourceClass.getInterfaces()[i];
63             if (inter == targetBaseInterface) {
64                 return sourceClass.getGenericInterfaces()[0];
65             } else {
66                 Type deeper = findGenericInteface(inter, targetBaseInterface);
67                 if (deeper != null) {
68                     return deeper;
69                 }
70             }
71         }
72         return null;
73     }
74 
75     /**
76      * Attempts to extract generic parameter type of given type.
77      * If there is no generic parameter it returns Object.class
78      */
extractGeneric(Type type)79     private static Class<?> extractGeneric(Type type) {
80         if (type instanceof ParameterizedType) {
81             Type[] genericTypes = ((ParameterizedType) type).getActualTypeArguments();
82             if (genericTypes.length > 0 && genericTypes[0] instanceof Class) {
83                 return (Class<?>) genericTypes[0];
84             }
85         }
86         return Object.class;
87     }
88 }
89