Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2014 Google, Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package dagger.internal.codegen;
     17 
     18 import com.google.auto.common.MoreElements;
     19 import com.google.auto.common.MoreTypes;
     20 import com.google.auto.value.AutoValue;
     21 import com.google.common.base.Optional;
     22 import com.google.common.collect.ImmutableSet;
     23 import com.google.common.collect.Sets;
     24 import dagger.Provides;
     25 import javax.inject.Inject;
     26 import javax.lang.model.element.Element;
     27 import javax.lang.model.element.ElementKind;
     28 import javax.lang.model.element.ExecutableElement;
     29 import javax.lang.model.element.TypeElement;
     30 import javax.lang.model.type.DeclaredType;
     31 import javax.lang.model.type.ExecutableType;
     32 import javax.lang.model.type.TypeKind;
     33 import javax.lang.model.type.TypeMirror;
     34 import javax.lang.model.util.Elements;
     35 import javax.lang.model.util.Types;
     36 
     37 import static com.google.auto.common.MoreElements.isAnnotationPresent;
     38 import static com.google.auto.common.MoreTypes.asDeclared;
     39 import static com.google.common.base.Preconditions.checkArgument;
     40 import static com.google.common.base.Preconditions.checkNotNull;
     41 import static com.google.common.base.Preconditions.checkState;
     42 import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
     43 import static dagger.internal.codegen.Scope.scopeOf;
     44 import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
     45 import static javax.lang.model.element.ElementKind.FIELD;
     46 import static javax.lang.model.element.ElementKind.METHOD;
     47 
     48 /**
     49  * A value object representing the mechanism by which a {@link Key} can be provided. New instances
     50  * should be created using an instance of the {@link Factory}.
     51  *
     52  * @author Gregory Kick
     53  * @since 2.0
     54  */
     55 @AutoValue
     56 abstract class ProvisionBinding extends ContributionBinding {
     57 
     58   @Override
     59   Binding.Type bindingType() {
     60     return Binding.Type.PROVISION;
     61   }
     62 
     63   @Override
     64   abstract Scope scope();
     65 
     66   static final class Factory {
     67     private final Elements elements;
     68     private final Types types;
     69     private final Key.Factory keyFactory;
     70     private final DependencyRequest.Factory dependencyRequestFactory;
     71 
     72     Factory(Elements elements, Types types, Key.Factory keyFactory,
     73         DependencyRequest.Factory dependencyRequestFactory) {
     74       this.elements = elements;
     75       this.types = types;
     76       this.keyFactory = keyFactory;
     77       this.dependencyRequestFactory = dependencyRequestFactory;
     78     }
     79 
     80     /** Returns an unresolved version of this binding. */
     81     ProvisionBinding unresolve(ProvisionBinding binding) {
     82       checkState(binding.hasNonDefaultTypeParameters());
     83       return forInjectConstructor((ExecutableElement) binding.bindingElement(),
     84           Optional.<TypeMirror>absent());
     85     }
     86 
     87     /**
     88      * Returns a ProvisionBinding for the given element. If {@code resolvedType} is present, this
     89      * will return a resolved binding, with the key & type resolved to the given type (using
     90      * {@link Types#asMemberOf(DeclaredType, Element)}).
     91      */
     92     ProvisionBinding forInjectConstructor(ExecutableElement constructorElement,
     93         Optional<TypeMirror> resolvedType) {
     94       checkNotNull(constructorElement);
     95       checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
     96       checkArgument(isAnnotationPresent(constructorElement, Inject.class));
     97       checkArgument(!getQualifier(constructorElement).isPresent());
     98 
     99       ExecutableType cxtorType = MoreTypes.asExecutable(constructorElement.asType());
    100       DeclaredType enclosingCxtorType =
    101           MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
    102       // If the class this is constructing has some type arguments, resolve everything.
    103       if (!enclosingCxtorType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
    104         DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
    105         // Validate that we're resolving from the correct type.
    106         checkState(types.isSameType(types.erasure(resolved), types.erasure(enclosingCxtorType)),
    107             "erased expected type: %s, erased actual type: %s",
    108             types.erasure(resolved), types.erasure(enclosingCxtorType));
    109         cxtorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement));
    110         enclosingCxtorType = resolved;
    111       }
    112 
    113       Key key = keyFactory.forInjectConstructorWithResolvedType(enclosingCxtorType);
    114       checkArgument(!key.qualifier().isPresent());
    115       ImmutableSet<DependencyRequest> dependencies =
    116           dependencyRequestFactory.forRequiredResolvedVariables(enclosingCxtorType,
    117               constructorElement.getParameters(),
    118               cxtorType.getParameterTypes());
    119       Optional<DependencyRequest> membersInjectionRequest =
    120           membersInjectionRequest(enclosingCxtorType);
    121       Scope scope = Scope.scopeOf(constructorElement.getEnclosingElement());
    122 
    123       TypeElement bindingTypeElement =
    124           MoreElements.asType(constructorElement.getEnclosingElement());
    125 
    126       return new AutoValue_ProvisionBinding(
    127           key,
    128           constructorElement,
    129           dependencies,
    130           findBindingPackage(key),
    131           hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types),
    132           Optional.<DeclaredType>absent(),
    133           Optional.<TypeElement>absent(),
    134           membersInjectionRequest,
    135           Kind.INJECTION,
    136           Provides.Type.UNIQUE,
    137           scope);
    138     }
    139 
    140     private static final ImmutableSet<ElementKind> MEMBER_KINDS =
    141         Sets.immutableEnumSet(METHOD, FIELD);
    142 
    143     private Optional<DependencyRequest> membersInjectionRequest(DeclaredType type) {
    144       TypeElement typeElement = MoreElements.asType(type.asElement());
    145       if (!types.isSameType(elements.getTypeElement(Object.class.getCanonicalName()).asType(),
    146           typeElement.getSuperclass())) {
    147         return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
    148       }
    149       for (Element enclosedElement : typeElement.getEnclosedElements()) {
    150         if (MEMBER_KINDS.contains(enclosedElement.getKind())
    151             && (isAnnotationPresent(enclosedElement, Inject.class))) {
    152           return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
    153         }
    154       }
    155       return Optional.absent();
    156     }
    157 
    158     ProvisionBinding forProvidesMethod(ExecutableElement providesMethod, TypeMirror contributedBy) {
    159       checkNotNull(providesMethod);
    160       checkArgument(providesMethod.getKind().equals(METHOD));
    161       checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED));
    162       Provides providesAnnotation = providesMethod.getAnnotation(Provides.class);
    163       checkArgument(providesAnnotation != null);
    164       DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy);
    165       ExecutableType resolvedMethod =
    166           MoreTypes.asExecutable(types.asMemberOf(declaredContainer, providesMethod));
    167       Key key = keyFactory.forProvidesMethod(resolvedMethod, providesMethod);
    168       ImmutableSet<DependencyRequest> dependencies =
    169           dependencyRequestFactory.forRequiredResolvedVariables(
    170               declaredContainer,
    171               providesMethod.getParameters(),
    172               resolvedMethod.getParameterTypes());
    173       Scope scope = Scope.scopeOf(providesMethod);
    174       return new AutoValue_ProvisionBinding(
    175           key,
    176           providesMethod,
    177           dependencies,
    178           findBindingPackage(key),
    179           false /* no non-default parameter types */,
    180           ConfigurationAnnotations.getNullableType(providesMethod),
    181           Optional.of(MoreTypes.asTypeElement(declaredContainer)),
    182           Optional.<DependencyRequest>absent(),
    183           Kind.PROVISION,
    184           providesAnnotation.type(),
    185           scope);
    186     }
    187 
    188     ProvisionBinding implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest) {
    189       checkNotNull(mapOfValueRequest);
    190       Optional<Key> implicitMapOfProviderKey =
    191           keyFactory.implicitMapProviderKeyFrom(mapOfValueRequest.key());
    192       checkArgument(
    193           implicitMapOfProviderKey.isPresent(),
    194           "%s is not a request for Map<K, V>",
    195           mapOfValueRequest);
    196       DependencyRequest implicitMapOfProviderRequest =
    197           dependencyRequestFactory.forImplicitMapBinding(
    198               mapOfValueRequest, implicitMapOfProviderKey.get());
    199       return new AutoValue_ProvisionBinding(
    200           mapOfValueRequest.key(),
    201           implicitMapOfProviderRequest.requestElement(),
    202           ImmutableSet.of(implicitMapOfProviderRequest),
    203           findBindingPackage(mapOfValueRequest.key()),
    204           false /* no non-default parameter types */,
    205           Optional.<DeclaredType>absent(),
    206           Optional.<TypeElement>absent(),
    207           Optional.<DependencyRequest>absent(),
    208           Kind.SYNTHETIC,
    209           Provides.Type.MAP,
    210           scopeOf(implicitMapOfProviderRequest.requestElement()));
    211     }
    212 
    213     ProvisionBinding forComponent(TypeElement componentDefinitionType) {
    214       checkNotNull(componentDefinitionType);
    215       return new AutoValue_ProvisionBinding(
    216           keyFactory.forComponent(componentDefinitionType.asType()),
    217           componentDefinitionType,
    218           ImmutableSet.<DependencyRequest>of(),
    219           Optional.<String>absent(),
    220           false /* no non-default parameter types */,
    221           Optional.<DeclaredType>absent(),
    222           Optional.<TypeElement>absent(),
    223           Optional.<DependencyRequest>absent(),
    224           Kind.COMPONENT,
    225           Provides.Type.UNIQUE,
    226           Scope.unscoped());
    227     }
    228 
    229     ProvisionBinding forComponentMethod(ExecutableElement componentMethod) {
    230       checkNotNull(componentMethod);
    231       checkArgument(componentMethod.getKind().equals(METHOD));
    232       checkArgument(componentMethod.getParameters().isEmpty());
    233       Scope scope = Scope.scopeOf(componentMethod);
    234       return new AutoValue_ProvisionBinding(
    235           keyFactory.forComponentMethod(componentMethod),
    236           componentMethod,
    237           ImmutableSet.<DependencyRequest>of(),
    238           Optional.<String>absent(),
    239           false /* no non-default parameter types */,
    240           ConfigurationAnnotations.getNullableType(componentMethod),
    241           Optional.<TypeElement>absent(),
    242           Optional.<DependencyRequest>absent(),
    243           Kind.COMPONENT_PROVISION,
    244           Provides.Type.UNIQUE,
    245           scope);
    246     }
    247 
    248     ProvisionBinding forSubcomponentBuilderMethod(
    249         ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy) {
    250       checkNotNull(subcomponentBuilderMethod);
    251       checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
    252       checkArgument(subcomponentBuilderMethod.getParameters().isEmpty());
    253       DeclaredType declaredContainer = asDeclared(contributedBy.asType());
    254       return new AutoValue_ProvisionBinding(
    255           keyFactory.forSubcomponentBuilderMethod(subcomponentBuilderMethod, declaredContainer),
    256           subcomponentBuilderMethod,
    257           ImmutableSet.<DependencyRequest>of(),
    258           Optional.<String>absent(),
    259           false /* no non-default parameter types */,
    260           Optional.<DeclaredType>absent(),
    261           Optional.of(contributedBy),
    262           Optional.<DependencyRequest>absent(),
    263           Kind.SUBCOMPONENT_BUILDER,
    264           Provides.Type.UNIQUE,
    265           Scope.unscoped());
    266     }
    267   }
    268 }
    269