dagger.internal.codegen.Key.java Source code

Java tutorial

Introduction

Here is the source code for dagger.internal.codegen.Key.java

Source

/*
 * Copyright (C) 2014 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;

import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.auto.common.MoreTypes.asExecutable;
import static com.google.auto.common.MoreTypes.isType;
import static com.google.auto.common.MoreTypes.isTypeOf;
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 static dagger.internal.codegen.InjectionAnnotations.getQualifier;
import static dagger.internal.codegen.MapKeys.getMapKey;
import static dagger.internal.codegen.MapKeys.getUnwrappedMapKeyType;
import static dagger.internal.codegen.MoreAnnotationMirrors.unwrapOptionalEquivalence;
import static dagger.internal.codegen.MoreAnnotationMirrors.wrapOptionalInEquivalence;
import static dagger.internal.codegen.Optionals.firstPresent;
import static dagger.internal.codegen.Util.*;
import static javax.lang.model.element.ElementKind.METHOD;

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.auto.value.extension.memoized.Memoized;
import com.google.common.base.Equivalence;
import com.google.common.base.Joiner;
import com.google.common.collect.*;
import com.google.common.util.concurrent.ListenableFuture;
import com.squareup.javapoet.ClassName;
import dagger.Binds;
import dagger.BindsOptionalOf;
import dagger.MapKey;
import dagger.multibindings.*;
import dagger.producers.Produced;
import dagger.producers.Producer;
import dagger.producers.Production;
import dagger.producers.internal.ProductionImplementation;
import dagger.producers.monitoring.ProductionComponentMonitor;
import dagger.releasablereferences.ForReleasableReferences;
import dagger.releasablereferences.ReleasableReferenceManager;
import dagger.releasablereferences.TypedReleasableReferenceManager;

import java.io.Serializable;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Stream;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Qualifier;
import javax.lang.model.element.*;
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;

/**
 * Represents a unique combination of {@linkplain TypeMirror type} and
 * {@linkplain Qualifier qualifier} to which binding can occur.
 *
 * @author Gregory Kick
 */
@AutoValue
abstract class Key implements Serializable {

    /** An object that is associated with a {@link Key}. */
    interface HasKey extends Serializable {
        /** 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<Equivalence.Wrapper<AnnotationMirror>> 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<TypeMirror> wrappedType();

    /**
     * Distinguishes keys for multibinding contributions that share a {@link #type()} and {@link
     * #qualifier()}.
     *
     * <p>Each multibound map and set has a {@linkplain
     * ProvisionBinding.Factory#syntheticMultibinding(Key, Iterable) synthetic multibinding} that
     * depends on the specific contributions to that map or set using keys that identify those
     * multibinding contributions.
     *
     * <p>Absent except for multibinding contributions.
     */
    abstract Optional<MultibindingContributionIdentifier> multibindingContributionIdentifier();

    abstract Builder toBuilder();

    @Memoized
    @Override
    public abstract int hashCode();

    static Builder builder(TypeMirror type) {
        return new dagger.internal.codegen.AutoValue_Key.Builder().type(type);
    }

    @AutoValue.Builder
    abstract static class Builder {
        abstract Builder wrappedType(Equivalence.Wrapper<TypeMirror> wrappedType);

        Builder type(TypeMirror type) {
            return wrappedType(MoreTypes.equivalence().wrap(checkNotNull(type)));
        }

        abstract Builder wrappedQualifier(Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedQualifier);

        abstract Builder wrappedQualifier(Equivalence.Wrapper<AnnotationMirror> wrappedQualifier);

        Builder qualifier(AnnotationMirror qualifier) {
            return wrappedQualifier(AnnotationMirrors.equivalence().wrap(checkNotNull(qualifier)));
        }

        Builder qualifier(Optional<AnnotationMirror> qualifier) {
            return wrappedQualifier(wrapOptionalInEquivalence(checkNotNull(qualifier)));
        }

        Builder qualifier(TypeElement annotationType) {
            return qualifier(SimpleAnnotationMirror.of(annotationType));
        }

        abstract Builder multibindingContributionIdentifier(
                Optional<MultibindingContributionIdentifier> identifier);

        abstract Builder multibindingContributionIdentifier(MultibindingContributionIdentifier identifier);

        abstract Key build();
    }

    /**
     * An object that identifies a multibinding contribution method and the module class that
     * contributes it to the graph.
     *
     * @see Key#multibindingContributionIdentifier()
     */
    static final class MultibindingContributionIdentifier {
        private final String identifierString;
        private final ExecutableElement bindingMethod;
        private final TypeElement contributingModule;

        MultibindingContributionIdentifier(ExecutableElement bindingMethod, TypeElement contributingModule) {
            this.bindingMethod = bindingMethod;
            this.contributingModule = contributingModule;
            this.identifierString = String.format("%s#%s", contributingModule.getQualifiedName(),
                    bindingMethod.getSimpleName());
        }

        /**
         * {@inheritDoc}
         *
         * <p>The returned string is human-readable and distinguishes the keys in the same way as the
         * whole object.
         */
        @Override
        public String toString() {
            return identifierString;
        }

        public ClassName getDelegateTypeName() {
            final TypeMirror returnType = bindingMethod.getReturnType();

            // find qualifier annotations
            final Optional<? extends AnnotationMirror> qualifier = bindingMethod.getAnnotationMirrors().stream()
                    .filter(this::hasQualifierAnnotation).findFirst();

            // find mapkey annotations
            final Optional<? extends AnnotationMirror> mapKey = bindingMethod.getAnnotationMirrors().stream()
                    .filter(this::hasMapKeyAnnotation).findFirst();

            Optional<String> mapValue = Optional.empty();
            Optional<String> qualifierValue = Optional.empty();

            String simpleMapKeyName = "";
            String simpleMapValueName = "";
            String simpleQualifierName = "";
            String simpleQualifierValue = "";

            if (mapKey.isPresent()) {
                mapValue = mapKey.get().getElementValues().entrySet().stream()
                        .filter(e -> e.getKey().getSimpleName().toString().equals("value"))
                        .map(e -> Util.extractClassName(e.getValue().getValue().toString().replace(".class", "")))
                        .findFirst();
                simpleMapKeyName = MoreAnnotationMirrors.simpleName(mapKey.get()).toString();
                if (mapValue.isPresent()) {
                    simpleMapValueName = mapValue.get();
                }
            }

            if (qualifier.isPresent()) {
                qualifierValue = qualifier.get().getElementValues().entrySet().stream()
                        .filter(e -> e.getKey().getSimpleName().toString().equals("value"))
                        .filter(e -> e.getKey().getReturnType().toString().equals(String.class.getName()))
                        .map(e -> e.getValue().getValue().toString()).findFirst();
                simpleQualifierName = MoreAnnotationMirrors.simpleName(qualifier.get()).toString();
                if (qualifierValue.isPresent()) {
                    simpleQualifierValue = qualifierValue.get();
                }
            }

            StringBuilder sb = new StringBuilder();
            sb.append(capitalize(simpleMapKeyName));
            sb.append(capitalize(simpleMapValueName));
            if (qualifier.isPresent()) {
                sb.append(capitalize(simpleQualifierName));
                if (!simpleQualifierValue.isEmpty()) {
                    sb.append(capitalize(simpleQualifierValue));
                } else {
                    sb.append(capitalize(extractClassName(typeToString(returnType))));
                }
            } else {
                sb.append(capitalize(extractClassName(typeToString(returnType))));
            }

            return ClassName.bestGuess(String.format("delegates.%sDelegate", sb.toString()));

        }

        public String getDelegateFieldName() {
            final TypeMirror returnType = bindingMethod.getReturnType();

            // find qualifier annotations
            final Optional<? extends AnnotationMirror> qualifier = bindingMethod.getAnnotationMirrors().stream()
                    .filter(this::hasQualifierAnnotation).findFirst();

            // find mapkey annotations
            final Optional<? extends AnnotationMirror> mapKey = bindingMethod.getAnnotationMirrors().stream()
                    .filter(this::hasMapKeyAnnotation).findFirst();

            Optional<String> mapValue = Optional.empty();
            Optional<String> qualifierValue = Optional.empty();

            String simpleMapKeyName = "";
            String simpleMapValueName = "";
            String simpleQualifierName = "";
            String simpleQualifierValue = "";

            if (mapKey.isPresent()) {
                mapValue = mapKey.get().getElementValues().entrySet().stream()
                        .filter(e -> e.getKey().getSimpleName().contentEquals("value"))
                        .map(e -> Util.extractClassName(e.getValue().getValue().toString().replace(".class", "")))
                        .findFirst();
                simpleMapKeyName = MoreAnnotationMirrors.simpleName(mapKey.get()).toString();
                if (mapValue.isPresent()) {
                    simpleMapValueName = mapValue.get();
                }
            }

            if (qualifier.isPresent()) {
                qualifierValue = qualifier.get().getElementValues().entrySet().stream()
                        .filter(e -> e.getKey().getSimpleName().contentEquals("value"))
                        .filter(e -> e.getKey().getReturnType().toString().equals(String.class.getName()))
                        .map(e -> e.getValue().getValue().toString()).findFirst();
                simpleQualifierName = MoreAnnotationMirrors.simpleName(qualifier.get()).toString();
                if (qualifierValue.isPresent()) {
                    simpleQualifierValue = qualifierValue.get();
                }
            }

            StringBuilder sb = new StringBuilder();
            sb.append(lowerCaseFirstLetter(simpleMapKeyName));
            sb.append(transformValue(simpleMapValueName, sb));
            if (qualifier.isPresent()) {
                sb.append(transformValue(simpleQualifierName, sb));
                if (!simpleQualifierValue.isEmpty()) {
                    sb.append(transformValue(simpleQualifierValue, sb));
                } else {
                    sb.append(transformValue(extractClassName(typeToString(returnType)), sb));
                }
            } else {
                sb.append(transformValue(extractClassName(typeToString(returnType)), sb));
            }

            return sb.toString();
        }

        protected String transformValue(String simpleMapValueName, StringBuilder sb) {
            return sb.length() == 0 ? lowerCaseFirstLetter(simpleMapValueName) : capitalize(simpleMapValueName);
        }

        private boolean hasMapKeyAnnotation(AnnotationMirror o) {
            return MoreElements.isAnnotationPresent(o.getAnnotationType().asElement(), MapKey.class);
        }

        private boolean hasQualifierAnnotation(AnnotationMirror o) {
            return MoreElements.isAnnotationPresent(o.getAnnotationType().asElement(), Qualifier.class);
        }

        private static String getCapitalizedAnnotationValue(AnnotationMirror annotation) {
            final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotation
                    .getElementValues();
            for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues
                    .entrySet()) {
                if (entry.getKey().getSimpleName().contentEquals("value")) {
                    final String original = entry.getValue().getValue().toString();
                    if (!original.isEmpty()) {
                        return capitalize(original);
                    }
                }
            }
            throw new IllegalStateException("value not found");
        }

        private static String capitalize(String original) {
            if (original == null || original.length() == 0) {
                return original;
            }
            return original.substring(0, 1).toUpperCase() + original.substring(1);
        }

        private static java.util.Optional<? extends AnnotationMirror> getAnnotationMirror(TypeMirror typeMirror) {
            final ImmutableList<String> annotations = ImmutableList.of(Named.class.getName(),
                    StringKey.class.getName(), IntKey.class.getName(), LongKey.class.getName(),
                    MapKey.class.getName(), ClassKey.class.getName());

            return typeMirror.getAnnotationMirrors().stream()
                    .filter(e -> Util.isAnnotationPresent(MoreTypes.asElement(typeMirror), e.getAnnotationType()))
                    .findFirst();
        }

        private static java.util.Optional<? extends AnnotationMirror> getAnnotationMirror(
                ExecutableElement method) {
            final ImmutableList<String> annotations = ImmutableList.of(Named.class.getName(),
                    StringKey.class.getName(), IntKey.class.getName(), LongKey.class.getName(),
                    MapKey.class.getName(), ClassKey.class.getName());

            return method.getAnnotationMirrors().stream()
                    .filter(e -> annotations.contains(e.getAnnotationType().toString())).findFirst();
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof MultibindingContributionIdentifier
                    && ((MultibindingContributionIdentifier) obj).identifierString.equals(this.identifierString);
        }

        @Override
        public int hashCode() {
            return identifierString.hashCode();
        }
    }

    /**
     * A {@link javax.inject.Qualifier} annotation that provides a unique namespace prefix
     * for the type of this key.
     */
    Optional<AnnotationMirror> qualifier() {
        return unwrapOptionalEquivalence(wrappedQualifier());
    }

    /**
     * The type represented by this key.
     */
    TypeMirror type() {
        return wrappedType().get();
    }

    /**
     * A key whose {@link #qualifier()} and {@link #type()} are equivalent to this one's, but without
     * a {@link #multibindingContributionIdentifier()}.
     */
    Key withoutMultibindingContributionIdentifier() {
        return toBuilder().multibindingContributionIdentifier(Optional.empty()).build();
    }

    boolean isValidMembersInjectionKey() {
        return !qualifier().isPresent() && type().getKind().equals(TypeKind.DECLARED);
    }

    /**
     * Returns {@code true} if this 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(Types types) {
        return isValidImplicitProvisionKey(qualifier(), type(), types);
    }

    /**
     * Returns {@code true} if a key with {@code qualifier} and {@code type} is valid as an implicit
     * key (that is, if it's valid for a just-in-time binding by discovering an {@code @Inject}
     * constructor).
     */
    static boolean isValidImplicitProvisionKey(Optional<? extends AnnotationMirror> qualifier, TypeMirror type,
            final Types types) {
        // Qualifiers disqualify implicit provisioning.
        if (qualifier.isPresent()) {
            return false;
        }

        return type.accept(new SimpleTypeVisitor6<Boolean, Void>(false) {
            @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<T> 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);
    }

    /**
     * {@inheritDoc}
     *
     * <p>The returned string is equal to another key's if and only if this key is {@link
     * #equals(Object)} to it.
     */
    @Override
    public String toString() {
        return Joiner.on(' ').skipNulls().join(qualifier().orElse(null), type(),
                multibindingContributionIdentifier().orElse(null));
    }

    /**
     * Indexes {@code haveKeys} by {@link HasKey#key()}.
     */
    static <T extends HasKey> ImmutableSetMultimap<Key, T> indexByKey(Iterable<T> haveKeys) {
        return ImmutableSetMultimap.copyOf(Multimaps.index(haveKeys, HasKey::key));
    }

    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 getClassElement(Class<?> cls) {
            return elements.getTypeElement(cls.getCanonicalName());
        }

        private TypeMirror boxPrimitives(TypeMirror type) {
            return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
        }

        private DeclaredType setOf(TypeMirror elementType) {
            return types.getDeclaredType(getClassElement(Set.class), boxPrimitives(elementType));
        }

        private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
            return types.getDeclaredType(getClassElement(Map.class), boxPrimitives(keyType),
                    boxPrimitives(valueType));
        }

        /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
        private TypeMirror mapOfFrameworkType(TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) {
            return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType)));
        }

        private DeclaredType typedReleasableReferenceManagerOf(DeclaredType metadataType) {
            return types.getDeclaredType(getClassElement(TypedReleasableReferenceManager.class), metadataType);
        }

        Key forComponentMethod(ExecutableElement componentMethod) {
            checkArgument(componentMethod.getKind().equals(METHOD));
            return forMethod(componentMethod, componentMethod.getReturnType());
        }

        Key forProductionComponentMethod(ExecutableElement componentMethod) {
            checkArgument(componentMethod.getKind().equals(METHOD));
            TypeMirror returnType = componentMethod.getReturnType();
            TypeMirror keyType = isTypeOf(ListenableFuture.class, returnType)
                    ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments())
                    : returnType;
            return forMethod(componentMethod, keyType);
        }

        Key forSubcomponentBuilderMethod(ExecutableElement subcomponentBuilderMethod,
                DeclaredType declaredContainer) {
            checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
            ExecutableType resolvedMethod = asExecutable(
                    types.asMemberOf(declaredContainer, subcomponentBuilderMethod));
            return builder(resolvedMethod.getReturnType()).build();
        }

        Key forSubcomponentBuilder(TypeMirror builderType) {
            return builder(builderType).build();
        }

        Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) {
            return forBindingMethod(method, contributingModule, Optional.of(getClassElement(Provider.class)));
        }

        Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) {
            return forBindingMethod(method, contributingModule, Optional.of(getClassElement(Producer.class)));
        }

        /** Returns the key bound by a {@link Binds} method. */
        Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) {
            checkArgument(isAnnotationPresent(method, Binds.class));
            return forBindingMethod(method, contributingModule, Optional.empty());
        }

        /** Returns the base key bound by a {@link BindsOptionalOf} method. */
        Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) {
            checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
            return forBindingMethod(method, contributingModule, Optional.empty());
        }

        private Key forBindingMethod(ExecutableElement method, TypeElement contributingModule,
                Optional<TypeElement> frameworkType) {
            checkArgument(method.getKind().equals(METHOD));
            ExecutableType methodType = MoreTypes
                    .asExecutable(types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method));
            ContributionType contributionType = ContributionType.fromBindingMethod(method);
            TypeMirror returnType = methodType.getReturnType();
            if (frameworkType.isPresent() && frameworkType.get().equals(getClassElement(Producer.class))
                    && isType(returnType)) {
                if (isTypeOf(ListenableFuture.class, returnType)) {
                    returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
                } else if (contributionType.equals(ContributionType.SET_VALUES) && SetType.isSet(returnType)) {
                    SetType setType = SetType.from(returnType);
                    if (setType.elementsAreTypeOf(ListenableFuture.class)) {
                        returnType = types.getDeclaredType(getClassElement(Set.class),
                                setType.unwrappedElementType(ListenableFuture.class));
                    }
                }
            }
            TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType);
            Key key = forMethod(method, keyType);
            return contributionType
                    .equals(ContributionType.UNIQUE)
                            ? key
                            : key.toBuilder()
                                    .multibindingContributionIdentifier(
                                            new MultibindingContributionIdentifier(method, contributingModule))
                                    .build();
        }

        /**
         * Returns the key for a {@link Multibinds @Multibinds} method.
         *
         * <p>The key's type is either {@code Set<T>} or {@code Map<K, F<V>>}, where {@code F} is either
         * {@link Provider} or {@link Producer}, depending on {@code bindingType}.
         */
        Key forMultibindsMethod(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 = executableType.getReturnType();
            TypeMirror keyType = MapType.isMap(returnType)
                    ? mapOfFrameworkType(MapType.from(returnType).keyType(), factoryType,
                            MapType.from(returnType).valueType())
                    : returnType;
            return forMethod(method, keyType);
        }

        private TypeMirror bindingMethodKeyType(TypeMirror returnType, ExecutableElement method,
                ContributionType contributionType, Optional<TypeElement> frameworkType) {
            switch (contributionType) {
            case UNIQUE:
                return returnType;
            case SET:
                return setOf(returnType);
            case MAP:
                if (frameworkType.isPresent()) {
                    return mapOfFrameworkType(mapKeyType(method), frameworkType.get(), returnType);
                } else {
                    return mapOf(mapKeyType(method), 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 the key for a binding associated with a {@link DelegateDeclaration}.
         *
         * If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
         * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code
         * delegateDeclaration} is not a map contribution, its key is returned.
         */
        Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType) {
            return delegateDeclaration.contributionType().equals(ContributionType.MAP)
                    ? wrapMapValue(delegateDeclaration.key(), frameworkType)
                    : delegateDeclaration.key();
        }

        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 forQualifiedType(getQualifier(method), keyType);
        }

        Key forInjectConstructorWithResolvedType(TypeMirror type) {
            return builder(type).build();
        }

        Key forComponent(TypeMirror type) {
            return builder(type).build();
        }

        Key forMembersInjectedType(TypeMirror type) {
            return builder(type).build();
        }

        Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
            return builder(boxPrimitives(type)).qualifier(qualifier).build();
        }

        Key forProductionExecutor() {
            return builder(getClassElement(Executor.class).asType()).qualifier(getClassElement(Production.class))
                    .build();
        }

        Key forProductionImplementationExecutor() {
            return builder(getClassElement(Executor.class).asType())
                    .qualifier(getClassElement(ProductionImplementation.class)).build();
        }

        Key forProductionComponentMonitor() {
            return builder(getClassElement(ProductionComponentMonitor.class).asType()).build();
        }

        /**
         * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys
         * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on
         * the classpath).
         */
        ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) {
            return Stream.of(implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey))
                    .filter(Optional::isPresent).map(Optional::get).collect(toImmutableSet());
        }

        /**
         * 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 <K, V>}, a key of {@code Map<K, Provider<V>>} will be returned.
         */
        Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
            return wrapMapKey(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 <K, V>} or {@code Map<K, Produced<V>>}, a key of
         * {@code Map<K, Producer<V>>} will be returned.
         */
        Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
            return firstPresent(rewrapMapKey(possibleMapKey, Produced.class, Producer.class),
                    wrapMapKey(possibleMapKey, Producer.class));
        }

        /**
         * Keys for map contributions from {@link dagger.Provides} and {@link dagger.producers.Produces}
         * are in the form {@code Map<K, Framework<V>>}, but keys for {@link Binds} methods are just
         * {@code Map<K, V>} since the framework type is not known until graph resolution. This
         * translates from the {@code @Provides}/{@code @Produces} format into the {@code @Binds}
         * format. If {@link Key#type() possibleMapKey.type()} is not a {@code Map<K, Framework<V>>},
         * returns {@code possibleMapKey}.
         */
        Key convertToDelegateKey(Key possibleMapKey) {
            if (!MapType.isMap(possibleMapKey)) {
                return possibleMapKey;
            }
            MapType mapType = MapType.from(possibleMapKey);
            TypeMirror wrappedValueType;
            if (mapType.valuesAreTypeOf(Provider.class)) {
                wrappedValueType = mapType.unwrappedValueType(Provider.class);
            } else if (mapType.valuesAreTypeOf(Producer.class)) {
                wrappedValueType = mapType.unwrappedValueType(Producer.class);
            } else {
                return possibleMapKey;
            }
            return possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build();
        }

        /**
         * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}.
         */
        private Key wrapMapValue(Key key, Class<?> newWrappingClass) {
            checkArgument(
                    FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass.getName()).asType()));
            return wrapMapKey(key, newWrappingClass).get();
        }

        /**
         * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type
         * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link
         * Optional#empty()}.
         *
         * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath.
         *
         * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code
         *     currentWrappingClass}
         */
        Optional<Key> rewrapMapKey(Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) {
            checkArgument(!currentWrappingClass.equals(newWrappingClass));
            if (MapType.isMap(possibleMapKey)) {
                MapType mapType = MapType.from(possibleMapKey);
                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.empty();
                    }
                    DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement,
                            mapType.unwrappedValueType(currentWrappingClass));
                    return Optional.of(
                            possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
                }
            }
            return Optional.empty();
        }

        /**
         * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass
         * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier.
         * Otherwise returns {@link Optional#empty()}.
         *
         * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath.
         */
        private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) {
            if (MapType.isMap(possibleMapKey)) {
                MapType mapType = MapType.from(possibleMapKey);
                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.empty();
                    }
                    DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.valueType());
                    return Optional.of(
                            possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
                }
            }
            return Optional.empty();
        }

        /**
         * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set
         * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}.
         */
        Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) {
            if (SetType.isSet(key)) {
                SetType setType = SetType.from(key);
                if (setType.elementsAreTypeOf(wrappingClass)) {
                    return Optional
                            .of(key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build());
                }
            }
            return Optional.empty();
        }

        /**
         * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
         * qualifier whose type is {@linkplain DependencyRequest#extractKindAndType(TypeMirror)
         * extracted} from {@code T}.
         */
        Optional<Key> unwrapOptional(Key key) {
            if (!OptionalType.isOptional(key)) {
                return Optional.empty();
            }
            TypeMirror underlyingType = DependencyRequest.extractKindAndType(OptionalType.from(key).valueType())
                    .type();
            return Optional.of(key.toBuilder().type(underlyingType).build());
        }

        /** Returns a key for a {@code @ForReleasableReferences(scope) ReleasableReferenceManager}. */
        Key forReleasableReferenceManager(Scope scope) {
            return forQualifiedType(Optional.of(forReleasableReferencesAnnotationMirror(scope)),
                    getClassElement(ReleasableReferenceManager.class).asType());
        }

        /**
         * Returns a key for a {@code @ForReleasableReferences(scope)
         * TypedReleasableReferenceManager<metadataType>}
         */
        Key forTypedReleasableReferenceManager(Scope scope, DeclaredType metadataType) {
            return builder(typedReleasableReferenceManagerOf(metadataType))
                    .qualifier(forReleasableReferencesAnnotationMirror(scope)).build();
        }

        /** Returns a key for a {@code Set<ReleasableReferenceManager>}. */
        Key forSetOfReleasableReferenceManagers() {
            return builder(setOf(getClassElement(ReleasableReferenceManager.class).asType())).build();
        }

        /** Returns a key for a {@code Set<TypedReleasableReferenceManager<metadataType>}. */
        Key forSetOfTypedReleasableReferenceManagers(DeclaredType metadataType) {
            return forQualifiedType(Optional.empty(), setOf(typedReleasableReferenceManagerOf(metadataType)));
        }

        private AnnotationMirror forReleasableReferencesAnnotationMirror(Scope scope) {
            return SimpleAnnotationMirror.of(getClassElement(ForReleasableReferences.class), ImmutableMap
                    .of("value", new SimpleTypeAnnotationValue(scope.scopeAnnotationElement().asType())));
        }
    }
}