AbstractTypes.java
package net.florianschoppmann.java.type;
import javax.annotation.Nullable;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.NullType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Types;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* Abstract skeletal implementation of {@link Types}.
*
* <p>This class provides a skeletal implementation of the {@link Types} interface. Specifically, it implements all
* methods pertaining to §4.10 (subtyping) in the Java Language Specification (JLS). Concrete subclasses are expected to
* implement the abstract methods in this class, which are responsible for creating appropriate type-mirror instances.
* This class does not place any additional constraints on the concrete {@link TypeMirror} and {@link Element}
* implementations, so mutability and thread-safety are implementation-defined. However, this class crucially relies on
* the {@code equals} method being well-defined. That is, {@link Element} objects that have equal names and equal
* enclosing elements must compare equal. Likewise, {@link TypeMirror} objects that contain equal values must compare
* equal. In particular, multiple instances created by one of the {@code get}-methods must compare equal when the given
* arguments compare equal.
*
* <p>Besides subtype-related methods, this class also provides method
* {@link #resolveActualTypeArguments(TypeElement, TypeMirror)} for resolving formal type parameters to actual type
* arguments. For instance, given type {@code List<String>}, this method determines the actual type argument for the
* formal type parameter of {@code Collection<E>} (that is, {@code String} in this simple example).
*
* <p>Unless explicitly stated otherwise, all methods in this class expect non-null arguments. Passing null where not
* expected will cause a {@link NullPointerException} to be thrown. Implementations typically place additional
* restrictions on method arguments not captured by the types of the formal parameters (which stem from
* {@link javax.lang.model} and its subpackages). While the details are implementation-defined, typically this means
* that arguments must have been crated by the same implementation, or otherwise an {@link IllegalArgumentException}
* will be thrown. Implementations must override {@link #requireValidType(TypeMirror)} and
* {@link #requireValidElement(Element)}, as these methods are expected to perform any necessary validation.
*/
public abstract class AbstractTypes implements Types {
private static final List<TypeKind> REFERENCE_TYPES = Collections.unmodifiableList(Arrays.asList(
TypeKind.ARRAY, TypeKind.DECLARED, TypeKind.NULL, TypeKind.TYPEVAR
));
private static final int DEFAULT_STRING_BUILDER_SIZE = 256;
private final SubstitutionVisitor substitutionVisitor = new SubstitutionVisitor();
private final ErasureVisitor erasureVisitor = new ErasureVisitor();
private final ToStringVisitor toStringVisitor = new ToStringVisitor();
private final SubtypeVisitor subtypeVisitor = new SubtypeVisitor();
private final DeclaredTypeSubtypeVisitor declaredTypeSubtypeVisitor = new DeclaredTypeSubtypeVisitor();
/**
* Verifies that the given {@link Element} is valid for use with this class.
*
* <p>The meaning of valid is implementation-defined, but typically a valid {@link Element} must have been created
* by the implementation that belongs to the current {@code AbstractTypes} instance.
*
* @param element element
* @throws NullPointerException if the argument is null
* @throws IllegalArgumentException if the given {@link Element} cannot be used with this class
*/
protected abstract void requireValidElement(Element element);
/**
* Verifies that the given {@link TypeMirror} is valid for use with this class, or that it is {@code null}.
*
* <p>The meaning of valid is implementation-defined, but typically a valid {@link TypeMirror} must have been
* created by the implementation that belongs to the current {@code AbstractTypes} instance. A {@code null} argument
* is always valid. The rationale is that {@code null} {@link TypeMirror} arguments have a special meaning for some
* methods such as {@link #getWildcardType(TypeMirror, TypeMirror)} or
* {@link #createTypeVariable(TypeParameterElement, WildcardType)}.
*
* @param type type mirror, may be {@code null}
* @throws IllegalArgumentException if the given {@link TypeMirror} instance is non-null and it cannot be used with
* this class
*/
protected abstract void requireValidType(@Nullable TypeMirror type);
/**
* Verifies that the given array is non-null and contains valid types that are not null.
*
* @param types array of types
* @throws NullPointerException if the given array or any of its elements are null
* @throws IllegalArgumentException if {@link #requireValidType(TypeMirror)} throws an exception for one of the
* array elements
*/
protected final void requireValidTypes(TypeMirror[] types) {
for (TypeMirror typeArg: types) {
Objects.requireNonNull(typeArg, "TypeMirror array must not contain null elements.");
requireValidType(typeArg);
}
}
/**
* Returns a type mirror corresponding to the given Java reflection type.
*
* <p>Subclasses are required to return the appropriate {@link DeclaredType} instances for the following
* {@link Class} instances:
* <ul><li>
* {@link Object}
* </li><li>
* {@link Serializable}
* </li><li>
* {@link Cloneable}
* </li></ul>
*
* <p>Support for other types is not required and implementation-defined.
*
* @param type type as represented by Java Reflection API
* @throws UnsupportedOperationException If the given type is not one of the above {@link Class} objects and
* this type-utilities implementation does not support mirroring arbitrary Java reflection types.
* @return the type mirror corresponding to the given reflection type
*/
protected abstract TypeMirror typeMirror(Type type);
/**
* Internal class that contains both the substitution map passed to {@link #substitute(TypeMirror, Map)} and the
* set of fresh type variables created at the beginning of that method.
*/
private static final class Substitutions {
private final Map<TypeParameterElement, ? extends TypeMirror> map;
private final Map<TypeParameterElement, TypeVariable> freshTypeVariables;
private Substitutions(Map<TypeParameterElement, ? extends TypeMirror> map,
Map<TypeParameterElement, TypeVariable> freshTypeVariables) {
this.map = map;
this.freshTypeVariables = freshTypeVariables;
}
}
/**
* Visitor of a type mirror. Returns a new type mirror after performing the substitutions passed as visitor
* argument.
*
* <p>This visitor is only used within this class and only on <em>valid</em> {@link TypeMirror} instances. Hence, it
* can be asserted that the visitor parameter is always non-null.
*
* @see #substitutionVisitor
* @see #requireValidType(TypeMirror)
*/
private final class SubstitutionVisitor extends ExtendedTypeKindVisitor7<TypeMirror, Substitutions> {
private TypeMirror[] substituteInList(List<? extends TypeMirror> types, Substitutions substitutions) {
TypeMirror[] substituted = new TypeMirror[types.size()];
int i = 0;
for (TypeMirror type: types) {
substituted[i] = type.accept(this, substitutions);
++i;
}
return substituted;
}
@Override
public TypeMirror visitDeclared(DeclaredType declaredType, @Nullable Substitutions substitutions) {
assert substitutions != null;
TypeMirror enclosingType = declaredType.getEnclosingType();
TypeElement typeDeclaration = (TypeElement) declaredType.asElement();
TypeMirror[] substitutedArguments = substituteInList(declaredType.getTypeArguments(), substitutions);
if (enclosingType.getKind() == TypeKind.DECLARED) {
return getDeclaredType((DeclaredType) enclosingType, typeDeclaration, substitutedArguments);
} else {
return getDeclaredType(typeDeclaration, substitutedArguments);
}
}
@Override
public TypeMirror visitArray(ArrayType arrayType, @Nullable Substitutions substitutions) {
assert substitutions != null;
return getArrayType(arrayType.getComponentType().accept(this, substitutions));
}
@Override
public TypeMirror visitTypeVariable(TypeVariable typeVariable, @Nullable Substitutions substitutions) {
assert substitutions != null;
TypeParameterElement formalTypeParameter = (TypeParameterElement) typeVariable.asElement();
@Nullable TypeVariable freshTypeVariable = substitutions.freshTypeVariables.get(formalTypeParameter);
if (freshTypeVariable != null && formalTypeParameter.asType().equals(typeVariable)) {
return freshTypeVariable;
}
@Nullable TypeMirror substitution = substitutions.map.get(formalTypeParameter);
if (substitution != null) {
return substitution;
}
return getTypeVariable(
formalTypeParameter,
typeVariable.getUpperBound().accept(this, substitutions),
typeVariable.getLowerBound().accept(this, substitutions),
capturedTypeArgument(typeVariable)
);
}
@Override
public TypeMirror visitWildcard(WildcardType wildcardType, @Nullable Substitutions substitutions) {
assert substitutions != null;
@Nullable TypeMirror extendsBounds = wildcardType.getExtendsBound();
@Nullable TypeMirror superBound = wildcardType.getSuperBound();
return getWildcardType(
extendsBounds != null
? extendsBounds.accept(this, substitutions)
: null,
superBound != null
? superBound.accept(this, substitutions)
: null
);
}
@Override
public TypeMirror visitIntersection(IntersectionType intersectionType, @Nullable Substitutions substitutions) {
assert substitutions != null;
return getIntersectionType(substituteInList(intersectionType.getBounds(), substitutions));
}
@Override
protected TypeMirror defaultAction(TypeMirror type, Substitutions substitutions) {
return type;
}
}
/**
* Replaces formal type parameters in the given type.
*
* <p>This method requires that {@code type} does not contain transitive references to itself, unless through
* {@link DeclaredType#asElement()} → {@link TypeElement#asType()} or {@link TypeVariable#asElement()} →
* {@link TypeParameterElement#asType()}. Otherwise, this method might run into an infinite recursion, resulting in
* a {@link StackOverflowError}.
*
* <p>Moreover, this method requires that any type variable transitively referenced by {@code substitutionMap} must
* not contain a transitive reference (through {@link TypeVariable#getUpperBound()} or
* {@link TypeVariable#getLowerBound()}) to itself. Instead, any instance of {@link TypeVariable} (transitively)
* referenced by a value in {@code substitutionMap} must be the result of {@link TypeParameterElement#asType()}.
*
* <p>This method creates a fresh type variable for each formal type parameter that is to be substituted by a type
* variable for the same formal type parameter. For instance, suppose {@code T extends Object} is a formal type
* parameter, and {@code substitutionMap} specifies to replace it with the type variable {@code T extends U<T>}. In
* this case, {@link #createTypeVariable(TypeParameterElement, WildcardType)} will be called with the formal type
* parameter {@code T extends Object} as (first) argument. Once all fresh types have been created,
* {@link #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)} will then be called with {@code U<T>} as
* upper bound, where {@code T} is the fresh type variable {@code T extends U<T>}.
*
* @param type type in which the type parameters will be replaced recursively, guaranteed non-null
* @param substitutionMap mapping from formal type parameters to substituted type, guaranteed non-null
* @return new port type, guaranteed non-null
*/
protected TypeMirror substitute(TypeMirror type, Map<TypeParameterElement, ? extends TypeMirror> substitutionMap) {
Objects.requireNonNull(type);
Objects.requireNonNull(substitutionMap);
requireValidType(type);
for (TypeMirror substitutionType: substitutionMap.values()) {
Objects.requireNonNull(substitutionType, "Substitution type cannot be null.");
requireValidType(substitutionType);
}
Map<TypeParameterElement, TypeVariable> freshTypeVariables = new LinkedHashMap<>();
for (Map.Entry<TypeParameterElement, ? extends TypeMirror> entry: substitutionMap.entrySet()) {
TypeMirror value = entry.getValue();
if (value.getKind() == TypeKind.TYPEVAR) {
TypeParameterElement formalTypeParameter = entry.getKey();
TypeVariable typeVariable = (TypeVariable) value;
if (entry.getKey().equals(typeVariable.asElement())) {
assert !freshTypeVariables.containsKey(formalTypeParameter);
freshTypeVariables.put(
formalTypeParameter,
createTypeVariable(formalTypeParameter, capturedTypeArgument(typeVariable))
);
}
}
}
Substitutions substitutions = new Substitutions(substitutionMap, freshTypeVariables);
for (Map.Entry<TypeParameterElement, TypeVariable> entry: freshTypeVariables.entrySet()) {
TypeVariable substitution = (TypeVariable) substitutionMap.get(entry.getKey());
setTypeVariableBounds(
entry.getValue(),
substitution.getUpperBound().accept(substitutionVisitor, substitutions),
substitution.getLowerBound().accept(substitutionVisitor, substitutions)
);
}
return type.accept(substitutionVisitor, substitutions);
}
/**
* Returns the actual type arguments of a type declaration given a subtype (typically with its own actual type
* arguments).
*
* <p>This method "projects" the actual type arguments of {@code subtype}, as well as all actual type arguments of
* super types in the type hierarchy between {@code typeElement} and {@code subType}, onto the type declaration
* represented by {@code typeElement}.
*
* <p>For example, {@code typeElement} may be the (generic) type declaration {@code Comparable<T>}, and
* {@code subType} may be the (non-generic) type {@link Integer}. The result in this case would be a singleton list
* containing the type {@link Integer}.
*
* <p>More generally, resolution works as follows: First, the shortest inheritance path from {@code subType} to
* {@code typeElement} is found. Note that while Java allows multiple inheritance for interfaces, JLS §8.1.5
* disallows inheriting from the same interface with different type parameters (both directly and transitively).
* Hence, the shortest path contains all information that is necessary to resolve formal type parameters to actual
* parameters. This method then propagates the actual type arguments bottom-up along the inheritance path.
* Note that the inheritance path consists of {@link DeclaredType} instances, and it may consist of generic types,
* non-generic types, and raw types.
*
* <p>If the inheritance path contains a raw type <em>before</em> the last path element, this method proceeds
* by using the "prototypical" type returned by {@link Element#asType()} instead. Correspondingly, it is possible
* that the returned list may contain type variables from a type declaration along the inheritance path. However, if
* the <em>last</em> inheritance path element is a raw type, the returned list will be empty. Otherwise, if a
* non-null non-empty {@link List} is returned, it is guaranteed to have the same number of elements as
* {@code typeElement.getTypeParameters()}.
*
* @param typeElement type declaration
* @param subType potential subtype of {@code typeElement}, must be a non-generic type declaration, raw type,
* generic type declaration, or parameterized type
* @return actual type arguments for the formal parameters of {@code typeElement} (empty list if the <em>last</em>
* path element in the inheritance path from {@code subType} to {@code typeElement} is a raw type), or
* {@code null} if {@code subType} is not a subtype of {@code typeElement}
* @throws IllegalArgumentException if the arguments do not satisfy the constraints mentioned above
*/
@Nullable
public final List<? extends TypeMirror> resolveActualTypeArguments(TypeElement typeElement, TypeMirror subType) {
requireValidElement(Objects.requireNonNull(typeElement));
requireValidType(Objects.requireNonNull(subType));
if (subType.getKind() != TypeKind.DECLARED) {
return null;
}
DeclaredType declaredSubType = (DeclaredType) subType;
// getShortestPathToSuperType() will throw an exception if subType does not satisfy the constraints mentioned
// above.
@Nullable List<DeclaredType> path = getShortestPathToSuperType(typeElement, declaredSubType);
if (path == null) {
return null;
}
// Early exit if there is nothing to resolve. However, we must not move this early exit any earlier, because
// we do want to return null if subType is not a subtype of typeElement.
if (typeElement.getTypeParameters().isEmpty()) {
return Collections.emptyList();
}
Iterator<DeclaredType> pathIterator = path.iterator();
DeclaredType current = pathIterator.next();
while (pathIterator.hasNext()) {
TypeElement currentTypeElement = (TypeElement) current.asElement();
// Check whether "current" is a raw type. This may happen in the first loop iteration if subType is a raw
// type, or in subsequent iterations if the type that was previously "current" (during the last iteration
// of the for-loop) derived from a raw type. If yes, use instead the "prototypical" type returned by
// Element#asType().
if (current.getTypeArguments().isEmpty() && !currentTypeElement.getTypeParameters().isEmpty()) {
current = (DeclaredType) currentTypeElement.asType();
}
List<? extends TypeParameterElement> currentFormalParameters = currentTypeElement.getTypeParameters();
List<? extends TypeMirror> currentActualParameters = current.getTypeArguments();
Map<TypeParameterElement, TypeMirror> currentFormalToActual = new LinkedHashMap<>();
for (int index = 0; index < currentFormalParameters.size(); ++index) {
currentFormalToActual.put(currentFormalParameters.get(index), currentActualParameters.get(index));
}
current = (DeclaredType) substitute(pathIterator.next(), currentFormalToActual);
}
return current.getTypeArguments();
}
/**
* Visitor of a type mirror. Returns whether the visited type mirror is a subtype of the visitor argument (of type
* {@link DeclaredType}).
*
* <p>This visitor is only used within this class and only on <em>valid</em> {@link TypeMirror} instances. Hence, it
* can be asserted that the visitor parameter is always non-null.
*
* @see #declaredTypeSubtypeVisitor
* @see #requireValidType(TypeMirror)
*/
private final class DeclaredTypeSubtypeVisitor extends ExtendedTypeKindVisitor7<Boolean, DeclaredType> {
private DeclaredTypeSubtypeVisitor() {
super(false);
}
/**
* Returns whether the first declared type is a subtype of the second declared type.
*
* <p>This method proceeds by computing the actual type arguments when {@code subType} is projected onto the
* type declaration corresponding to {@code superType}. It then tests if all actual type arguments of
* {@code subType} are <em>contained</em> in those of {@code superType}.
*/
@Override
public Boolean visitDeclared(DeclaredType subType, @Nullable DeclaredType superType) {
assert superType != null;
DeclaredType actualSubType = subType;
// First test if there subType has at least one wildcard type argument. In that case, we need to perform a
// capture conversion first.
// Note that this is the right place to do capture conversion: JLS §8.1.4 and §9.1.3 state about class types
// and interfaces type listed in the extends or implements clause of a class/interface declaration:
// - "If the ClassType has type arguments, it must denote a well-formed parameterized type (§4.5), and none
// of the type arguments may be wildcard type arguments, or a compile-time error occurs."
// - "If an InterfaceType has type arguments, it must denote a well-formed parameterized type (§4.5), and
// none of the type arguments may be wildcard type arguments, or a compile-time error occurs."
// Hence, wildcards do not appear on the "inheritance path" between subType and superType.
for (TypeMirror subTypeArgument: subType.getTypeArguments()) {
if (subTypeArgument.getKind() == TypeKind.WILDCARD) {
actualSubType = (DeclaredType) capture(subType);
break;
}
}
// Resolve the actual type parameters of subType when projected onto the superType
TypeElement superTypeDeclaration = (TypeElement) superType.asElement();
@Nullable List<? extends TypeMirror> projectedTypeArguments
= resolveActualTypeArguments(superTypeDeclaration, actualSubType);
if (projectedTypeArguments == null) {
// subType is not a subtype of the type declaration
return false;
}
List<? extends TypeMirror> superTypeArguments = superType.getTypeArguments();
if (projectedTypeArguments.isEmpty() && !superTypeArguments.isEmpty()) {
// the projection of subType onto superType resulted in a raw type, which is neither a subtype of any
// parametrized type of the generic type declaration of superType, nor the generic type declaration
// itself
return false;
}
// Note that superType could be a raw type, in which case superTypeArguments is empty. In that case, the
// loop would not be executed at all.
Iterator<? extends TypeMirror> projectedTypeArgumentsIterator = projectedTypeArguments.iterator();
for (TypeMirror to: superTypeArguments) {
TypeMirror from = projectedTypeArgumentsIterator.next();
if (!contains(to, from)) {
return false;
}
}
return true;
}
/**
* Returns whether the array type is a subtype of the declared type.
*
* <p>According to JLS §4.10.3, an array type can only be a subtype of a declared type if the latter represents
* one of {@link Object}, {@link Cloneable}, or {@link Serializable}.
*/
@Override
public Boolean visitArray(ArrayType subType, @Nullable DeclaredType superType) {
assert superType != null;
return typeMirror(Object.class).equals(superType)
|| typeMirror(Cloneable.class).equals(superType)
|| typeMirror(Serializable.class).equals(superType);
}
/**
* Returns whether the type variable is a subtype of the declared type.
*
* <p>According to JLS §4.10.2, the direct supertypes of a type variable are the types listed in its bound.
* Hence, this method returns true if {@link TypeVariable#getUpperBound()} is a subtype of {@code superType}.
*/
@Override
public Boolean visitTypeVariable(TypeVariable subType, @Nullable DeclaredType superType) {
assert superType != null;
return isSubtype(subType.getUpperBound(), superType);
}
/**
* Returns whether the intersection type is a subtype of the declared type.
*
* <p>According to JLS §4.10.2, the direct supertypes of an intersection type {@code T_1 & ... T_n} are
* {@code T_1}, ..., {@code T_n}. Hence, this method returns true if at least one of
* {@link IntersectionType#getBounds()} is a subtype of {@code superType}.
*/
@Override
public Boolean visitIntersection(IntersectionType subType, @Nullable DeclaredType superType) {
assert superType != null;
for (TypeMirror bound: subType.getBounds()) {
if (isSubtype(bound, superType)) {
return true;
}
}
return false;
}
}
/**
* Visitor of a type mirror. Returns whether the visited type mirror is a supertype of the visitor argument.
*
* <p>This visitor does not have to deal with the null-type, which has been dealt with before. It has to make a
* decision for {@link ArrayType}, {@link DeclaredType}, {@link PrimitiveType}, and {@link TypeVariable}.
*
* <p>Java 8 introduces {@code IntersectionType}, but this code currently uses Java 7. Moreover, there are other
* types that are currently not supported, such as {@link javax.lang.model.type.UnionType}. Finally,
* {@link WildcardType} is not a type, but only a type argument, so it is not necessary to be dealt with here.
* Likewise, {@link NoType} is not used to model proper types, but only empty bounds, non-existence of interface
* super classes, etc.
*
* <p>This visitor is only used within this class and only on <em>valid</em> {@link TypeMirror} instances. Hence, it
* can be asserted that the visitor parameter is always non-null.
*
* @see #subtypeVisitor
* @see #requireValidType(TypeMirror)
*/
private final class SubtypeVisitor extends ExtendedTypeKindVisitor7<Boolean, TypeMirror> {
private SubtypeVisitor() {
super(false);
}
/**
* Returns whether the array type is a super type of the type given as second argument.
*
* <p>According to JLS §4.10.3, array component types are covariant; for instance, {@code Integer[]} is a proper
* subtype of {@code Number[]}. Moreover, all subtypes of an array type are again array types. Hence, this
* method simply reduces the problem to testing if {@code subType} is also an array type and then applying
* {@link AbstractTypes#isSubtype(TypeMirror, TypeMirror)} to the component types.
*/
@Override
public Boolean visitArray(ArrayType superType, @Nullable TypeMirror subType) {
assert subType != null;
return subType.getKind() == TypeKind.ARRAY
&& isSubtype(((ArrayType) subType).getComponentType(), superType.getComponentType());
}
/**
* Returns whether the declared type is a super type of the type given as second argument.
*
* <p>This method has {@link DeclaredTypeSubtypeVisitor} visit {@code subType}.
*/
@Override
public Boolean visitDeclared(DeclaredType superType, @Nullable TypeMirror subType) {
assert subType != null;
return subType.accept(declaredTypeSubtypeVisitor, superType);
}
private final List<TypeKind> numericKindEnumValues = Collections.unmodifiableList(Arrays.asList(
TypeKind.DOUBLE, TypeKind.FLOAT, TypeKind.LONG, TypeKind.INT, TypeKind.SHORT, TypeKind.BYTE
));
private final int intIndex = numericKindEnumValues.indexOf(TypeKind.INT);
/**
* Returns whether the primitive type is a supertype of the given type.
*/
@Override
public Boolean visitPrimitive(PrimitiveType superType, @Nullable TypeMirror subType) {
assert subType != null;
if (!subType.getKind().isPrimitive()) {
return false;
}
int superTypeIndex = numericKindEnumValues.indexOf(superType.getKind());
int subTypeIndex = numericKindEnumValues.indexOf(subType.getKind());
return (subType.getKind() == TypeKind.CHAR && 0 <= superTypeIndex && superTypeIndex <= intIndex)
|| (0 <= superTypeIndex && superTypeIndex <= subTypeIndex);
}
/**
* Returns whether the type variable is a super type of the given type.
*
* <p>A type variable is only a supertype of its lower bound.
*/
@Override
public Boolean visitTypeVariable(TypeVariable superType, @Nullable TypeMirror subType) {
assert subType != null;
return isSameType(superType.getLowerBound(), subType);
}
/**
* Returns whether the given intersection type is a super type of the given type.
*
* <p>While one might expect that the set of supertypes of an intersection type {@code T_1 & ... & T_n} includes
* the intersection of any (non-empty) subset of {@code T_1}, ..., {@code T_n}, this seems is not specified by
* JLS §4.10 (which only says that "the direct supertypes of an intersection type {@code T_1 & ... & T_n} are
* {@code T_i} (1 ≤ i ≤ n)"). See also issue
* <a href="https://bugs.openjdk.java.net/browse/JDK-6718388">JDK-6718388</a>.
*
* <p>Therefore, an intersection type is only a supertype of itself.
*/
@Override
public Boolean visitIntersection(IntersectionType superType, @Nullable TypeMirror subType) {
assert subType != null;
return isSameType(superType, subType);
}
}
/**
* Returns whether the first type is a subtype of the second type, as specified by JLS §4.10.
*
* <p>The subtype relationship is transitive and reflexive.
*
* @param t1 the first type
* @param t2 the second type
* @return {@code true} if and only if the first type is a subtype of the second
* @throws NullPointerException if an argument is null
* @throws IllegalArgumentException if given an executable or package type
*/
@Override
public final boolean isSubtype(TypeMirror t1, TypeMirror t2) {
requireValidType(Objects.requireNonNull(t1));
requireValidType(Objects.requireNonNull(t2));
// §4.10.2: The direct supertypes of the null type are all reference types other than the null type itself.
if (t1.getKind() == TypeKind.NULL && REFERENCE_TYPES.contains(t2.getKind())) {
return true;
}
return t2.accept(subtypeVisitor, t1);
}
/**
* Returns whether the first type argument <em>contains</em> the second type argument, as specified by JLS §4.5.1.
*
* <p>Using the JLS notation, this method returns true if {@code t2 <= t1}. As JLS §4.10 states, "subtyping does not
* extend through parameterized types." Hence, this method is necessarily different from
* {@link #isSubtype(TypeMirror, TypeMirror)}. In particular, the super-type relationship does not include types
* with covariant type arguments.
*
* @param t1 the first type
* @param t2 the second type
* @return {@code true} if and only if the first type contains the second
* @throws IllegalArgumentException if given an executable or package type
*/
@Override
public final boolean contains(TypeMirror t1, TypeMirror t2) {
Objects.requireNonNull(t1);
Objects.requireNonNull(t2);
requireValidType(t1);
requireValidType(t2);
// We need to cover these cases (JLS §4.5.1):
//
// (a) wildcard t2 <= wildcard t1
// 1. ? extends T <= ? extends S if T <: S
// 2. ? extends T <= ?
// 3. ? super T <= ? super S if S <: T
// 4. ? super T <= ?
// 5. ? super T <= ? extends Object
//
// (b) other type t2 <= other type t1
// 1. T <= T
//
// (c) other type t2 <= wildcard t1
// 1. T <= ? extends T
// 2. T <= ? super T
if (t1.getKind() == TypeKind.WILDCARD) {
@Nullable TypeMirror t1ExtendsBound = ((WildcardType) t1).getExtendsBound();
@Nullable TypeMirror t1SuperBound = ((WildcardType) t1).getSuperBound();
boolean t1HasExtendsBound = t1ExtendsBound != null;
boolean t1HasSuperBound = t1SuperBound != null;
if (t2.getKind() == TypeKind.WILDCARD) {
// Handle (a).
@Nullable TypeMirror t2ExtendsBound = ((WildcardType) t2).getExtendsBound();
@Nullable TypeMirror t2SuperBound = ((WildcardType) t2).getSuperBound();
if (t2ExtendsBound != null) {
if (t1ExtendsBound != null) {
// (a) 1.
return isSubtype(t2ExtendsBound, t1ExtendsBound);
} else if (t1SuperBound == null) {
// (a) 2.
return true;
}
// Note that "? super S" never contains a type argument of form "? extends T"
return false;
} else if (t2SuperBound != null) {
if (t1SuperBound != null) {
// (a) 3.
return isSubtype(t1SuperBound, t2SuperBound);
} else {
// (a) 4. and 5.: Handle case "? super T <= ?" (always true) or "? super S <= ? extends T" (only
// if T is Object)
return t1ExtendsBound == null
|| isSameType(t1ExtendsBound, typeMirror(Object.class));
}
} else {
// Handle special case of (a), namely "? <= ? extends T" (only if T is Object), "? <= ? super T"
// (always false), or "? <= ?" (which is equivalent to "? extends Object <= ? extends Object" and
// therefore true).
return t1SuperBound == null && (
t1ExtendsBound == null || isSameType(t1ExtendsBound, typeMirror(Object.class))
);
}
} else {
// Handle (c). Reduce to case (a).
if (t1HasExtendsBound) {
// (c) 1.
return contains(t1, getWildcardType(t2, null));
} else if (t1HasSuperBound) {
// (c) 2.
return contains(t1, getWildcardType(null, t2));
}
// Combining (c) 1. with (a) 2. or (c) 2. with (a) 4., we immediately have "T <= ?"
return true;
}
} else {
// Handle (b).
return isSameType(t1, t2);
}
}
/**
* Returns the direct super types of the given type declaration, as defined by JLS §4.10.2.
*/
private List<DeclaredType> directSupertypesOfTypeDeclaration(TypeElement typeElement) {
TypeMirror superClass = typeElement.getSuperclass();
List<? extends TypeMirror> interfaces = typeElement.getInterfaces();
List<DeclaredType> newSuperTypes = new ArrayList<>(1 + interfaces.size());
if (superClass.getKind() == TypeKind.DECLARED) {
newSuperTypes.add((DeclaredType) superClass);
}
for (TypeMirror superInterface: interfaces) {
newSuperTypes.add((DeclaredType) superInterface);
}
if (typeElement.getKind() == ElementKind.INTERFACE && interfaces.isEmpty()) {
newSuperTypes.add((DeclaredType) typeMirror(Object.class));
}
return newSuperTypes;
}
/**
* Internal class to keep state for the Dijkstra shortest-path algorithm in
* {@link #getShortestPathToSuperType(TypeElement, DeclaredType)}.
*/
private static final class TypeDeclarationVertexState {
private int distance;
private boolean visited;
private final TypeElement typeElement;
/**
* The type as contained in {@code previous.typeDeclaration.getSuperTypes()}.
*/
private final DeclaredType declaredType;
@Nullable private TypeDeclarationVertexState previous;
private TypeDeclarationVertexState(int distance, boolean visited, TypeElement typeElement,
DeclaredType declaredType) {
this.distance = distance;
this.visited = visited;
this.typeElement = typeElement;
this.declaredType = declaredType;
}
/**
* Returns the path induced by this node, starting with {@code derived} and ending with {@link #declaredType}.
*
* <p>The path is obtained by following {@link #previous} until {@code null}. Each element in the path (except
* for the first) is the {@link #declaredType} of the current instance.
*
* @param derived the first element in the path
* @return the path induced by this node
*/
private List<DeclaredType> toPath(DeclaredType derived) {
DeclaredType[] path = new DeclaredType[distance + 1];
int count = path.length;
TypeDeclarationVertexState pathElement = this;
while (pathElement.previous != null) {
--count;
path[count] = pathElement.declaredType;
pathElement = pathElement.previous;
}
path[0] = derived;
return Arrays.asList(path);
}
}
/**
* Returns the shortest inheritance path between a type declaration and a subtype (starting with
* {@code derived} and ending with {@code base}).
*
* <p>Each element in the returned path is a non-generic type, a raw type, or a parameterized type. The
* {@link DeclaredType} at position {@code i} is always contained in the result of
* {@link #directSupertypesOfTypeDeclaration(TypeElement)} applied to the type declaration of the type at position
* {@code (i - 1)}.
*
* <p>This methods runs a Dijkstra shortest-path algorithm. It relies on {@link TypeElement#equals(Object)}
* being well-defined (two object representing the same type declaration must compare equal). Consequently, if
* {@link DeclaredType} instances have an identity, it must be guaranteed that there are no two instances
* representing the same type declaration.
*
* @param base base type declaration
* @param derived derived type
* @return If there is an inheritance path from {@code derived} to {@code base}, then a {@code List<DeclaredType>}
* {@code p} such that {@code p.get(0).equals(toGenericType(derived))} and
* {@code toRawTypeDeclaration(p.get(p.size() - 1)).equals(base)} are {@code true}. Otherwise, {@code null} to
* indicate that there is no such path.
*/
@Nullable
private List<DeclaredType> getShortestPathToSuperType(TypeElement base, DeclaredType derived) {
TypeElement typeElement = (TypeElement) derived.asElement();
Set<TypeElement> boundary = new LinkedHashSet<>();
Map<TypeElement, TypeDeclarationVertexState> dijkstraState = new HashMap<>();
// Distance from derived to itself is 0
dijkstraState.put(typeElement, new TypeDeclarationVertexState(0, false, typeElement, derived));
// Start off with derived
boundary.add(typeElement);
// Invariants:
// - boundary only contains nodes that have *not* been visited
// - For all visited nodes, the shortest path is known
while (!boundary.isEmpty()) {
// shortest := vertex in boundary with smallest distance from typeElement
@Nullable TypeDeclarationVertexState shortest = null;
for (TypeElement currentDeclaration: boundary) {
TypeDeclarationVertexState current = dijkstraState.get(currentDeclaration);
if (shortest == null || current.distance < shortest.distance) {
shortest = current;
}
}
// Since boundary is non-empty, shortest was assigned in the previous loop. Also note that due to the above
// invariant, shortest has not been visited.
assert shortest != null && !shortest.visited;
// Terminate if we found base. Since shortest.distance is non-decreasing over the loop iterations, it is
// impossible to find a shorter path in future iterations.
if (shortest.typeElement.equals(base)) {
return shortest.toPath(derived);
}
// Remove shortest from boundary.
boundary.remove(shortest.typeElement);
shortest.visited = true;
for (DeclaredType superType: directSupertypesOfTypeDeclaration(shortest.typeElement)) {
// A direct super type of a type declaration is either a non-generic type declaration or a raw type (in
// both cases represented as DeclaredType with no actual type parameters) or a parameterized type
TypeElement superDeclaration = (TypeElement) superType.asElement();
@Nullable TypeDeclarationVertexState stats = dijkstraState.get(superDeclaration);
if (stats == null) {
stats = new TypeDeclarationVertexState(Integer.MAX_VALUE, false, superDeclaration, superType);
dijkstraState.put(superDeclaration, stats);
}
int alt = shortest.distance + 1;
if (!stats.visited && alt < stats.distance) {
stats.distance = alt;
stats.previous = shortest;
boundary.add(superDeclaration);
}
}
}
return null;
}
/**
* Visitor of a type mirror. Returns the erasure of the visited type mirror.
*
* @see #erasureVisitor
*/
private final class ErasureVisitor extends ExtendedTypeKindVisitor7<TypeMirror, Void> {
@Override
public TypeMirror visitDeclared(DeclaredType declaredType, @Nullable Void ignored) {
TypeMirror originalEnclosingType = declaredType.getEnclosingType();
@Nullable DeclaredType newEnclosingType = originalEnclosingType.getKind() == TypeKind.NONE
? null
: (DeclaredType) erasure(declaredType.getEnclosingType());
return getDeclaredType(newEnclosingType, (TypeElement) declaredType.asElement());
}
/**
* Returns the array type corresponding to the erasure of the component type.
*/
@Override
public TypeMirror visitArray(ArrayType arrayType, @Nullable Void ignored) {
return getArrayType(erasure(arrayType.getComponentType()));
}
/**
* Returns the erasure of the leftmost bound of the given type variable.
*
* <p>The erasure of a type variable is the erasure of its leftmost bound (JLS §4.6). If multiple bounds are
* present, the upper bound is modelled as an intersection type. The erasure of an intersection type is
* guaranteed to have see right form (see {@link #visitIntersection(IntersectionType, Void)}).
*/
@Override
public TypeMirror visitTypeVariable(TypeVariable typeVariable, @Nullable Void ignored) {
return erasure(typeVariable.getUpperBound());
}
/**
* Returns the erasure of the leftmost member of the given intersection type.
*
* <p>While JLS §4.6 does not mention intersection types (and thus, strictly speaking, the erasure of an
* intersection type should be the unmodified intersection type itself), this implementation computes the
* erasure of an intersection type as the erasure of its left-most type.
*/
@Override
public TypeMirror visitIntersection(IntersectionType intersectionType, @Nullable Void ignored) {
return erasure(intersectionType.getBounds().get(0));
}
/**
* Returns the given type itself.
*
* <p>JLS §4.6 specifies: "The erasure of every other type is the type itself."
*/
@Override
protected TypeMirror defaultAction(TypeMirror type, Void ignored) {
return type;
}
}
/**
* Returns the erasure of a type, as specified by JLS §4.6.
*
* @param type the type to be erased
* @return the erasure of the given type
* @throws IllegalArgumentException if given a package type
*/
@Override
public final TypeMirror erasure(TypeMirror type) {
Objects.requireNonNull(type);
requireValidType(type);
return type.accept(erasureVisitor, null);
}
/**
* Returns the element corresponding to a type.
*
* <p>The type may be a {@code DeclaredType} or {@code TypeVariable}. Returns {@code null} if the type is not one
* with a corresponding element.
*
* @param type the type
* @return the element corresponding to the given type
*/
@Override
public final Element asElement(TypeMirror type) {
Objects.requireNonNull(type);
requireValidType(type);
if (type.getKind() == TypeKind.DECLARED) {
return ((DeclaredType) type).asElement();
} else if (type.getKind() == TypeKind.TYPEVAR) {
return ((TypeVariable) type).asElement();
} else {
return null;
}
}
/**
* Returns whether the two given type arguments represent the same type.
*
* <p>If either of the arguments to this method represents a wildcard, this method will return false. As a
* consequence, a wildcard is not the same type as itself.
*
* @param t1 the first type
* @param t2 the second type
* @return {@code true} if and only if the two types are the same
*/
@Override
public final boolean isSameType(TypeMirror t1, TypeMirror t2) {
requireValidType(Objects.requireNonNull(t1));
requireValidType(Objects.requireNonNull(t2));
return t1.getKind() != TypeKind.WILDCARD && t1.equals(t2);
}
/**
* Returns the greatest lower bound (glb) of a wildcard extends bound and an upper bound of a type parameter.
*
* <p>This method is only called from {@link #capture(TypeMirror)}. JLS §5.1.10 defines the greatest lower bound
* {@code glb(V_1, ..., V_m)} as {@code V_1 & ... & V_m}. Unfortunately, the specification provides no clarity
* whether intersection types are allowed to be nested. This implementation takes the interpretation that
* intersection types should not be nested. Therefore, the bounds contained in {@code originalUpperBound} are
* unwrapped.
*
* @param wildcardExtendsBound extends bound of the wildcard type argument
* @param originalUpperBound original upper bound of the type parameter
* @return the greatest lower bound
*/
private static TypeMirror[] greatestLowerBound(TypeMirror wildcardExtendsBound, TypeMirror originalUpperBound) {
@Nullable TypeMirror[] result = null;
if (originalUpperBound instanceof IntersectionType) {
IntersectionType originalIntersectionBound = (IntersectionType) originalUpperBound;
if (originalIntersectionBound.isIntersectionType()) {
List<? extends TypeMirror> originalBounds = originalIntersectionBound.getBounds();
result = new TypeMirror[1 + originalBounds.size()];
int i = 0;
for (TypeMirror originalBound: originalBounds) {
++i;
result[i] = originalBound;
}
}
}
if (result == null) {
result = new TypeMirror[2];
result[1] = originalUpperBound;
}
result[0] = wildcardExtendsBound;
return result;
}
/**
* Returns the caputure conversion of (just) the given wildcard argument.
*
* <p>This method is only called by {@link #capture(TypeMirror)}.
*/
private TypeVariable captureWildcardArgument(WildcardType wildcardArgument, TypeParameterElement typeParameter) {
TypeVariable originalTypeVariable = (TypeVariable) typeParameter.asType();
// Denoted U_i in JLS 5.1.10
TypeMirror originalUpperBound = originalTypeVariable.getUpperBound();
// Both of the following are denoted B_i in JLS 5.1.10 (in "? extends B_i" and "? super B_i", respectively)
@Nullable TypeMirror wildcardExtendsBound = wildcardArgument.getExtendsBound();
@Nullable TypeMirror wildcardSuperBound = wildcardArgument.getSuperBound();
TypeMirror newUpperBound;
TypeMirror newLowerBound;
// There exists a capture conversion from a parameterized type G<T_1,...,T_n> (§4.5) to a parameterized type
// G<S_1, ..., S_n>, where, for 1 <= i <= n:
if (wildcardExtendsBound == null && wildcardSuperBound == null) {
// If T_i is a wildcard type argument (§4.5.1) of the form ?, then S_i is a fresh type variable whose
// upper bound is U_i[A_1 := S_1, ..., A_n := S_n] and whose lower bound is the null type (§4.1).
newUpperBound = originalUpperBound;
newLowerBound = getNullType();
} else if (wildcardSuperBound == null) {
// If T_i is a wildcard type argument of the form ? extends B_i, then S_i is a fresh type variable whose
// upper bound is glb(B_i, U_i[A_1 := S_1, ..., A_n := S_n]) and whose lower bound is the null type.
//
// glb(V_1, ..., V_m) is defined as V_1 & ... & V_m.
// It is a compile-time error if, for any two classes (not interfaces) V_i and V_j, V_i is not a
// subclass of V_j or vice versa.
newUpperBound = getIntersectionType(greatestLowerBound(wildcardExtendsBound, originalUpperBound));
newLowerBound = getNullType();
} else {
// If T_i is a wildcard type argument of the form ? super B_i, then S_i is a fresh type variable whose
// upper bound is U_i[A_1 := S1, ..., A_n := S_n] and whose lower bound is B_i.
assert wildcardExtendsBound == null;
newUpperBound = originalUpperBound;
newLowerBound = wildcardSuperBound;
}
return getTypeVariable(typeParameter, newUpperBound, newLowerBound, wildcardArgument);
}
/**
* Returns the capture conversion of the given type, as specified by JLS §5.1.10.
*
* @param type the type to be converted
* @return the result of applying capture conversion
* @throws IllegalArgumentException if given an executable or package type
*/
@Override
public final TypeMirror capture(TypeMirror type) {
Objects.requireNonNull(type);
requireValidType(type);
// JLS §5.1.10 states: "Capture conversion on any type other than a parameterized type (§4.5) acts as an
// identity conversion (§5.1.1)."
if (type.getKind() != TypeKind.DECLARED) {
return type;
}
DeclaredType declaredType = (DeclaredType) type;
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (typeArguments.isEmpty()) {
return declaredType;
}
TypeElement typeDeclaration = (TypeElement) declaredType.asElement();
Iterator<? extends TypeMirror> typeArgumentIterator = typeArguments.iterator();
Iterator<? extends TypeParameterElement> typeParameterIterator = typeDeclaration.getTypeParameters().iterator();
TypeMirror[] newArguments = new TypeMirror[typeArguments.size()];
Map<TypeParameterElement, TypeMirror> substitutions = new LinkedHashMap<>();
for (int index = 0; index < newArguments.length; ++index) {
TypeMirror typeArgument = typeArgumentIterator.next();
TypeParameterElement typeParameter = typeParameterIterator.next();
TypeMirror substitution;
if (typeArgument.getKind() != TypeKind.WILDCARD) {
newArguments[index] = typeArgument;
substitution = typeArgument;
} else {
// For the intermediate declared type (see below), we need the original type variable corresponding to
// the formal type parameter. Only original type variables will be replaced by the substitutionVisitor.
newArguments[index] = typeParameter.asType();
substitution = captureWildcardArgument((WildcardType) typeArgument, typeParameter);
}
substitutions.put(typeParameter, substitution);
}
TypeMirror enclosingType = declaredType.getEnclosingType();
// Construct intermediateDeclaredType that already has type variables in its argument list instead of wildcard
// arguments.
DeclaredType intermediateDeclaredType;
if (enclosingType.getKind() == TypeKind.DECLARED) {
intermediateDeclaredType = getDeclaredType((DeclaredType) enclosingType, typeDeclaration, newArguments);
} else {
intermediateDeclaredType = getDeclaredType(typeDeclaration, newArguments);
}
return substitute(intermediateDeclaredType, substitutions);
}
/**
* Returns a new type variable that corresponds to the given formal type parameter and that has the given actual
* upper and lower bounds.
*
* <p>This method is primarily needed during capture conversion, in order to create a fresh type variable that
* overrides the bounds of the formal type parameter it represents. This method is also called during substitution.
* As an example, given a formal type parameter {@code T extends Object} and an upper bound {@code Number}, this
* method returns the type variable {@code T} with upper bound {@code Number}.
*
* <p>This method is not suited for creating type variables with recursive type bounds if these bounds override the
* bounds of the formal type parameter (as only happens during capture conversion). In order to create such a type
* variable, this method may be used to create an interim type variable, where the (overridden) upper and lower
* bounds should only reference the type variable returned by {@link TypeParameterElement#asType()}. As a second
* step, {@link #substitute(TypeMirror, Map)} may then be used to substitute the original type variable with the
* interim type variable. The result will be a fresh type variable with the overridden bounds, and these bounds
* will reference the fresh type variable instead of the original type variable.
*
* @param typeParameter the formal type parameter
* @param upperBound the upper bound for the new type variable, may contain recursive references to
* {@code typeParameter.asType()}
* @param lowerBound the lower bound for the new type variable, may contain recursive references to
* {@code typeParameter.asType()}
* @param capturedTypeArgument the wildcard type argument that new type variable captures as part of a capture
* conversion (§5.1.10 JLS), or {@code null} if the new type variable is not the result of a capture conversion
* @return the new type variable
* @throws NullPointerException if any of the first three arguments is null
*/
protected TypeVariable getTypeVariable(TypeParameterElement typeParameter, TypeMirror upperBound,
TypeMirror lowerBound, @Nullable WildcardType capturedTypeArgument) {
Objects.requireNonNull(typeParameter);
Objects.requireNonNull(upperBound);
Objects.requireNonNull(lowerBound);
requireValidType(upperBound);
requireValidType(lowerBound);
requireValidType(capturedTypeArgument);
TypeVariable typeVariable = createTypeVariable(typeParameter, capturedTypeArgument);
setTypeVariableBounds(typeVariable, upperBound, lowerBound);
return typeVariable;
}
/**
* Creates a new <em>unfinished</em> type variable for the given formal parameter.
*
* <p>Whenever this method is called within this class, the returned type variable is guaranteed to be passed to
* {@link #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)} before being used as a {@link TypeVariable}
* instance. That is, the returned type variable is considered to be under construction until being passed to
* {@link #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)}, and only after that method call, the type
* variable will have to satisfy the contract specified by interface {@link TypeVariable} and its super-interfaces.
*
* <p>Before {@link #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)} is called on the returned
* {@link TypeVariable}, calling either {@link TypeVariable#getUpperBound()} or {@link TypeVariable#getLowerBound()}
* must trigger an {@link IllegalStateException}.
*
* <p>Note that the previous paragraph does not guarantee that
* {@link #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)} is always called on the newly returned
* type-variable instance. If any exception occurs before the new type-variable could be used, then
* {@link #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)} may not be called (even if the exception is
* unrelated to the construction of the new type-variable instance).
*
* <p>The {@link TypeVariable} interface does not provide access to captured wildcard type arguments. It can be
* retrieved by calling {@link #capturedTypeArgument(TypeVariable)} instead.
*
* @param typeParameter the formal type parameter
* @param capturedTypeArgument the wildcard type argument that new type variable captures as part of a capture
* conversion (§5.1.10 JLS), or {@code null} if the new type variable is not the result of a capture conversion
* @return new unfinished type variable for the given formal parameter, which may not yet satisfy the contracts
* of {@link TypeVariable}
* @see #setTypeVariableBounds(TypeVariable, TypeMirror, TypeMirror)
* @see #capturedTypeArgument(TypeVariable)
* @throws NullPointerException if {@code typeParameter} is null
*/
protected abstract TypeVariable createTypeVariable(TypeParameterElement typeParameter,
@Nullable WildcardType capturedTypeArgument);
/**
* Sets the bounds of a type variable previously returned by
* {@link #createTypeVariable(TypeParameterElement, WildcardType)}.
*
* <p>Before an (unfinished) type-variable instance returned by
* {@link #createTypeVariable(TypeParameterElement, WildcardType)} is used by this class, this method is guaranteed
* to be called exactly once.
*
* <p>Implementations that create effectively immutable {@link TypeMirror} instances may use this method to "freeze"
* the given type-variable instance.
*
* @param typeVariable type variable previously returned by
* {@link #createTypeVariable(TypeParameterElement, WildcardType)}
* @param upperBound Upper bound for the given type variable. If no explicit upper bound is used, a
* {@link DeclaredType} representing {@link Object} will be passed.
* @param lowerBound Lower bound for the given type variable. This may a {@link NullType} instance, unless capture
* conversion produced a type variable with a non-trivial lower bound.
* @see #createTypeVariable(TypeParameterElement, WildcardType)
*/
protected abstract void setTypeVariableBounds(TypeVariable typeVariable, TypeMirror upperBound,
TypeMirror lowerBound);
/**
* Returns the captured wildcard type argument of the given type variable, or null if the given type variable is not
* the result of a capture conversion.
*
* <p>This method returns the wildcard type argument that was previously passed to
* {@link #createTypeVariable(TypeParameterElement, WildcardType)}.
*
* @param typeVariable the type variable that may be the result of a capture conversion
* @return the captured wildcard type argument, or null if not applicable
*/
@Nullable
protected abstract WildcardType capturedTypeArgument(TypeVariable typeVariable);
/**
* Returns a new intersection type. At least one bounds needs to be given.
*
* @param bounds the bounds of the new intersection type
* @return the new intersection type
* @throws IllegalArgumentException if the given array is empty
*/
public abstract IntersectionType getIntersectionType(TypeMirror... bounds);
/**
* Visitor of {@link TypeMirror} instances that appends the {@link String} representation to the
* {@link StringBuilder} instance passed as visitor argument.
*
* <p>This visitor is only used within this class and only on <em>valid</em> {@link TypeMirror} instances. Hence, it
* can be asserted that the visitor parameter is always non-null.
*
* @see #requireValidType(TypeMirror)
*/
private final class ToStringVisitor extends ExtendedTypeKindVisitor7<Void, StringBuilder> {
@Override
public Void visitPrimitive(PrimitiveType primitiveType, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
stringBuilder.append(primitiveType.getKind().toString().toLowerCase());
return null;
}
@Override
public Void visitNull(NullType nullType, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
stringBuilder.append("null");
return null;
}
@Override
public Void visitNoType(NoType noType, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
stringBuilder.append(noType.getKind().toString().toLowerCase());
return null;
}
@Override
public Void visitDeclared(DeclaredType declaredType, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
TypeMirror enclosingType = declaredType.getEnclosingType();
TypeElement typeElement = (TypeElement) declaredType.asElement();
if (enclosingType.getKind() == TypeKind.DECLARED) {
visitDeclared((DeclaredType) enclosingType, stringBuilder);
stringBuilder.append('.').append(typeElement.getSimpleName());
} else {
stringBuilder.append(typeElement.getQualifiedName());
}
List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments();
if (!typeArguments.isEmpty()) {
stringBuilder.append('<');
appendList(stringBuilder, typeArguments, ", ");
stringBuilder.append('>');
}
return null;
}
@Override
public Void visitArray(ArrayType arrayType, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
arrayType.getComponentType().accept(this, stringBuilder);
stringBuilder.append("[]");
return null;
}
@Override
public Void visitTypeVariable(TypeVariable typeVariable, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
@Nullable WildcardType capturedTypeArgument = capturedTypeArgument(typeVariable);
if (capturedTypeArgument != null) {
stringBuilder.append("capture<");
capturedTypeArgument.accept(this, stringBuilder);
stringBuilder.append('>');
} else {
stringBuilder.append(typeVariable.asElement().getSimpleName());
}
return null;
}
@Override
public Void visitWildcard(WildcardType wildcardTypeArgument, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
stringBuilder.append('?');
@Nullable TypeMirror extendsBound = wildcardTypeArgument.getExtendsBound();
if (extendsBound != null) {
stringBuilder.append(" extends ");
extendsBound.accept(this, stringBuilder);
}
@Nullable TypeMirror superBound = wildcardTypeArgument.getSuperBound();
if (superBound != null) {
stringBuilder.append(" super ");
superBound.accept(this, stringBuilder);
}
return null;
}
@Override
public Void visitIntersection(IntersectionType intersectionType, @Nullable StringBuilder stringBuilder) {
assert stringBuilder != null;
appendList(stringBuilder, intersectionType.getBounds(), " & ");
return null;
}
private void appendList(StringBuilder stringBuilder, List<? extends TypeMirror> types, String glue) {
assert !types.isEmpty();
boolean first = true;
for (TypeMirror type: types) {
if (first) {
first = false;
} else {
stringBuilder.append(glue);
}
type.accept(this, stringBuilder);
}
}
}
/**
* Returns the canonical string representation of the given type.
*
* @param type type
* @return canonical string representation of the given type
*/
public final String toString(TypeMirror type) {
requireValidType(Objects.requireNonNull(type));
StringBuilder stringBuilder = new StringBuilder(DEFAULT_STRING_BUILDER_SIZE);
type.accept(toStringVisitor, stringBuilder);
return stringBuilder.toString();
}
}