| /******************************************************************************* |
| * Copyright (c) 2010, 2013 Sonatype, Inc. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Stuart McCulloch (Sonatype, Inc.) - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.sisu.inject; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.lang.reflect.WildcardType; |
| |
| import javax.inject.Qualifier; |
| |
| import com.google.inject.ImplementedBy; |
| import com.google.inject.Key; |
| import com.google.inject.ProvidedBy; |
| import com.google.inject.TypeLiteral; |
| |
| /** |
| * Utility methods for dealing with generic type arguments. |
| */ |
| public final class TypeArguments |
| { |
| // ---------------------------------------------------------------------- |
| // Constants |
| // ---------------------------------------------------------------------- |
| |
| private static final TypeLiteral<?>[] NO_TYPE_LITERALS = {}; |
| |
| private static final TypeLiteral<?> OBJECT_TYPE_LITERAL = TypeLiteral.get( Object.class ); |
| |
| // ---------------------------------------------------------------------- |
| // Constructors |
| // ---------------------------------------------------------------------- |
| |
| private TypeArguments() |
| { |
| // static utility class, not allowed to create instances |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Utility methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Get all type arguments from a generic type, for example {@code [Foo,Bar]} from {@code Map<Foo,Bar>}. |
| * |
| * @param typeLiteral The generic type |
| * @return Array of type arguments |
| */ |
| public static TypeLiteral<?>[] get( final TypeLiteral<?> typeLiteral ) |
| { |
| final Type type = typeLiteral.getType(); |
| if ( type instanceof ParameterizedType ) |
| { |
| final Type[] argumentTypes = ( (ParameterizedType) type ).getActualTypeArguments(); |
| final TypeLiteral<?>[] argumentLiterals = new TypeLiteral[argumentTypes.length]; |
| for ( int i = 0; i < argumentTypes.length; i++ ) |
| { |
| argumentLiterals[i] = expand( argumentTypes[i] ); |
| } |
| return argumentLiterals; |
| } |
| if ( type instanceof GenericArrayType ) |
| { |
| return new TypeLiteral[] { expand( ( (GenericArrayType) type ).getGenericComponentType() ) }; |
| } |
| return NO_TYPE_LITERALS; |
| } |
| |
| /** |
| * Get an indexed type argument from a generic type, for example {@code Bar} from {@code Map<Foo,Bar>}. |
| * |
| * @param typeLiteral The generic type |
| * @param index The argument index |
| * @return Indexed type argument; {@code TypeLiteral<Object>} if the given type is a raw class |
| */ |
| public static TypeLiteral<?> get( final TypeLiteral<?> typeLiteral, final int index ) |
| { |
| final Type type = typeLiteral.getType(); |
| if ( type instanceof ParameterizedType ) |
| { |
| return expand( ( (ParameterizedType) type ).getActualTypeArguments()[index] ); |
| } |
| if ( type instanceof GenericArrayType ) |
| { |
| if ( 0 == index ) |
| { |
| return expand( ( (GenericArrayType) type ).getGenericComponentType() ); |
| } |
| throw new ArrayIndexOutOfBoundsException( index ); |
| } |
| return OBJECT_TYPE_LITERAL; |
| } |
| |
| /** |
| * Determines if the sub-type can be converted to the generic super-type via an identity or widening conversion. |
| * |
| * @param superLiteral The generic super-type |
| * @param subLiteral The generic sub-type |
| * @return {@code true} if the sub-type can be converted to the generic super-type; otherwise {@code false} |
| * @see Class#isAssignableFrom(Class) |
| */ |
| public static boolean isAssignableFrom( final TypeLiteral<?> superLiteral, final TypeLiteral<?> subLiteral ) |
| { |
| final Class<?> superClazz = superLiteral.getRawType(); |
| if ( !superClazz.isAssignableFrom( subLiteral.getRawType() ) ) |
| { |
| return false; |
| } |
| final Type superType = superLiteral.getType(); |
| if ( superClazz == superType ) |
| { |
| return true; |
| } |
| if ( superType instanceof ParameterizedType ) |
| { |
| final Type resolvedType = subLiteral.getSupertype( superClazz ).getType(); |
| if ( resolvedType instanceof ParameterizedType ) |
| { |
| final Type[] superArgs = ( (ParameterizedType) superType ).getActualTypeArguments(); |
| final Type[] subArgs = ( (ParameterizedType) resolvedType ).getActualTypeArguments(); |
| return isAssignableFrom( superArgs, subArgs ); |
| } |
| } |
| else if ( superType instanceof GenericArrayType ) |
| { |
| final Type resolvedType = subLiteral.getSupertype( superClazz ).getType(); |
| if ( resolvedType instanceof GenericArrayType ) |
| { |
| final Type superComponent = ( (GenericArrayType) superType ).getGenericComponentType(); |
| final Type subComponent = ( (GenericArrayType) resolvedType ).getGenericComponentType(); |
| return isAssignableFrom( new Type[] { superComponent }, new Type[] { subComponent } ); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Determines if the given generic type represents a concrete type. |
| * |
| * @param literal The generic type |
| * @return {@code true} if the generic type is concrete; otherwise {@code false} |
| */ |
| public static boolean isConcrete( final TypeLiteral<?> literal ) |
| { |
| return isConcrete( literal.getRawType() ); |
| } |
| |
| /** |
| * Determines if the given raw type represents a concrete type. |
| * |
| * @param clazz The raw type |
| * @return {@code true} if the raw type is concrete; otherwise {@code false} |
| */ |
| public static boolean isConcrete( final Class<?> clazz ) |
| { |
| return !clazz.isInterface() && !Modifier.isAbstract( clazz.getModifiers() ); |
| } |
| |
| /** |
| * Determines if the given generic type represents an implicit binding. |
| * |
| * @param literal The generic type |
| * @return {@code true} if the generic type is implicit; otherwise {@code false} |
| */ |
| public static boolean isImplicit( final TypeLiteral<?> literal ) |
| { |
| return isImplicit( literal.getRawType() ); |
| } |
| |
| /** |
| * Determines if the given raw type represents an implicit binding. |
| * |
| * @param clazz The raw type |
| * @return {@code true} if the raw type is implicit; otherwise {@code false} |
| */ |
| public static boolean isImplicit( final Class<?> clazz ) |
| { |
| return isConcrete( clazz ) || clazz.isAnnotationPresent( ImplementedBy.class ) |
| || clazz.isAnnotationPresent( ProvidedBy.class ); |
| } |
| |
| /** |
| * Creates a special binding key for the given implicit type. |
| * |
| * @param clazz The implicit type |
| * @return Implicit binding key |
| */ |
| public static <T> Key<T> implicitKey( final Class<T> clazz ) |
| { |
| return Key.get( clazz, Implicit.class ); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Implementation types |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * {@link Qualifier} of bindings that should be treated as implicit. |
| */ |
| @Qualifier |
| @Retention( RetentionPolicy.RUNTIME ) |
| private static @interface Implicit |
| { |
| } |
| |
| // ---------------------------------------------------------------------- |
| // Implementation methods |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * Expands wild-card types where possible, for example {@code Bar} from {@code ? extends Bar}. |
| * |
| * @param type The generic type |
| * @return Widened type that is still assignment-compatible with the original. |
| */ |
| private static TypeLiteral<?> expand( final Type type ) |
| { |
| if ( type instanceof WildcardType ) |
| { |
| return TypeLiteral.get( ( (WildcardType) type ).getUpperBounds()[0] ); |
| } |
| if ( type instanceof TypeVariable<?> ) |
| { |
| return TypeLiteral.get( ( (TypeVariable<?>) type ).getBounds()[0] ); |
| } |
| return TypeLiteral.get( type ); |
| } |
| |
| /** |
| * Determines whether the resolved sub-type arguments can be assigned to their generic super-type arguments. |
| * |
| * @param superArgs The generic super-arguments |
| * @param subArgs The resolved sub-arguments |
| * @return {@code true} if all the super-arguments have assignable resolved arguments; otherwise {@code false} |
| */ |
| private static boolean isAssignableFrom( final Type[] superArgs, final Type[] subArgs ) |
| { |
| for ( int i = 0, len = Math.min( superArgs.length, subArgs.length ); i < len; i++ ) |
| { |
| final Type superType = superArgs[i]; |
| final Type subType = subArgs[i]; |
| |
| /* |
| * Implementations could have unbound type variables, such as ArrayList<T>. We want to support injecting |
| * MyList<T extends Number> into List<Double> which is why we reverse the following expanded arguments: |
| */ |
| if ( subType instanceof TypeVariable<?> && isAssignableFrom( expand( subType ), expand( superType ) ) ) |
| { |
| continue; |
| } |
| /* |
| * Interfaces can have wild-card types, such as List<? extends Number>. Note: we only check the initial |
| * upper-bound of the super-type against the resolved type (trading absolute accuracy for performance). |
| */ |
| if ( superType instanceof WildcardType || superType instanceof TypeVariable<?> ) |
| { |
| if ( !isAssignableFrom( expand( superType ), expand( subType ) ) ) |
| { |
| return false; |
| } |
| } |
| /* |
| * Non-wild-card arguments must be tested with equals: List<Number> is not assignable from List<Float>. |
| */ |
| else if ( !superType.equals( subType ) ) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |