![JAR search and dependency download from the Maven repository](/logo.png)
dagger.internal.codegen.Key Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2014 Google, Inc.
*
* 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;
import com.google.auto.common.AnnotationMirrors;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimaps;
import com.google.common.util.concurrent.ListenableFuture;
import dagger.Binds;
import dagger.Multibindings;
import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.producers.Production;
import dagger.producers.internal.ProductionImplementation;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import javax.inject.Provider;
import javax.inject.Qualifier;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor6;
import javax.lang.model.util.Types;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.auto.common.MoreTypes.asExecutable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
import static dagger.internal.codegen.MapKeys.getMapKey;
import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType;
import static javax.lang.model.element.ElementKind.METHOD;
/**
* Represents a unique combination of {@linkplain TypeMirror type} and
* {@linkplain Qualifier qualifier} to which binding can occur.
*
* @author Gregory Kick
*/
@AutoValue
abstract class Key {
/** An object that is associated with a {@link Key}. */
interface HasKey {
/** The key associated with this object. */
Key key();
}
/**
* A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix
* for the type of this key.
*
* Despite documentation in {@link AnnotationMirror}, equals and hashCode aren't implemented
* to represent logical equality, so {@link AnnotationMirrors#equivalence()}
* provides this facility.
*/
abstract Optional> wrappedQualifier();
/**
* The type represented by this key.
*
* As documented in {@link TypeMirror}, equals and hashCode aren't implemented to represent
* logical equality, so {@link MoreTypes#equivalence()} wraps this type.
*/
abstract Equivalence.Wrapper wrappedType();
/**
* For multibinding contributions, this is the binding method element. Each multibound map and set
* is represented by a
* {@linkplain ProvisionBinding.Factory#syntheticMultibinding(DependencyRequest, Iterable)
* synthetic binding} that depends on the specific contributions to that map or set. Each such
* contribution binding therefore needs a key that identifies the specific binding, and not only
* the qualified type that is bound.
*/
abstract Optional bindingMethod();
/**
* A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix
* for the type of this key.
*/
Optional qualifier() {
return unwrapOptionalEquivalence(wrappedQualifier());
}
/**
* The type represented by this key.
*/
TypeMirror type() {
return wrappedType().get();
}
private static TypeMirror normalize(Types types, TypeMirror type) {
TypeKind kind = type.getKind();
return kind.isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
}
/**
* A key whose {@link #qualifier()} and {@link #bindingMethod()} are equivalent to this one's, but
* with {@code newType} (normalized) as its {@link #type()}.
*/
private Key withType(Types types, TypeMirror newType) {
return new AutoValue_Key(
wrappedQualifier(),
MoreTypes.equivalence().wrap(normalize(types, newType)),
bindingMethod());
}
/**
* A key whose {@link #qualifier()} and {@link #type()} are equivalent to this one's, but
* with {@code bindingMethod} as its {@link #bindingMethod()}.
*/
private Key withBindingMethod(SourceElement bindingMethod) {
return new AutoValue_Key(wrappedQualifier(), wrappedType(), Optional.of(bindingMethod));
}
/**
* A key whose {@link #qualifier()} and {@link #type()} are equivalent to this one's, but with an
* absent {@link #bindingMethod()}.
*/
Key withoutBindingMethod() {
return new AutoValue_Key(wrappedQualifier(), wrappedType(), Optional.absent());
}
boolean isValidMembersInjectionKey() {
return !qualifier().isPresent() && !type().getKind().equals(TypeKind.WILDCARD);
}
/**
* Returns true if the key is valid as an implicit key (that is, if it's valid for a just-in-time
* binding by discovering an {@code @Inject} constructor).
*/
boolean isValidImplicitProvisionKey(final Types types) {
// Qualifiers disqualify implicit provisioning.
if (qualifier().isPresent()) {
return false;
}
return type().accept(new SimpleTypeVisitor6() {
@Override protected Boolean defaultAction(TypeMirror e, Void p) {
return false; // Only declared types are allowed.
}
@Override public Boolean visitDeclared(DeclaredType type, Void ignored) {
// Non-classes or abstract classes aren't allowed.
TypeElement element = MoreElements.asType(type.asElement());
if (!element.getKind().equals(ElementKind.CLASS)
|| element.getModifiers().contains(Modifier.ABSTRACT)) {
return false;
}
// If the key has type arguments, validate that each type argument is declared.
// Otherwise the type argument may be a wildcard (or other type), and we can't
// resolve that to actual types.
for (TypeMirror arg : type.getTypeArguments()) {
if (arg.getKind() != TypeKind.DECLARED) {
return false;
}
}
// Also validate that the key is not the erasure of a generic type.
// If it is, that means the user referred to Foo as just 'Foo',
// which we don't allow. (This is a judgement call -- we *could*
// allow it and instantiate the type bounds... but we don't.)
return MoreTypes.asDeclared(element.asType()).getTypeArguments().isEmpty()
|| !types.isSameType(types.erasure(element.asType()), type());
}
}, null);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(Key.class)
.omitNullValues()
.add("qualifier", qualifier().orNull())
.add("type", type())
.add("bindingMethod", bindingMethod().orNull())
.toString();
}
/**
* Indexes {@code haveKeys} by {@link HasKey#key()}.
*/
static ImmutableSetMultimap indexByKey(Iterable haveKeys) {
return ImmutableSetMultimap.copyOf(
Multimaps.index(
haveKeys,
new Function() {
@Override
public Key apply(HasKey hasKey) {
return hasKey.key();
}
}));
}
/**
* Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Wrapper} for that type.
*/
private static Optional> wrapOptionalInEquivalence(
Equivalence equivalence, Optional optional) {
return optional.isPresent()
? Optional.of(equivalence.wrap(optional.get()))
: Optional.>absent();
}
/**
* Unwraps an {@link Optional} of a {@link Wrapper} into an {@code Optional} of the underlying
* type.
*/
private static Optional unwrapOptionalEquivalence(
Optional> wrappedOptional) {
return wrappedOptional.isPresent()
? Optional.of(wrappedOptional.get().get())
: Optional.absent();
}
static final class Factory {
private final Types types;
private final Elements elements;
Factory(Types types, Elements elements) {
this.types = checkNotNull(types);
this.elements = checkNotNull(elements);
}
private TypeElement getSetElement() {
return elements.getTypeElement(Set.class.getCanonicalName());
}
private TypeElement getMapElement() {
return elements.getTypeElement(Map.class.getCanonicalName());
}
private TypeElement getProviderElement() {
return elements.getTypeElement(Provider.class.getCanonicalName());
}
private TypeElement getProducerElement() {
return elements.getTypeElement(Producer.class.getCanonicalName());
}
private TypeElement getClassElement(Class> cls) {
return elements.getTypeElement(cls.getCanonicalName());
}
Key forComponentMethod(ExecutableElement componentMethod) {
checkNotNull(componentMethod);
checkArgument(componentMethod.getKind().equals(METHOD));
TypeMirror returnType = normalize(types, componentMethod.getReturnType());
return forMethod(componentMethod, returnType);
}
Key forProductionComponentMethod(ExecutableElement componentMethod) {
checkNotNull(componentMethod);
checkArgument(componentMethod.getKind().equals(METHOD));
TypeMirror returnType = normalize(types, componentMethod.getReturnType());
TypeMirror keyType = returnType;
if (MoreTypes.isTypeOf(ListenableFuture.class, returnType)) {
keyType = Iterables.getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
}
return forMethod(componentMethod, keyType);
}
Key forSubcomponentBuilderMethod(
ExecutableElement subcomponentBuilderMethod, DeclaredType declaredContainer) {
checkNotNull(subcomponentBuilderMethod);
checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
ExecutableType resolvedMethod =
asExecutable(types.asMemberOf(declaredContainer, subcomponentBuilderMethod));
TypeMirror returnType = normalize(types, resolvedMethod.getReturnType());
return forMethod(subcomponentBuilderMethod, returnType);
}
Key forProvidesMethod(SourceElement sourceElement) {
return forProvidesOrProducesMethod(sourceElement, getProviderElement());
}
Key forProducesMethod(SourceElement sourceElement) {
return forProvidesOrProducesMethod(sourceElement, getProducerElement());
}
private Key forProvidesOrProducesMethod(
SourceElement sourceElement, TypeElement frameworkType) {
checkArgument(sourceElement.element().getKind().equals(METHOD));
ExecutableElement method = MoreElements.asExecutable(sourceElement.element());
ExecutableType methodType =
MoreTypes.asExecutable(sourceElement.asMemberOfContributingType(types));
ContributionType contributionType = ContributionType.fromBindingMethod(method);
TypeMirror returnType = normalize(types, methodType.getReturnType());
if (frameworkType.equals(getProducerElement())
&& MoreTypes.isTypeOf(ListenableFuture.class, returnType)) {
returnType = Iterables.getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
}
TypeMirror keyType =
providesOrProducesKeyType(returnType, method, contributionType, frameworkType);
Key key = forMethod(method, keyType);
return contributionType.equals(ContributionType.UNIQUE)
? key
: key.withBindingMethod(sourceElement);
}
/**
* Returns the key for a method in a {@link Multibindings @Multibindings} interface.
*
* The key's type is either {@code Set} or {@code Map>}, where {@code F} is either
* {@link Provider} or {@link Producer}, depending on {@code bindingType}.
*/
Key forMultibindingsMethod(
BindingType bindingType, ExecutableType executableType, ExecutableElement method) {
checkArgument(method.getKind().equals(METHOD), "%s must be a method", method);
TypeElement factoryType =
elements.getTypeElement(bindingType.frameworkClass().getCanonicalName());
TypeMirror returnType = normalize(types, executableType.getReturnType());
TypeMirror keyType =
MapType.isMap(returnType)
? mapOfFrameworkType(
MapType.from(returnType).keyType(),
factoryType,
MapType.from(returnType).valueType())
: returnType;
return forMethod(method, keyType);
}
/** Returns the key bound by a {@link Binds} method. */
Key forBindsMethod(SourceElement bindsMethodElement) {
ExecutableElement method = MoreElements.asExecutable(bindsMethodElement.element());
ExecutableType methodType =
MoreTypes.asExecutable(bindsMethodElement.asMemberOfContributingType(types));
checkArgument(isAnnotationPresent(method, Binds.class));
TypeMirror returnType = normalize(types, methodType.getReturnType());
return forMethod(method, returnType);
}
/** Returns the key for the single parameter of a {@link Binds} method. */
Key forBindParameter(SourceElement bindsMethodElement) {
ExecutableElement method = MoreElements.asExecutable(bindsMethodElement.element());
VariableElement parameterElement = Iterables.getOnlyElement(method.getParameters());
ExecutableType methodType =
MoreTypes.asExecutable(bindsMethodElement.asMemberOfContributingType(types));
checkArgument(isAnnotationPresent(method, Binds.class));
TypeMirror parameterType = Iterables.getOnlyElement(methodType.getParameterTypes());
return forQualifiedType(getQualifier(parameterElement), parameterType);
}
private TypeMirror providesOrProducesKeyType(
TypeMirror returnType,
ExecutableElement method,
ContributionType contributionType,
TypeElement frameworkType) {
switch (contributionType) {
case UNIQUE:
return returnType;
case SET:
return types.getDeclaredType(getSetElement(), returnType);
case MAP:
return mapOfFrameworkType(mapKeyType(method), frameworkType, returnType);
case SET_VALUES:
// TODO(gak): do we want to allow people to use "covariant return" here?
checkArgument(SetType.isSet(returnType));
return returnType;
default:
throw new AssertionError();
}
}
/**
* Returns {@code Map>}.
*/
private TypeMirror mapOfFrameworkType(
TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) {
return types.getDeclaredType(
getMapElement(), keyType, types.getDeclaredType(frameworkType, valueType));
}
private TypeMirror mapKeyType(ExecutableElement method) {
AnnotationMirror mapKeyAnnotation = getMapKey(method).get();
return MapKeys.unwrapValue(mapKeyAnnotation).isPresent()
? getUnwrappedMapKeyType(mapKeyAnnotation.getAnnotationType(), types)
: mapKeyAnnotation.getAnnotationType();
}
private Key forMethod(ExecutableElement method, TypeMirror keyType) {
return new AutoValue_Key(
wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), getQualifier(method)),
MoreTypes.equivalence().wrap(keyType),
Optional.absent());
}
Key forInjectConstructorWithResolvedType(TypeMirror type) {
return new AutoValue_Key(
Optional.>absent(),
MoreTypes.equivalence().wrap(type),
Optional.absent());
}
Key forComponent(TypeMirror type) {
return new AutoValue_Key(
Optional.>absent(),
MoreTypes.equivalence().wrap(normalize(types, type)),
Optional.absent());
}
Key forMembersInjectedType(TypeMirror type) {
return new AutoValue_Key(
Optional.>absent(),
MoreTypes.equivalence().wrap(normalize(types, type)),
Optional.absent());
}
Key forQualifiedType(Optional qualifier, TypeMirror type) {
return new AutoValue_Key(
wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), qualifier),
MoreTypes.equivalence().wrap(normalize(types, type)),
Optional.absent());
}
Key forProductionExecutor() {
return forQualifiedType(
Optional.of(SimpleAnnotationMirror.of(getClassElement(Production.class))),
getClassElement(Executor.class).asType());
}
Key forProductionImplementationExecutor() {
return forQualifiedType(
Optional.of(SimpleAnnotationMirror.of(getClassElement(ProductionImplementation.class))),
getClassElement(Executor.class).asType());
}
/**
* Optionally extract a {@link Key} for the underlying provision binding(s) if such a
* valid key can be inferred from the given key. Specifically, if the key represents a
* {@link Map}{@code }, a key of {@code Map>} will be returned.
*/
Optional implicitMapProviderKeyFrom(Key possibleMapKey) {
return maybeWrapMapValue(possibleMapKey, Provider.class);
}
/**
* Optionally extract a {@link Key} for the underlying production binding(s) if such a
* valid key can be inferred from the given key. Specifically, if the key represents a
* {@link Map}{@code } or {@code Map>}, a key of
* {@code Map>} will be returned.
*/
Optional implicitMapProducerKeyFrom(Key possibleMapKey) {
return maybeRewrapMapValue(possibleMapKey, Produced.class, Producer.class)
.or(maybeWrapMapValue(possibleMapKey, Producer.class));
}
/**
* Returns a key of {@link Map}{@code >} if the input key represents a
* {@code Map>}.
*/
private Optional maybeRewrapMapValue(
Key possibleMapKey, Class> currentWrappingClass, Class> newWrappingClass) {
checkArgument(!currentWrappingClass.equals(newWrappingClass));
if (MapType.isMap(possibleMapKey.type())) {
MapType mapType = MapType.from(possibleMapKey.type());
if (mapType.valuesAreTypeOf(currentWrappingClass)) {
TypeElement wrappingElement = getClassElement(newWrappingClass);
if (wrappingElement == null) {
// This target might not be compiled with Producers, so wrappingClass might not have an
// associated element.
return Optional.absent();
}
DeclaredType wrappedValueType =
types.getDeclaredType(
wrappingElement, mapType.unwrappedValueType(currentWrappingClass));
TypeMirror wrappedMapType =
types.getDeclaredType(getMapElement(), mapType.keyType(), wrappedValueType);
return Optional.of(possibleMapKey.withType(types, wrappedMapType));
}
}
return Optional.absent();
}
/**
* Returns a key of {@link Map}{@code >} if the input key represents a
* {@code Map}.
*/
private Optional maybeWrapMapValue(Key possibleMapKey, Class> wrappingClass) {
if (MapType.isMap(possibleMapKey.type())) {
MapType mapType = MapType.from(possibleMapKey.type());
if (!mapType.valuesAreTypeOf(wrappingClass)) {
TypeElement wrappingElement = getClassElement(wrappingClass);
if (wrappingElement == null) {
// This target might not be compiled with Producers, so wrappingClass might not have an
// associated element.
return Optional.absent();
}
DeclaredType wrappedValueType =
types.getDeclaredType(wrappingElement, mapType.valueType());
TypeMirror wrappedMapType =
types.getDeclaredType(getMapElement(), mapType.keyType(), wrappedValueType);
return Optional.of(possibleMapKey.withType(types, wrappedMapType));
}
}
return Optional.absent();
}
/**
* Optionally extract a {@link Key} for a {@code Set} if the given key is for
* {@code Set>}.
*/
Optional implicitSetKeyFromProduced(Key possibleSetOfProducedKey) {
if (MoreTypes.isType(possibleSetOfProducedKey.type())
&& MoreTypes.isTypeOf(Set.class, possibleSetOfProducedKey.type())) {
TypeMirror argType =
MoreTypes.asDeclared(possibleSetOfProducedKey.type()).getTypeArguments().get(0);
if (MoreTypes.isType(argType) && MoreTypes.isTypeOf(Produced.class, argType)) {
TypeMirror producedArgType = MoreTypes.asDeclared(argType).getTypeArguments().get(0);
TypeMirror setType = types.getDeclaredType(getSetElement(), producedArgType);
return Optional.of(possibleSetOfProducedKey.withType(types, setType));
}
}
return Optional.absent();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy