All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.internal.codegen.Key Maven / Gradle / Ivy

There is a newer version: 2.55
Show newest version
/*
 * 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