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