/* * Copyright 2017 the original author or authors. * * 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.gradle.api.reflect; import com.google.common.base.Function; import org.gradle.api.Incubating; import org.gradle.api.Nullable; import org.gradle.internal.Cast; import org.gradle.model.internal.type.ModelType; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import static com.google.common.collect.ImmutableList.copyOf; import static com.google.common.collect.Iterables.transform; import static java.util.Arrays.asList; /** * Provides a way to preserve high-fidelity {@link Type} information on generic types. * * Capture a generic type with an anonymous subclass. For example: <pre> {@code * new TypeOf<NamedDomainObjectContainer<ArtifactRepository>>() {}}</pre> * * @param <T> Parameterized type * @since 3.5 */ @Incubating public abstract class TypeOf<T> { /** * Creates an instance of {@literal TypeOf} for the given {@literal Class}. * * @param type the {@literal Class} * @param <T> the parameterized type of the given {@literal Class} * @return the {@literal TypeOf} that captures the generic type of the given {@literal Class} */ public static <T> TypeOf<T> typeOf(Class<T> type) { return new TypeOf<T>( ModelType.of(typeWhichCannotBeNull(type))) { }; } /** * Creates an instance of {@literal TypeOf} for the given {@literal Type}. * * @param type the {@literal Type} * @param <T> the parameterized type of the given {@literal Type} * @return the {@literal TypeOf} that captures the generic type of the given {@literal Type} */ public static <T> TypeOf<T> typeOf(Type type) { return new TypeOf<T>( Cast.<ModelType<T>>uncheckedCast( ModelType.of(typeWhichCannotBeNull(type)))) { }; } /** * Constructs a new parameterized type from a given parameterized type definition and an array of type arguments. * * For example, {@code parameterizedTypeOf(new TypeOf<List<?>>() {}, new TypeOf<String>() {})} is equivalent to * {@code new TypeOf<List<String>>() {}}, except both the parameterized type definition and type arguments can be dynamically computed. * * @param parameterizedType the parameterized type from which to construct the new parameterized type * @param typeArguments the arguments with which to construct the new parameterized type * @see #isParameterized() */ public static TypeOf<?> parameterizedTypeOf(TypeOf<?> parameterizedType, TypeOf<?>... typeArguments) { ModelType<?> parameterizedModelType = parameterizedType.type; if (!parameterizedModelType.isParameterized()) { throw new IllegalArgumentException("Expecting a parameterized type, got: " + parameterizedType + "."); } return typeOf(parameterizedModelType.withArguments(modelTypeListFrom(typeArguments))); } private final ModelType<T> type; private TypeOf(ModelType<T> type) { this.type = type; } protected TypeOf() { this.type = captureTypeArgument(); } /** * Queries whether this object represents a simple (non-composite) type, not an array and not a generic type. * * @return true if this object represents a simple type. */ public boolean isSimple() { return type.isClass() && !rawClass().isArray(); } /** * Queries whether this object represents a synthetic type as defined by {@link Class#isSynthetic()}. * * @return true if this object represents a synthetic type. */ public boolean isSynthetic() { return rawClass().isSynthetic(); } /** * Queries whether the type represented by this object is public ({@link java.lang.reflect.Modifier#isPublic(int)}). * * @see java.lang.reflect.Modifier#isPublic(int) * @see Class#getModifiers() */ public boolean isPublic() { return Modifier.isPublic(getModifiers()); } private int getModifiers() { return rawClass().getModifiers(); } /** * Queries whether this object represents an array, generic or otherwise. * * @return true if this object represents an array. * @see #getComponentType() */ public boolean isArray() { return type.isGenericArray() || (type.isClass() && rawClass().isArray()); } /** * Returns the component type of the array type this object represents. * * @return null if this object does not represent an array type. * @see #isArray() */ @Nullable public TypeOf<?> getComponentType() { return type.isGenericArray() ? typeOf(type.getComponentType()) : nullableTypeOf(rawClass().getComponentType()); } /** * Queries whether this object represents a parameterized type. * * @return true if this object represents a parameterized type. * @see #getParameterizedTypeDefinition() * @see #getActualTypeArguments() */ public boolean isParameterized() { return type.isParameterized(); } /** * Returns an object that represents the type from which this parameterized type was constructed. * * @see #isParameterized() */ public TypeOf<?> getParameterizedTypeDefinition() { return typeOf(type.getRawType()); } /** * Returns the list of type arguments used in the construction of this parameterized type. * * @see #isParameterized() */ public List<TypeOf<?>> getActualTypeArguments() { return typeOfListFrom(type.getTypeVariables()); } /** * Queries whether this object represents a wildcard type expression, such as * {@code ?}, {@code ? extends Number}, or {@code ? super Integer}. * * @return true if this object represents a wildcard type expression. * @see #getUpperBound() */ public boolean isWildcard() { return type.isWildcard(); } /** * Returns the first declared upper-bound of the wildcard type expression represented by this type. * * @return null if no upper-bound has been explicitly declared. */ @Nullable public TypeOf<?> getUpperBound() { return nullableTypeOf(type.getUpperBound()); } /** * Is this type assignable from the given type? * * @param type the given type * @return {@literal true} if this type is assignable from the given type, {@literal false otherwise} */ public final boolean isAssignableFrom(TypeOf<?> type) { return this.type.isAssignableFrom(type.type); } /** * Is this type assignable from the given type? * * @param type the given type * @return {@literal true} if this type is assignable from the given type, {@literal false otherwise} */ public final boolean isAssignableFrom(Type type) { return this.type.isAssignableFrom(ModelType.of(type)); } /** * Simple name. * * @return this type's simple name */ public String getSimpleName() { return type.getDisplayName(); } @Override public final String toString() { return type.toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TypeOf)) { return false; } TypeOf<?> typeOf = (TypeOf<?>) o; return type.equals(typeOf.type); } @Override public int hashCode() { return type.hashCode(); } private ModelType<T> captureTypeArgument() { Type genericSuperclass = getClass().getGenericSuperclass(); Type type = genericSuperclass instanceof ParameterizedType ? ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0] : Object.class; return Cast.uncheckedCast(ModelType.of(type)); } private Class<? super T> rawClass() { return type.getRawClass(); } private static <T> T typeWhichCannotBeNull(T type) { if (type == null) { throw new IllegalArgumentException("type cannot be null."); } return type; } private static List<ModelType<?>> modelTypeListFrom(TypeOf<?>[] typeOfs) { return map(asList(typeOfs), new Function<TypeOf<?>, ModelType<?>>() { @Override public ModelType<?> apply(TypeOf<?> it) { return it.type; } }); } private static List<TypeOf<?>> typeOfListFrom(List<ModelType<?>> modelTypes) { return map(modelTypes, new Function<ModelType<?>, TypeOf<?>>() { @Override public TypeOf<?> apply(ModelType<?> it) { return typeOf(it); } }); } private static <U> TypeOf<U> typeOf(ModelType<U> componentType) { return new TypeOf<U>(componentType) { }; } private TypeOf<?> nullableTypeOf(Class<?> type) { return type != null ? typeOf(type) : null; } private TypeOf<?> nullableTypeOf(ModelType<?> type) { return type != null ? typeOf(type) : null; } private static <T, U> List<U> map(Iterable<T> iterable, Function<T, U> function) { return copyOf(transform(iterable, function)); } }