package com.tracersoft.util.reflection;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Stack;
/**
* Alex Tracer (c) 2009
*/
public class ReflectionUtils {
/**
* Для некоторого класса (или интерфейса) определяет,
* каким классом был параметризован один из его предков (реализующих классов) с generic-параметрами.
*
* @param actualClass анализируемый класс
* @param genericClass класс (или интерфейс), для которого определяется значение параметра
* @param parameterIndex номер параметра
* @return класс, являющийся параметром с индексом parameterIndex в genericClass
*/
public static Class getGenericParameterClass(final Class actualClass, final Class genericClass, final int parameterIndex) {
// Прекращаем работу если genericClass не является предком actualClass.
if (!genericClass.isAssignableFrom(actualClass) || genericClass.equals(actualClass)) {
throw new IllegalArgumentException("Class " + genericClass.getName() + " is not a superclass of "
+ actualClass.getName() + ".");
}
final boolean isInterface = genericClass.isInterface();
// Нам нужно найти класс, для которого непосредственным родителем будет genericClass.
// Мы будем подниматься вверх по иерархии, пока не найдем интересующий нас класс.
// В процессе поднятия мы будем сохранять в genericClasses все классы - они нам понадобятся при спуске вниз.
// Проейденные классы - используются для спуска вниз.
Stack<ParameterizedType> genericClasses = new Stack<ParameterizedType>();
// clazz - текущий рассматриваемый класс
Class clazz = actualClass;
while (true) {
Type genericInterface = isInterface ? getGenericInterface(clazz, genericClass) : null;
Type currentType = genericInterface != null ? genericInterface : clazz.getGenericSuperclass();
boolean isParameterizedType = currentType instanceof ParameterizedType;
if (isParameterizedType) {
// Если предок - параметризованный класс, то запоминаем его - возможно он пригодится при спуске вниз.
genericClasses.push((ParameterizedType) currentType);
} else {
// В иерархии встретился непараметризованный класс. Все ранее сохраненные параметризованные классы будут бесполезны.
genericClasses.clear();
}
// Проверяем, дошли мы до нужного предка или нет.
Type rawType = isParameterizedType ? ((ParameterizedType) currentType).getRawType() : currentType;
if (!rawType.equals(genericClass)) {
// genericClass не является непосредственным родителем для текущего класса.
// Поднимаемся по иерархии дальше.
clazz = (Class) rawType;
} else {
// Мы поднялись до нужного класса. Останавливаемся.
break;
}
}
// Нужный класс найден. Теперь мы можем узнать, какими типами он параметризован.
Type result = genericClasses.pop().getActualTypeArguments()[parameterIndex];
while (result instanceof TypeVariable && !genericClasses.empty()) {
// Похоже наш параметр задан где-то ниже по иерархии, спускаемся вниз.
// Получаем индекс параметра в том классе, в котором он задан.
int actualArgumentIndex = getParameterTypeDeclarationIndex((TypeVariable) result);
// Берем соответствующий класс, содержащий метаинформацию о нашем параметре.
ParameterizedType type = genericClasses.pop();
// Получаем информацию о значении параметра.
result = type.getActualTypeArguments()[actualArgumentIndex];
}
if (result instanceof TypeVariable) {
// Мы спустились до самого низа, но даже там нужный параметр не имеет явного задания.
// Следовательно из-за "Type erasure" узнать класс для параметра невозможно.
throw new IllegalStateException("Unable to resolve type variable " + result + "."
+ " Try to replace instances of parametrized class with its non-parameterized subtype.");
}
if (result instanceof ParameterizedType) {
// Сам параметр оказался параметризованным.
// Отбросим информацию о его параметрах, она нам не нужна.
result = ((ParameterizedType) result).getRawType();
}
if (result == null) {
// Should never happen. :)
throw new IllegalStateException("Unable to determine actual parameter type for "
+ actualClass.getName() + ".");
}
if (!(result instanceof Class)) {
// Похоже, что параметр - массив, примитивный типи, интерфейс или еще-что-то, что не является классом.
throw new IllegalStateException("Actual parameter type for " + actualClass.getName() + " is not a Class.");
}
return (Class) result;
}
public static int getParameterTypeDeclarationIndex(final TypeVariable typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
// Ищем наш параметр среди всех параметров того класса, где определен нужный нам параметр.
TypeVariable[] typeVariables = genericDeclaration.getTypeParameters();
Integer actualArgumentIndex = null;
for (int i = 0; i < typeVariables.length; i++) {
if (typeVariables[i].equals(typeVariable)) {
actualArgumentIndex = i;
break;
}
}
if (actualArgumentIndex != null) {
return actualArgumentIndex;
} else {
throw new IllegalStateException("Argument " + typeVariable.toString() + " is not found in "
+ genericDeclaration.toString() + ".");
}
}
public static Type getGenericInterface(final Class sourceClass, final Class genericInterface) {
Type[] types = sourceClass.getGenericInterfaces();
for (Type type : types) {
if (type instanceof Class) {
if (genericInterface.isAssignableFrom((Class) type)) {
return type;
}
} else if (type instanceof ParameterizedType) {
if (genericInterface.isAssignableFrom((Class) ((ParameterizedType) type).getRawType())) {
return type;
}
}
}
return null;
}
}