org.gradle.api.reflect.TypeOf Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 5.6 API redistribution.
/*
* 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.internal.Cast;
import org.gradle.model.internal.type.ModelType;
import javax.annotation.Nullable;
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: {@code
* new TypeOf>() {}}
*
* @param Parameterized type
* @since 3.5
*/
public abstract class TypeOf {
/**
* Creates an instance of {@literal TypeOf} for the given {@literal Class}.
*
* @param type the {@literal Class}
* @param the parameterized type of the given {@literal Class}
* @return the {@literal TypeOf} that captures the generic type of the given {@literal Class}
*/
public static TypeOf typeOf(Class type) {
return new TypeOf(
ModelType.of(typeWhichCannotBeNull(type))) {
};
}
/**
* Creates an instance of {@literal TypeOf} for the given {@literal Type}.
*
* @param type the {@literal Type}
* @param the parameterized type of the given {@literal Type}
* @return the {@literal TypeOf} that captures the generic type of the given {@literal Type}
*/
public static TypeOf typeOf(Type type) {
return new TypeOf(
Cast.>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>() {}, new TypeOf() {})} is equivalent to
* {@code new TypeOf>() {}}, 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 type;
private TypeOf(ModelType 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> 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();
}
/**
*
* This returns the underlying, concrete Java {@link java.lang.Class}.
*
*
* For example, a simple {@code TypeOf} will be the given generic type {@code String.class}.
*
* Generic types like {@code TypeOf>} would have the concrete type of {@code List.class}.
*
* For array types like {@code TypeOf}, the concrete type will be an array of the component type ({@code String[].class}).
*
*
* @since 5.0
*
* @return Underlying Java Class of this type.
*/
@Incubating
public Class getConcreteClass() {
return type.getConcreteClass();
}
@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 captureTypeArgument() {
Type genericSuperclass = getClass().getGenericSuperclass();
Type type = genericSuperclass instanceof ParameterizedType
? ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0]
: Object.class;
return Cast.uncheckedCast(ModelType.of(type));
}
private Class rawClass() {
return type.getRawClass();
}
private static T typeWhichCannotBeNull(T type) {
if (type == null) {
throw new IllegalArgumentException("type cannot be null.");
}
return type;
}
private static List> modelTypeListFrom(TypeOf[] typeOfs) {
return map(asList(typeOfs), new Function, ModelType>() {
@Override
public ModelType apply(TypeOf it) {
return it.type;
}
});
}
private static List> typeOfListFrom(List> modelTypes) {
return map(modelTypes, new Function, TypeOf>() {
@Override
public TypeOf apply(ModelType it) {
return typeOf(it);
}
});
}
private static TypeOf typeOf(ModelType componentType) {
return new TypeOf(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 List map(Iterable iterable, Function function) {
return copyOf(transform(iterable, function));
}
}