blob: fb291dbdb3135a6278d9861670e8f2760aaa056b [file] [log] [blame]
/*
* Copyright (C) 2016 The Dagger 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 dagger.internal.codegen.langmodel;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableSet;
import com.google.common.graph.Traverser;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.ListenableFuture;
import com.squareup.javapoet.ClassName;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import javax.inject.Inject;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.ExecutableType;
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.SimpleTypeVisitor8;
import javax.lang.model.util.Types;
/** Extension of {@link Types} that adds Dagger-specific methods. */
public final class DaggerTypes implements Types {
private final Types types;
private final DaggerElements elements;
@Inject
public DaggerTypes(Types types, DaggerElements elements) {
this.types = checkNotNull(types);
this.elements = checkNotNull(elements);
}
/**
* Returns the non-{@link Object} superclass of the type with the proper type parameters. An empty
* {@link Optional} is returned if there is no non-{@link Object} superclass.
*/
public Optional<DeclaredType> nonObjectSuperclass(DeclaredType type) {
return Optional.ofNullable(MoreTypes.nonObjectSuperclass(types, elements, type).orNull());
}
/**
* Returns the {@linkplain #directSupertypes(TypeMirror) supertype}s of a type in breadth-first
* order.
*/
public Iterable<TypeMirror> supertypes(TypeMirror type) {
return Traverser.<TypeMirror>forGraph(this::directSupertypes).breadthFirst(type);
}
/**
* Returns {@code type}'s single type argument.
*
* <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
*
* @throws IllegalArgumentException if {@code type} is not a declared type or has zero or more
* than one type arguments.
*/
public static TypeMirror unwrapType(TypeMirror type) {
TypeMirror unwrapped = unwrapTypeOrDefault(type, null);
checkArgument(unwrapped != null, "%s is a raw type", type);
return unwrapped;
}
/**
* Returns {@code type}'s single type argument, if one exists, or {@link Object} if not.
*
* <p>For example, if {@code type} is {@code List<Number>} this will return {@code Number}.
*
* @throws IllegalArgumentException if {@code type} is not a declared type or has more than one
* type argument.
*/
public TypeMirror unwrapTypeOrObject(TypeMirror type) {
return unwrapTypeOrDefault(type, elements.getTypeElement(Object.class).asType());
}
private static TypeMirror unwrapTypeOrDefault(TypeMirror type, TypeMirror defaultType) {
DeclaredType declaredType = MoreTypes.asDeclared(type);
TypeElement typeElement = MoreElements.asType(declaredType.asElement());
checkArgument(
!typeElement.getTypeParameters().isEmpty(),
"%s does not have a type parameter",
typeElement.getQualifiedName());
return getOnlyElement(declaredType.getTypeArguments(), defaultType);
}
/**
* Returns {@code type} wrapped in {@code wrappingClass}.
*
* <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
* Set.class}, this will return {@code Set<List<Number>>}.
*/
public DeclaredType wrapType(TypeMirror type, Class<?> wrappingClass) {
return types.getDeclaredType(elements.getTypeElement(wrappingClass), type);
}
/**
* Returns {@code type}'s single type argument wrapped in {@code wrappingClass}.
*
* <p>For example, if {@code type} is {@code List<Number>} and {@code wrappingClass} is {@code
* Set.class}, this will return {@code Set<Number>}.
*
* <p>If {@code type} has no type parameters, returns a {@link TypeMirror} for {@code
* wrappingClass} as a raw type.
*
* @throws IllegalArgumentException if {@code} has more than one type argument.
*/
public DeclaredType rewrapType(TypeMirror type, Class<?> wrappingClass) {
List<? extends TypeMirror> typeArguments = MoreTypes.asDeclared(type).getTypeArguments();
TypeElement wrappingType = elements.getTypeElement(wrappingClass);
switch (typeArguments.size()) {
case 0:
return getDeclaredType(wrappingType);
case 1:
return getDeclaredType(wrappingType, getOnlyElement(typeArguments));
default:
throw new IllegalArgumentException(type + " has more than 1 type argument");
}
}
/**
* Returns a publicly accessible type based on {@code type}:
*
* <ul>
* <li>If {@code type} is publicly accessible, returns it.
* <li>If not, but {@code type}'s raw type is publicly accessible, returns the raw type.
* <li>Otherwise returns {@link Object}.
* </ul>
*/
public TypeMirror publiclyAccessibleType(TypeMirror type) {
return accessibleType(
type, Accessibility::isTypePubliclyAccessible, Accessibility::isRawTypePubliclyAccessible);
}
/**
* Returns an accessible type in {@code requestingClass}'s package based on {@code type}:
*
* <ul>
* <li>If {@code type} is accessible from the package, returns it.
* <li>If not, but {@code type}'s raw type is accessible from the package, returns the raw type.
* <li>Otherwise returns {@link Object}.
* </ul>
*/
public TypeMirror accessibleType(TypeMirror type, ClassName requestingClass) {
return accessibleType(
type,
t -> Accessibility.isTypeAccessibleFrom(t, requestingClass.packageName()),
t -> Accessibility.isRawTypeAccessible(t, requestingClass.packageName()));
}
private TypeMirror accessibleType(
TypeMirror type,
Predicate<TypeMirror> accessibilityPredicate,
Predicate<TypeMirror> rawTypeAccessibilityPredicate) {
if (accessibilityPredicate.test(type)) {
return type;
} else if (type.getKind().equals(TypeKind.DECLARED)
&& rawTypeAccessibilityPredicate.test(type)) {
return getDeclaredType(MoreTypes.asTypeElement(type));
} else {
return elements.getTypeElement(Object.class).asType();
}
}
/**
* Throws {@link TypeNotPresentException} if {@code type} is an {@link
* javax.lang.model.type.ErrorType}.
*/
public static void checkTypePresent(TypeMirror type) {
type.accept(
// TODO(ronshapiro): Extract a base class that visits all components of a complex type
// and put it in auto.common
new SimpleTypeVisitor8<Void, Void>() {
@Override
public Void visitArray(ArrayType arrayType, Void p) {
return arrayType.getComponentType().accept(this, p);
}
@Override
public Void visitDeclared(DeclaredType declaredType, Void p) {
declaredType.getTypeArguments().forEach(t -> t.accept(this, p));
return null;
}
@Override
public Void visitError(ErrorType errorType, Void p) {
throw new TypeNotPresentException(type.toString(), null);
}
},
null);
}
private static final ImmutableSet<Class<?>> FUTURE_TYPES =
ImmutableSet.of(ListenableFuture.class, FluentFuture.class);
public static boolean isFutureType(TypeMirror type) {
return FUTURE_TYPES.stream().anyMatch(t -> MoreTypes.isTypeOf(t, type));
}
public static boolean hasTypeVariable(TypeMirror type) {
return type.accept(
new SimpleTypeVisitor8<Boolean, Void>() {
@Override
public Boolean visitArray(ArrayType arrayType, Void p) {
return arrayType.getComponentType().accept(this, p);
}
@Override
public Boolean visitDeclared(DeclaredType declaredType, Void p) {
return declaredType.getTypeArguments().stream().anyMatch(type -> type.accept(this, p));
}
@Override
public Boolean visitTypeVariable(TypeVariable t, Void aVoid) {
return true;
}
@Override
protected Boolean defaultAction(TypeMirror e, Void aVoid) {
return false;
}
},
null);
}
/**
* Resolves the type of the given executable element as a member of the given type. This may
* resolve type variables to concrete types, etc.
*/
public ExecutableType resolveExecutableType(ExecutableElement element, TypeMirror containerType) {
return MoreTypes.asExecutable(asMemberOf(MoreTypes.asDeclared(containerType), element));
}
// Implementation of Types methods, delegating to types.
@Override
public Element asElement(TypeMirror t) {
return types.asElement(t);
}
@Override
public boolean isSameType(TypeMirror t1, TypeMirror t2) {
return types.isSameType(t1, t2);
}
@Override
public boolean isSubtype(TypeMirror t1, TypeMirror t2) {
return types.isSubtype(t1, t2);
}
@Override
public boolean isAssignable(TypeMirror t1, TypeMirror t2) {
return types.isAssignable(t1, t2);
}
@Override
public boolean contains(TypeMirror t1, TypeMirror t2) {
return types.contains(t1, t2);
}
@Override
public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
return types.isSubsignature(m1, m2);
}
@Override
public List<? extends TypeMirror> directSupertypes(TypeMirror t) {
return types.directSupertypes(t);
}
@Override
public TypeMirror erasure(TypeMirror t) {
return types.erasure(t);
}
@Override
public TypeElement boxedClass(PrimitiveType p) {
return types.boxedClass(p);
}
@Override
public PrimitiveType unboxedType(TypeMirror t) {
return types.unboxedType(t);
}
@Override
public TypeMirror capture(TypeMirror t) {
return types.capture(t);
}
@Override
public PrimitiveType getPrimitiveType(TypeKind kind) {
return types.getPrimitiveType(kind);
}
@Override
public NullType getNullType() {
return types.getNullType();
}
@Override
public NoType getNoType(TypeKind kind) {
return types.getNoType(kind);
}
@Override
public ArrayType getArrayType(TypeMirror componentType) {
return types.getArrayType(componentType);
}
@Override
public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) {
return types.getWildcardType(extendsBound, superBound);
}
@Override
public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
return types.getDeclaredType(typeElem, typeArgs);
}
@Override
public DeclaredType getDeclaredType(
DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) {
return types.getDeclaredType(containing, typeElem, typeArgs);
}
@Override
public TypeMirror asMemberOf(DeclaredType containing, Element element) {
return types.asMemberOf(containing, element);
}
}