176 lines
7.2 KiB
Java
176 lines
7.2 KiB
Java
/*
|
|
* Copyright (C) 2008 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package org.mcteam.factions.gson;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.GenericArrayType;
|
|
import java.lang.reflect.ParameterizedType;
|
|
import java.lang.reflect.Type;
|
|
import java.lang.reflect.TypeVariable;
|
|
import java.lang.reflect.WildcardType;
|
|
|
|
/**
|
|
* A static factory class used to construct the "TypeInfo" objects.
|
|
*
|
|
* @author Inderjeet Singh
|
|
* @author Joel Leitch
|
|
*/
|
|
final class TypeInfoFactory {
|
|
|
|
private TypeInfoFactory() {
|
|
// Not instantiable since it provides factory methods only.
|
|
}
|
|
|
|
public static TypeInfoArray getTypeInfoForArray(Type type) {
|
|
Preconditions.checkArgument(TypeUtils.isArray(type));
|
|
return new TypeInfoArray(type);
|
|
}
|
|
|
|
/**
|
|
* Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a
|
|
* "TypeVariable" in a parameterized type then it evaluates the real type.
|
|
*
|
|
* @param f the actual field object to retrieve the type from
|
|
* @param typeDefiningF the type that contains the field {@code f}
|
|
* @return the type information for the field
|
|
*/
|
|
public static TypeInfo getTypeInfoForField(Field f, Type typeDefiningF) {
|
|
Class<?> classDefiningF = TypeUtils.toRawClass(typeDefiningF);
|
|
Type type = f.getGenericType();
|
|
Type actualType = getActualType(type, typeDefiningF, classDefiningF);
|
|
return new TypeInfo(actualType);
|
|
}
|
|
|
|
private static Type getActualType(
|
|
Type typeToEvaluate, Type parentType, Class<?> rawParentClass) {
|
|
if (typeToEvaluate instanceof Class<?>) {
|
|
return typeToEvaluate;
|
|
} else if (typeToEvaluate instanceof ParameterizedType) {
|
|
ParameterizedType castedType = (ParameterizedType) typeToEvaluate;
|
|
Type owner = castedType.getOwnerType();
|
|
Type[] actualTypeParameters =
|
|
extractRealTypes(castedType.getActualTypeArguments(), parentType, rawParentClass);
|
|
Type rawType = castedType.getRawType();
|
|
return new ParameterizedTypeImpl(rawType, actualTypeParameters, owner);
|
|
} else if (typeToEvaluate instanceof GenericArrayType) {
|
|
GenericArrayType castedType = (GenericArrayType) typeToEvaluate;
|
|
Type componentType = castedType.getGenericComponentType();
|
|
Type actualType = getActualType(componentType, parentType, rawParentClass);
|
|
if (componentType.equals(actualType)) {
|
|
return castedType;
|
|
}
|
|
return actualType instanceof Class<?> ?
|
|
TypeUtils.wrapWithArray(TypeUtils.toRawClass(actualType))
|
|
: new GenericArrayTypeImpl(actualType);
|
|
} else if (typeToEvaluate instanceof TypeVariable<?>) {
|
|
if (parentType instanceof ParameterizedType) {
|
|
// The class definition has the actual types used for the type variables.
|
|
// Find the matching actual type for the Type Variable used for the field.
|
|
// For example, class Foo<A> { A a; }
|
|
// new Foo<Integer>(); defines the actual type of A to be Integer.
|
|
// So, to find the type of the field a, we will have to look at the class'
|
|
// actual type arguments.
|
|
TypeVariable<?> fieldTypeVariable = (TypeVariable<?>) typeToEvaluate;
|
|
TypeVariable<?>[] classTypeVariables = rawParentClass.getTypeParameters();
|
|
ParameterizedType objParameterizedType = (ParameterizedType) parentType;
|
|
int indexOfActualTypeArgument = getIndex(classTypeVariables, fieldTypeVariable);
|
|
Type[] actualTypeArguments = objParameterizedType.getActualTypeArguments();
|
|
return actualTypeArguments[indexOfActualTypeArgument];
|
|
} else if (typeToEvaluate instanceof TypeVariable<?>) {
|
|
Type theSearchedType = null;
|
|
|
|
do {
|
|
theSearchedType = extractTypeForHierarchy(parentType, (TypeVariable<?>) typeToEvaluate);
|
|
} while ((theSearchedType != null) && (theSearchedType instanceof TypeVariable<?>));
|
|
|
|
if (theSearchedType != null) {
|
|
return theSearchedType;
|
|
}
|
|
}
|
|
|
|
throw new UnsupportedOperationException("Expecting parameterized type, got " + parentType
|
|
+ ".\n Are you missing the use of TypeToken idiom?\n See "
|
|
+ "http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener");
|
|
} else if (typeToEvaluate instanceof WildcardType) {
|
|
WildcardType castedType = (WildcardType) typeToEvaluate;
|
|
return getActualType(castedType.getUpperBounds()[0], parentType, rawParentClass);
|
|
} else {
|
|
throw new IllegalArgumentException("Type \'" + typeToEvaluate + "\' is not a Class, "
|
|
+ "ParameterizedType, GenericArrayType or TypeVariable. Can't extract type.");
|
|
}
|
|
}
|
|
|
|
private static Type extractTypeForHierarchy(Type parentType, TypeVariable<?> typeToEvaluate) {
|
|
Class<?> rawParentType = null;
|
|
if (parentType instanceof Class<?>) {
|
|
rawParentType = (Class<?>) parentType;
|
|
} else if (parentType instanceof ParameterizedType) {
|
|
ParameterizedType parentTypeAsPT = (ParameterizedType) parentType;
|
|
rawParentType = (Class<?>) parentTypeAsPT.getRawType();
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
Type superClass = rawParentType.getGenericSuperclass();
|
|
if (superClass instanceof ParameterizedType
|
|
&& ((ParameterizedType) superClass).getRawType() == typeToEvaluate.getGenericDeclaration()) {
|
|
// Evaluate type on this type
|
|
TypeVariable<?>[] classTypeVariables =
|
|
((Class<?>) ((ParameterizedType) superClass).getRawType()).getTypeParameters();
|
|
int indexOfActualTypeArgument = getIndex(classTypeVariables, typeToEvaluate);
|
|
|
|
Type[] actualTypeArguments = null;
|
|
if (parentType instanceof Class<?>) {
|
|
actualTypeArguments = ((ParameterizedType) superClass).getActualTypeArguments();
|
|
} else if (parentType instanceof ParameterizedType) {
|
|
actualTypeArguments = ((ParameterizedType) parentType).getActualTypeArguments();
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
return actualTypeArguments[indexOfActualTypeArgument];
|
|
}
|
|
|
|
Type searchedType = null;
|
|
if (superClass != null) {
|
|
searchedType = extractTypeForHierarchy(superClass, typeToEvaluate);
|
|
}
|
|
return searchedType;
|
|
}
|
|
|
|
private static Type[] extractRealTypes(
|
|
Type[] actualTypeArguments, Type parentType, Class<?> rawParentClass) {
|
|
Preconditions.checkNotNull(actualTypeArguments);
|
|
|
|
Type[] retTypes = new Type[actualTypeArguments.length];
|
|
for (int i = 0; i < actualTypeArguments.length; ++i) {
|
|
retTypes[i] = getActualType(actualTypeArguments[i], parentType, rawParentClass);
|
|
}
|
|
return retTypes;
|
|
}
|
|
|
|
private static int getIndex(TypeVariable<?>[] types, TypeVariable<?> type) {
|
|
for (int i = 0; i < types.length; ++i) {
|
|
if (type.equals(types[i])) {
|
|
return i;
|
|
}
|
|
}
|
|
throw new IllegalStateException(
|
|
"How can the type variable not be present in the class declaration!");
|
|
}
|
|
}
|