blob: 6657f4db83173f4278863a2c60af84bdfd4da22e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.webbeans.util;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
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.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.webbeans.config.OwbParametrizedTypeImpl;
/**
* Utility classes for generic type operations.
*/
public final class GenericsUtil
{
public static boolean satisfiesDependency(boolean isDelegate, Type injectionPointType, Type beanType)
{
if (beanType instanceof TypeVariable || beanType instanceof WildcardType || beanType instanceof GenericArrayType)
{
return isAssignableFrom(isDelegate, injectionPointType, beanType);
}
else
{
Type injectionPointRawType = injectionPointType instanceof ParameterizedType? ((ParameterizedType)injectionPointType).getRawType(): injectionPointType;
Type beanRawType = beanType instanceof ParameterizedType? ((ParameterizedType)beanType).getRawType(): beanType;
if (ClassUtil.isSame(injectionPointRawType, beanRawType))
{
return isAssignableFrom(isDelegate, injectionPointType, beanType);
}
}
return false;
}
/**
* 5.2.3 and 5.2.4
*/
public static boolean isAssignableFrom(boolean isDelegate, Type requiredType, Type beanType)
{
if (requiredType instanceof Class)
{
return isAssignableFrom(isDelegate, (Class<?>)requiredType, beanType);
}
else if (requiredType instanceof ParameterizedType)
{
return isAssignableFrom(isDelegate, (ParameterizedType)requiredType, beanType);
}
else if (requiredType instanceof TypeVariable)
{
return isAssignableFrom(isDelegate, (TypeVariable<?>)requiredType, beanType);
}
else if (requiredType instanceof GenericArrayType)
{
return isAssignableFrom(isDelegate, (GenericArrayType)requiredType, beanType);
}
else if (requiredType instanceof WildcardType)
{
return isAssignableFrom(isDelegate, (WildcardType)requiredType, beanType);
}
else
{
throw new IllegalArgumentException("Unsupported type " + requiredType.getClass());
}
}
private static boolean isAssignableFrom(boolean isDelegate, Class<?> injectionPointType, Type beanType)
{
if (beanType instanceof Class)
{
return isAssignableFrom(isDelegate, injectionPointType, (Class<?>)beanType);
}
else if (beanType instanceof TypeVariable)
{
return isAssignableFrom(isDelegate, injectionPointType, (TypeVariable<?>)beanType);
}
else if (beanType instanceof ParameterizedType)
{
return isAssignableFrom(isDelegate, injectionPointType, (ParameterizedType)beanType);
}
else if (beanType instanceof GenericArrayType)
{
return isAssignableFrom(isDelegate, injectionPointType, (GenericArrayType)beanType);
}
else if (beanType instanceof WildcardType)
{
return isAssignableFrom(isDelegate, (Type)injectionPointType, (WildcardType)beanType);
}
else
{
throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass());
}
}
private static boolean isAssignableFrom(boolean isDelegate, Class<?> injectionPointType, Class<?> beanType)
{
return ClassUtil.isClassAssignable(injectionPointType, beanType);
}
private static boolean isAssignableFrom(boolean isDelegate, Class<?> injectionPointType, TypeVariable<?> beanType)
{
for (Type bounds: beanType.getBounds())
{
if (isAssignableFrom(isDelegate, injectionPointType, bounds))
{
return true;
}
}
return false;
}
/**
* CDI Spec. 5.2.4: "A parameterized bean type is considered assignable to a raw required type
* if the raw types are identical and all type parameters of the bean type are either unbounded type variables or java.lang.Object."
*/
private static boolean isAssignableFrom(boolean isDelegate, Class<?> injectionPointType, ParameterizedType beanType)
{
if (beanType.getRawType() != injectionPointType)
{
return false; //raw types don't match
}
for (Type typeArgument: beanType.getActualTypeArguments())
{
if (typeArgument == Object.class)
{
continue;
}
if (!(typeArgument instanceof TypeVariable))
{
return false; //neither object nor type variable
}
TypeVariable<?> typeVariable = (TypeVariable<?>)typeArgument;
for (Type bounds: typeVariable.getBounds())
{
if (bounds != Object.class)
{
return false; //bound type variable
}
}
}
return true;
}
private static boolean isAssignableFrom(boolean isDelegate, Class<?> injectionPointType, GenericArrayType beanType)
{
if (!injectionPointType.isArray())
{
return false;
}
return isAssignableFrom(isDelegate, injectionPointType.getComponentType(), beanType.getGenericComponentType());
}
private static boolean isAssignableFrom(boolean isDelegate, Type injectionPointType, WildcardType beanType)
{
for (Type bounds: beanType.getLowerBounds())
{
if (!isAssignableFrom(isDelegate, bounds, injectionPointType))
{
return false;
}
}
for (Type bounds: beanType.getUpperBounds())
{
if (isAssignableFrom(isDelegate, injectionPointType, bounds))
{
return true;
}
}
return false;
}
private static boolean isAssignableFrom(boolean isDelegate, ParameterizedType injectionPointType, Type beanType)
{
if (beanType instanceof Class)
{
return isAssignableFrom(isDelegate, injectionPointType, (Class<?>)beanType);
}
else if (beanType instanceof TypeVariable)
{
return isAssignableFrom(isDelegate, injectionPointType, (TypeVariable<?>)beanType);
}
else if (beanType instanceof ParameterizedType)
{
return isAssignableFrom(isDelegate, injectionPointType, (ParameterizedType)beanType);
}
else if (beanType instanceof WildcardType)
{
return isAssignableFrom(isDelegate, (Type)injectionPointType, (WildcardType)beanType);
}
else if (beanType instanceof GenericArrayType)
{
return false;
}
else
{
throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass());
}
}
private static boolean isAssignableFrom(boolean isDelegate, ParameterizedType injectionPointType, Class<?> beanType)
{
return isAssignableFrom(isDelegate, injectionPointType.getRawType(), beanType);
}
private static boolean isAssignableFrom(boolean isDelegate, ParameterizedType injectionPointType, TypeVariable<?> beanType)
{
for (Type bounds: beanType.getBounds())
{
if (isAssignableFrom(isDelegate, injectionPointType, bounds))
{
return true;
}
}
return false;
}
/**
* CDI Spec. 5.2.4
*/
private static boolean isAssignableFrom(boolean isDelegate, ParameterizedType injectionPointType, ParameterizedType beanType)
{
if (injectionPointType.getRawType() != beanType.getRawType())
{
return false;
}
Type[] injectionPointTypeArguments = injectionPointType.getActualTypeArguments();
Type[] beanTypeArguments = beanType.getActualTypeArguments();
for (int i = 0; i < injectionPointTypeArguments.length; i++)
{
Type injectionPointTypeArgument = injectionPointTypeArguments[i];
Type beanTypeArgument = beanTypeArguments[i];
// for this special case it's actually an 'assignable to', thus we swap the params, see CDI-389
// but this special rule does not apply to Delegate injection points...
if (!isDelegate &&
injectionPointTypeArgument instanceof Class &&
beanTypeArgument instanceof TypeVariable)
{
for (Type upperBound: ((TypeVariable) beanTypeArgument).getBounds())
{
if (!isAssignableFrom(isDelegate, upperBound, injectionPointTypeArgument))
{
return false;
}
}
}
else if (!isAssignableFrom(isDelegate, injectionPointTypeArgument, beanTypeArgument))
{
return false;
}
}
return true;
}
private static boolean isAssignableFrom(boolean isDelegate, TypeVariable<?> injectionPointType, Type beanType)
{
for (Type bounds: injectionPointType.getBounds())
{
if (!isAssignableFrom(isDelegate, bounds, beanType))
{
return false;
}
}
return true;
}
private static boolean isAssignableFrom(boolean isDelegate, GenericArrayType injectionPointType, Type beanType)
{
throw new UnsupportedOperationException("Not yet implementeds");
}
private static boolean isAssignableFrom(boolean isDelegate, WildcardType injectionPointType, Type beanType)
{
for (Type bounds: injectionPointType.getLowerBounds())
{
if (!isAssignableFrom(isDelegate, beanType, bounds))
{
return false;
}
}
for (Type bounds: injectionPointType.getUpperBounds())
{
if (!isAssignableFrom(isDelegate, bounds, beanType))
{
return false;
}
}
return true;
}
/**
* Resolves the actual type of the specified field for the type hierarchy specified by the given subclass
*/
public static Type resolveType(Class<?> subclass, Field field)
{
return resolveType(field.getGenericType(), new TypeVariableResolver(subclass, field.getDeclaringClass()));
}
/**
* Resolves the actual return type of the specified method for the type hierarchy specified by the given subclass
*/
public static Type resolveReturnType(Class<?> subclass, Method method)
{
return resolveType(method.getGenericReturnType(), new TypeVariableResolver(subclass, method.getDeclaringClass()));
}
/**
* Resolves the actual parameter types of the specified constructor for the type hierarchy specified by the given subclass
*/
public static Type[] resolveParameterTypes(Class<?> subclass, Constructor<?> constructor)
{
return resolveTypes(constructor.getGenericParameterTypes(), new TypeVariableResolver(subclass, constructor.getDeclaringClass()));
}
/**
* Resolves the actual parameter types of the specified method for the type hierarchy specified by the given subclass
*/
public static Type[] resolveParameterTypes(Class<?> subclass, Method method)
{
return resolveTypes(method.getGenericParameterTypes(), new TypeVariableResolver(subclass, method.getDeclaringClass()));
}
/**
* Resolves the actual type of the specified type for the type hierarchy specified by the given subclass
*/
public static Type resolveType(Type type, Class<?> subclass, Member member)
{
return resolveType(type, new TypeVariableResolver(subclass, member.getDeclaringClass()));
}
private static Type resolveType(Type type, TypeVariableResolver resolver)
{
if (type instanceof Class)
{
return type;
}
else if (type instanceof ParameterizedType)
{
ParameterizedType parameterizedType = (ParameterizedType)type;
Type[] resolvedTypes = resolveTypes(parameterizedType.getActualTypeArguments(), resolver);
return new OwbParametrizedTypeImpl(parameterizedType.getOwnerType(), parameterizedType.getRawType(), resolvedTypes);
}
else if (type instanceof TypeVariable)
{
TypeVariable<?> variable = (TypeVariable<?>)type;
return resolver.resolve(variable);
}
else if (type instanceof WildcardType)
{
WildcardType wildcardType = (WildcardType) type;
if (wildcardType.getLowerBounds().length > 0)
{
return type;
}
Type[] resolvedTypes = resolveTypes(wildcardType.getUpperBounds(), resolver);
return resolveType(getMostSpecificType(getRawTypes(resolvedTypes, resolver), resolvedTypes), resolver);
}
else if (type instanceof GenericArrayType)
{
Type componentType = resolveType(((GenericArrayType)type).getGenericComponentType(), resolver);
Class<?> componentClass = getRawType(componentType, resolver);
return Array.newInstance(componentClass, 0).getClass();
}
else
{
throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
}
}
public static Type[] resolveTypes(Type[] types, TypeVariableResolver resolution)
{
Type[] resolvedTypeArguments = new Type[types.length];
for (int i = 0; i < types.length; i++)
{
resolvedTypeArguments[i] = resolveType(types[i], resolution);
}
return resolvedTypeArguments;
}
public static Set<Type> getTypeClosure(Type type, Class<?> owningClass, Class<?> declaringClass)
{
Set<Type> typeClosure = new HashSet<Type>();
typeClosure.add(Object.class);
fillTypeHierarchy(typeClosure, type, new TypeVariableResolver(owningClass, declaringClass));
return typeClosure;
}
private static void fillTypeHierarchy(Set<Type> set, Type type, TypeVariableResolver resolver)
{
if (type == null)
{
return;
}
Type resolvedType = GenericsUtil.resolveType(type, resolver);
set.add(resolvedType);
Class<?> resolvedClass = GenericsUtil.getRawType(resolvedType, resolver);
if (resolvedClass.getSuperclass() != null)
{
fillTypeHierarchy(set, resolvedClass.getGenericSuperclass(), resolver.add(resolvedClass));
}
for (Type interfaceType: resolvedClass.getGenericInterfaces())
{
fillTypeHierarchy(set, interfaceType, resolver.add(resolvedClass, interfaceType));
}
}
static <T> Class<T> getRawType(Type type, TypeVariableResolver resolver)
{
if (type instanceof Class)
{
return (Class<T>)type;
}
else if (type instanceof ParameterizedType)
{
return getRawType(((ParameterizedType) type).getRawType(), resolver);
}
else if ((type instanceof TypeVariable) || (type instanceof WildcardType) || (type instanceof GenericArrayType))
{
Type resolvedType = resolveType(type, resolver);
if (resolvedType instanceof TypeVariable)
{
TypeVariable<?> variable = (TypeVariable<?>)resolvedType;
return getRawType(resolveType(getRawType(variable.getBounds(), resolver), resolver), resolver);
}
else
{
return getRawType(resolvedType, resolver);
}
}
else
{
throw new IllegalArgumentException("Unsupported type " + type.getClass().getName());
}
}
private static Type getRawType(Type[] types, TypeVariableResolver resolver)
{
Class<?>[] rawTypes = getRawTypes(types, resolver);
Class<?>[] classTypes = getClassTypes(rawTypes);
if (classTypes.length > 0)
{
return getMostSpecificType(classTypes, types);
}
else
{
return getMostSpecificType(rawTypes, types);
}
}
private static <T> Class<T>[] getRawTypes(Type[] types, TypeVariableResolver resolver)
{
Class<T>[] rawTypes = new Class[types.length];
for (int i = 0; i < types.length; i++)
{
rawTypes[i] = getRawType(types[i], resolver);
}
return rawTypes;
}
private static Type getMostSpecificType(Class<?>[] types, Type[] genericTypes)
{
Class<?> mostSpecificType = types[0];
int mostSpecificIndex = 0;
for (int i = 0; i < types.length; i++)
{
if (mostSpecificType.isAssignableFrom(types[i]))
{
mostSpecificType = types[i];
mostSpecificIndex = i;
}
}
return genericTypes[mostSpecificIndex];
}
private static Class<?>[] getClassTypes(Class<?>[] rawTypes)
{
List<Class<?>> classTypes = new ArrayList<Class<?>>();
for (Class<?> rawType : rawTypes)
{
if (!rawType.isInterface())
{
classTypes.add(rawType);
}
}
return classTypes.toArray(new Class[classTypes.size()]);
}
/**
* resolves actual types of a TypeVariable for a specific type hierarchy
*/
private static class TypeVariableResolver
{
private List<TypeVariableDeclaration> declarations = new ArrayList<TypeVariableDeclaration>();
private TypeVariableResolver(List<TypeVariableDeclaration> implementation)
{
declarations = implementation;
}
public TypeVariableResolver(Class<?> subclass, Class<?> declaringClass)
{
declarations.add(new TypeVariableDeclaration(subclass, subclass.getGenericSuperclass()));
while (declaringClass != subclass && declaringClass.isAssignableFrom(subclass))
{
subclass = subclass.getSuperclass();
declarations.add(new TypeVariableDeclaration(subclass, subclass.getGenericSuperclass()));
}
}
public Type resolve(TypeVariable<?> variable)
{
if (declarations.size() < 2)
{
return variable;
//X TODO better handling needed: return getRawType(variable.getBounds(), this);
}
int hierarchyIndex = declarations.size() - 1;
TypeVariableDeclaration typeVariableImplementation = declarations.get(hierarchyIndex);
TypeVariable<?>[] typeParameters = typeVariableImplementation.getDeclaredTypeParameters();
int typeIndex = -1;
for (int i = 0; i < typeParameters.length; i++)
{
if (variable.getName().equals(typeParameters[i].getName()))
{
typeIndex = i;
break;
}
}
if (typeIndex == -1)
{
// type erasure
return Object.class;
}
TypeVariableDeclaration declaration = declarations.get(hierarchyIndex - 1);
Type genericClass = declaration.getAssignment();
if (genericClass instanceof ParameterizedType)
{
ParameterizedType classType = (ParameterizedType)genericClass;
final Type[] actualTypeArguments = classType.getActualTypeArguments();
if (actualTypeArguments.length > typeIndex)
{
return resolveType(actualTypeArguments[typeIndex], remove());
}
else
{
return Object.class;
}
}
else
{
TypeVariable<?>[] typeVariables = declaration.getDeclaredTypeParameters();
if (typeVariables.length > typeIndex)
{
return resolveType(typeVariables[typeIndex], remove());
}
else
{
return Object.class; //type erasure
}
}
}
public TypeVariableResolver add(Class<?> type)
{
return add(type, type.getGenericSuperclass());
}
public TypeVariableResolver add(Class<?> declaringClass, Type assignment)
{
List<TypeVariableDeclaration> declarations = new ArrayList<TypeVariableDeclaration>(this.declarations);
declarations.add(new TypeVariableDeclaration(declaringClass, assignment));
return new TypeVariableResolver(declarations);
}
public TypeVariableResolver remove()
{
List<TypeVariableDeclaration> declarations = new ArrayList<TypeVariableDeclaration>(this.declarations);
declarations.remove(declarations.size() - 1);
return new TypeVariableResolver(declarations);
}
}
/**
* A declaration of type variables along with its assignments
*/
private static class TypeVariableDeclaration
{
private Class<?> declaringClass;
private Type assignment;
public TypeVariableDeclaration(Class<?> declaringClass, Type assignment)
{
this.declaringClass = declaringClass;
this.assignment = assignment;
}
public Type getAssignment()
{
return assignment;
}
public TypeVariable<?>[] getDeclaredTypeParameters()
{
return declaringClass.getTypeParameters();
}
}
private static class TypeErasureException extends Exception
{
public TypeErasureException()
{
super("generic type information not available");
}
}
}