| /* |
| * 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"); |
| } |
| } |
| } |