com.google.sitebricks.conversion.generics.Generics Maven / Gradle / Ivy
The newest version!
/*
* Copied from Gentyref project http://code.google.com/p/gentyref/
* Code was reformatted and moved to fit package structure
*/
package com.google.sitebricks.conversion.generics;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Generics {
private static final Type UNBOUND_WILDCARD = new WildcardTypeImpl(new Type[] { Object.class },
new Type[] {});
/**
* Returns the erasure of the given type.
*/
public static Class> erase(Type type)
{
if (type instanceof Class>)
{
return (Class>) type;
}
else if (type instanceof ParameterizedType)
{
return (Class>) ((ParameterizedType) type).getRawType();
}
else if (type instanceof TypeVariable>)
{
TypeVariable> tv = (TypeVariable>) type;
if (tv.getBounds().length == 0)
return Object.class;
else
return erase(tv.getBounds()[0]);
}
else if (type instanceof GenericArrayType)
{
GenericArrayType aType = (GenericArrayType) type;
return GenericArrayTypeImpl.createArrayType(erase(aType.getGenericComponentType()));
}
else
{
// TODO at least support CaptureType here
throw new RuntimeException("not supported: " + type.getClass());
}
}
/**
* Maps type parameters in a type to their values.
*
* @param toMapType
* Type possibly containing type arguments
* @param typeAndParams
* must be either ParameterizedType, or (in case there are no
* type arguments, or it's a raw type) Class
* @return toMapType, but with type parameters from typeAndParams replaced.
*/
private static Type mapTypeParameters(Type toMapType, Type typeAndParams)
{
if (isMissingTypeParameters(typeAndParams))
{
return erase(toMapType);
}
else
{
VarMap varMap = new VarMap();
Type handlingTypeAndParams = typeAndParams;
while (handlingTypeAndParams instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType) handlingTypeAndParams;
Class> clazz = (Class>) pType.getRawType(); // getRawType
// should always
// be Class
varMap.addAll(clazz.getTypeParameters(), pType.getActualTypeArguments());
handlingTypeAndParams = pType.getOwnerType();
}
return varMap.map(toMapType);
}
}
/**
* Checks if the given type is a class that is supposed to have type
* parameters, but doesn't. In other words, if it's a really raw type.
*/
private static boolean isMissingTypeParameters(Type type)
{
if (type instanceof Class>)
{
for (Class> clazz = (Class>) type; clazz != null; clazz = clazz.getEnclosingClass())
{
if (clazz.getTypeParameters().length != 0)
return true;
}
return false;
}
else if (type instanceof ParameterizedType)
{
return false;
}
else
{
throw new AssertionError("Unexpected type " + type.getClass());
}
}
/**
* Returns a type representing the class, with all type parameters the
* unbound wildcard ("?"). For example,
* addWildcardParameters(Map.class) returns a type representing
* Map<?,?>.
*
* @return
* - If clazz is a class or interface without type parameters,
* clazz itself is returned.
* - If clazz is a class or interface with type parameters, an
* instance of ParameterizedType is returned.
* - if clazz is an array type, an array type is returned with
* unbound wildcard parameters added in the the component type.
*
*/
public static Type addWildcardParameters(Class> clazz)
{
if (clazz.isArray())
{
return GenericArrayTypeImpl.createArrayType(addWildcardParameters(clazz
.getComponentType()));
}
else if (isMissingTypeParameters(clazz))
{
TypeVariable>[] vars = clazz.getTypeParameters();
Type[] arguments = new Type[vars.length];
Arrays.fill(arguments, UNBOUND_WILDCARD);
Type owner = clazz.getDeclaringClass() == null ? null : addWildcardParameters(clazz
.getDeclaringClass());
return new ParameterizedTypeImpl(clazz, arguments, owner);
}
else
{
return clazz;
}
}
/**
* With type a supertype of searchClass, returns the exact supertype of the
* given class, including type parameters. For example, with
* class StringList implements List<String>,
* getExactSuperType(StringList.class, Collection.class) returns a
* {@link ParameterizedType} representing Collection<String>.
*
* - Returns null if searchClass is not a superclass of type.
* - Returns an instance of {@link Class} if type if it is a raw
* type, or has no type parameters
* - Returns an instance of {@link ParameterizedType} if the type does
* have parameters
* - Returns an instance of {@link GenericArrayType} if
* searchClass is an array type, and the actual type has type
* parameters
*
*/
public static Type getExactSuperType(Type type, Class> searchClass)
{
if (type instanceof ParameterizedType || type instanceof Class>
|| type instanceof GenericArrayType)
{
Class> clazz = erase(type);
if (searchClass == clazz)
{
return type;
}
if (!searchClass.isAssignableFrom(clazz))
return null;
}
for (Type superType : getExactDirectSuperTypes(type))
{
Type result = getExactSuperType(superType, searchClass);
if (result != null)
return result;
}
return null;
}
/**
* Gets the type parameter for a given type that is the value for a given
* type variable. For example, with
* class StringList implements List<String>,
* getTypeParameter(StringList.class, Collection.class.getTypeParameters()[0])
* returns String.
*
* @param type
* The type to inspect.
* @param variable
* The type variable to find the value for.
* @return The type parameter for the given variable. Or null if type is not
* a subtype of the type that declares the variable, or if the
* variable isn't known (because of raw types).
*/
public static Type getTypeParameter(Type type, TypeVariable extends Class>> variable)
{
Class> clazz = variable.getGenericDeclaration();
Type superType = getExactSuperType(type, clazz);
if (superType instanceof ParameterizedType)
{
int index = Arrays.asList(clazz.getTypeParameters()).indexOf(variable);
return ((ParameterizedType) superType).getActualTypeArguments()[index];
}
else
{
return null;
}
}
/**
* Checks if the capture of subType is a subtype of superType
*/
public static boolean isSuperType(Type superType, Type subType)
{
if (superType instanceof ParameterizedType || superType instanceof Class>
|| superType instanceof GenericArrayType)
{
Class> superClass = erase(superType);
Type mappedSubType = getExactSuperType(capture(subType), superClass);
if (mappedSubType == null)
{
return false;
}
else if (superType instanceof Class>)
{
return true;
}
else if (mappedSubType instanceof Class>)
{
// TODO treat supertype by being raw type differently
// ("supertype, but with warnings")
return true; // class has no parameters, or it's a raw type
}
else if (mappedSubType instanceof GenericArrayType)
{
Type superComponentType = getArrayComponentType(superType);
assert superComponentType != null;
Type mappedSubComponentType = getArrayComponentType(mappedSubType);
assert mappedSubComponentType != null;
return isSuperType(superComponentType, mappedSubComponentType);
}
else
{
assert mappedSubType instanceof ParameterizedType;
ParameterizedType pMappedSubType = (ParameterizedType) mappedSubType;
assert pMappedSubType.getRawType() == superClass;
ParameterizedType pSuperType = (ParameterizedType) superType;
Type[] superTypeArgs = pSuperType.getActualTypeArguments();
Type[] subTypeArgs = pMappedSubType.getActualTypeArguments();
assert superTypeArgs.length == subTypeArgs.length;
for (int i = 0; i < superTypeArgs.length; i++)
{
if (!contains(superTypeArgs[i], subTypeArgs[i]))
{
return false;
}
}
// params of the class itself match, so if the owner types are
// supertypes too, it's a supertype.
return pSuperType.getOwnerType() == null
|| isSuperType(pSuperType.getOwnerType(), pMappedSubType.getOwnerType());
}
}
else if (superType instanceof CaptureType)
{
if (superType.equals(subType))
return true;
for (Type lowerBound : ((CaptureType) superType).getLowerBounds())
{
if (isSuperType(lowerBound, subType))
{
return true;
}
}
return false;
}
else if (superType instanceof GenericArrayType)
{
return isArraySupertype(superType, subType);
}
else
{
throw new RuntimeException("not implemented: " + superType.getClass());
}
}
private static boolean isArraySupertype(Type arraySuperType, Type subType)
{
Type superTypeComponent = getArrayComponentType(arraySuperType);
assert superTypeComponent != null;
Type subTypeComponent = getArrayComponentType(subType);
if (subTypeComponent == null)
{ // subType is not an array type
return false;
}
else
{
return isSuperType(superTypeComponent, subTypeComponent);
}
}
/**
* If type is an array type, returns the type of the component of the array.
* Otherwise, returns null.
*/
public static Type getArrayComponentType(Type type)
{
if (type instanceof Class>)
{
Class> clazz = (Class>) type;
return clazz.getComponentType();
}
else if (type instanceof GenericArrayType)
{
GenericArrayType aType = (GenericArrayType) type;
return aType.getGenericComponentType();
}
else
{
return null;
}
}
private static boolean contains(Type containingType, Type containedType)
{
if (containingType instanceof WildcardType)
{
WildcardType wContainingType = (WildcardType) containingType;
for (Type upperBound : wContainingType.getUpperBounds())
{
if (!isSuperType(upperBound, containedType))
{
return false;
}
}
for (Type lowerBound : wContainingType.getLowerBounds())
{
if (!isSuperType(containedType, lowerBound))
{
return false;
}
}
return true;
}
else
{
return containingType.equals(containedType);
}
}
/**
* Returns the direct supertypes of the given type. Resolves type
* parameters.
*/
public static Type[] getExactDirectSuperTypes(Type type)
{
if (type instanceof ParameterizedType || type instanceof Class>)
{
Class> clazz;
if (type instanceof ParameterizedType)
{
clazz = (Class>) ((ParameterizedType) type).getRawType();
}
else
{
// TODO primitive types?
clazz = (Class>) type;
if (clazz.isArray())
return getArrayExactDirectSuperTypes(clazz);
}
Type[] superInterfaces = clazz.getGenericInterfaces();
Type superClass = clazz.getGenericSuperclass();
Type[] result;
int resultIndex;
if (superClass == null)
{
result = new Type[superInterfaces.length];
resultIndex = 0;
}
else
{
result = new Type[superInterfaces.length + 1];
resultIndex = 1;
result[0] = mapTypeParameters(superClass, type);
}
for (Type superInterface : superInterfaces)
{
result[resultIndex++] = mapTypeParameters(superInterface, type);
}
return result;
}
else if (type instanceof TypeVariable>)
{
TypeVariable> tv = (TypeVariable>) type;
return tv.getBounds();
}
else if (type instanceof WildcardType)
{
// This should be a rare case: normally this wildcard is already
// captured.
// But it does happen if the upper bound of a type variable contains
// a wildcard
// TODO shouldn't upper bound of type variable have been captured
// too? (making this case impossible?)
return ((WildcardType) type).getUpperBounds();
}
else if (type instanceof CaptureType)
{
return ((CaptureType) type).getUpperBounds();
}
else if (type instanceof GenericArrayType)
{
return getArrayExactDirectSuperTypes(type);
}
else
{
throw new RuntimeException("not implemented type: " + type);
}
}
private static Type[] getArrayExactDirectSuperTypes(Type arrayType)
{
// see
// http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.10.3
Type typeComponent = getArrayComponentType(arrayType);
Type[] result;
int resultIndex;
if (typeComponent instanceof Class> && ((Class>) typeComponent).isPrimitive())
{
resultIndex = 0;
result = new Type[3];
}
else
{
Type[] componentSupertypes = getExactDirectSuperTypes(typeComponent);
result = new Type[componentSupertypes.length + 3];
for (resultIndex = 0; resultIndex < componentSupertypes.length; resultIndex++)
{
result[resultIndex] = GenericArrayTypeImpl
.createArrayType(componentSupertypes[resultIndex]);
}
}
result[resultIndex++] = Object.class;
result[resultIndex++] = Cloneable.class;
result[resultIndex++] = Serializable.class;
return result;
}
/**
* Returns the exact return type of the given method in the given type. This
* may be different from m.getGenericReturnType() when the method
* was declared in a superclass, of type is a raw type.
*/
public static Type getExactReturnType(Method m, Type type)
{
Type returnType = m.getGenericReturnType();
Type exactDeclaringType = getExactSuperType(capture(type), m.getDeclaringClass());
return mapTypeParameters(returnType, exactDeclaringType);
}
/**
* Returns the exact type of the given field in the given type. This may be
* different from f.getGenericType() when the field was declared in
* a superclass, of type is a raw type.
*/
public static Type getExactFieldType(Field f, Type type)
{
Type returnType = f.getGenericType();
Type exactDeclaringType = getExactSuperType(capture(type), f.getDeclaringClass());
return mapTypeParameters(returnType, exactDeclaringType);
}
/**
* Applies capture conversion to the given type.
*/
public static Type capture(Type type)
{
VarMap varMap = new VarMap();
List toInit = new ArrayList();
if (type instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType) type;
Class> clazz = (Class>) pType.getRawType();
Type[] arguments = pType.getActualTypeArguments();
TypeVariable>[] vars = clazz.getTypeParameters();
Type[] capturedArguments = new Type[arguments.length];
assert arguments.length == vars.length;
for (int i = 0; i < arguments.length; i++)
{
Type argument = arguments[i];
if (argument instanceof WildcardType)
{
CaptureTypeImpl captured = new CaptureTypeImpl((WildcardType) argument, vars[i]);
argument = captured;
toInit.add(captured);
}
capturedArguments[i] = argument;
varMap.add(vars[i], argument);
}
for (CaptureTypeImpl captured : toInit)
{
captured.init(varMap);
}
Type ownerType = (pType.getOwnerType() == null) ? null : capture(pType.getOwnerType());
return new ParameterizedTypeImpl(clazz, capturedArguments, ownerType);
}
else
{
return type;
}
}
/**
* Returns the display name of a Type.
*/
public static String getTypeName(Type type)
{
if (type instanceof Class>)
{
Class> clazz = (Class>) type;
return clazz.isArray() ? (getTypeName(clazz.getComponentType()) + "[]") : clazz.getName();
}
else
{
return type.toString();
}
}
}