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.Function;
     22 import com.google.common.base.Optional;
     23 import com.google.common.collect.ComparisonChain;
     24 import com.google.common.collect.FluentIterable;
     25 import com.google.common.collect.ImmutableSet;
     26 import com.google.common.collect.ImmutableSortedSet;
     27 import com.google.common.collect.LinkedHashMultimap;
     28 import com.google.common.collect.SetMultimap;
     29 import java.util.ArrayList;
     30 import java.util.Comparator;
     31 import java.util.HashSet;
     32 import java.util.List;
     33 import java.util.Set;
     34 import javax.inject.Inject;
     35 import javax.lang.model.element.Element;
     36 import javax.lang.model.element.ElementKind;
     37 import javax.lang.model.element.ElementVisitor;
     38 import javax.lang.model.element.ExecutableElement;
     39 import javax.lang.model.element.TypeElement;
     40 import javax.lang.model.element.VariableElement;
     41 import javax.lang.model.type.DeclaredType;
     42 import javax.lang.model.type.ExecutableType;
     43 import javax.lang.model.type.TypeMirror;
     44 import javax.lang.model.util.ElementKindVisitor6;
     45 import javax.lang.model.util.Elements;
     46 import javax.lang.model.util.Types;
     47 
     48 import static com.google.auto.common.MoreElements.isAnnotationPresent;
     49 import static com.google.common.base.Preconditions.checkArgument;
     50 import static com.google.common.base.Preconditions.checkNotNull;
     51 import static com.google.common.base.Preconditions.checkState;
     52 import static javax.lang.model.element.Modifier.PRIVATE;
     53 import static javax.lang.model.element.Modifier.STATIC;
     54 
     55 /**
     56  * Represents the full members injection of a particular type. This does not pay attention to
     57  * injected members on supertypes.
     58  *
     59  * @author Gregory Kick
     60  * @since 2.0
     61  */
     62 @AutoValue
     63 abstract class MembersInjectionBinding extends Binding {
     64   @Override abstract TypeElement bindingElement();
     65 
     66   /** The set of individual sites where {@link Inject} is applied. */
     67   abstract ImmutableSortedSet<InjectionSite> injectionSites();
     68 
     69   abstract Optional<DependencyRequest> parentInjectorRequest();
     70 
     71   enum Strategy {
     72     NO_OP,
     73     INJECT_MEMBERS,
     74   }
     75 
     76   Strategy injectionStrategy() {
     77     return injectionSites().isEmpty() ? Strategy.NO_OP : Strategy.INJECT_MEMBERS;
     78   }
     79 
     80   MembersInjectionBinding withoutParentInjectorRequest() {
     81     return new AutoValue_MembersInjectionBinding(
     82           key(),
     83           dependencies(),
     84           implicitDependencies(),
     85           bindingPackage(),
     86           hasNonDefaultTypeParameters(),
     87           bindingElement(),
     88           injectionSites(),
     89           Optional.<DependencyRequest>absent());
     90   }
     91 
     92   @Override
     93   protected Binding.Type bindingType() {
     94     return Binding.Type.MEMBERS_INJECTION;
     95   }
     96 
     97   @AutoValue
     98   abstract static class InjectionSite {
     99     enum Kind {
    100       FIELD,
    101       METHOD,
    102     }
    103 
    104     abstract Kind kind();
    105 
    106     abstract Element element();
    107 
    108     abstract ImmutableSet<DependencyRequest> dependencies();
    109 
    110     protected int indexAmongSiblingMembers(InjectionSite injectionSite) {
    111       return injectionSite
    112           .element()
    113           .getEnclosingElement()
    114           .getEnclosedElements()
    115           .indexOf(injectionSite.element());
    116     }
    117   }
    118 
    119   static final class Factory {
    120     private final Elements elements;
    121     private final Types types;
    122     private final Key.Factory keyFactory;
    123     private final DependencyRequest.Factory dependencyRequestFactory;
    124 
    125     Factory(Elements elements, Types types, Key.Factory keyFactory,
    126         DependencyRequest.Factory dependencyRequestFactory) {
    127       this.elements = checkNotNull(elements);
    128       this.types = checkNotNull(types);
    129       this.keyFactory = checkNotNull(keyFactory);
    130       this.dependencyRequestFactory = checkNotNull(dependencyRequestFactory);
    131     }
    132 
    133     private InjectionSite injectionSiteForInjectMethod(
    134         ExecutableElement methodElement, DeclaredType containingType) {
    135       checkNotNull(methodElement);
    136       checkArgument(methodElement.getKind().equals(ElementKind.METHOD));
    137       ExecutableType resolved =
    138           MoreTypes.asExecutable(types.asMemberOf(containingType, methodElement));
    139       return new AutoValue_MembersInjectionBinding_InjectionSite(
    140           InjectionSite.Kind.METHOD,
    141           methodElement,
    142           dependencyRequestFactory.forRequiredResolvedVariables(
    143               containingType, methodElement.getParameters(), resolved.getParameterTypes()));
    144     }
    145 
    146     private InjectionSite injectionSiteForInjectField(
    147         VariableElement fieldElement, DeclaredType containingType) {
    148       checkNotNull(fieldElement);
    149       checkArgument(fieldElement.getKind().equals(ElementKind.FIELD));
    150       checkArgument(isAnnotationPresent(fieldElement, Inject.class));
    151       TypeMirror resolved = types.asMemberOf(containingType, fieldElement);
    152       return new AutoValue_MembersInjectionBinding_InjectionSite(
    153           InjectionSite.Kind.FIELD,
    154           fieldElement,
    155           ImmutableSet.of(
    156               dependencyRequestFactory.forRequiredResolvedVariable(
    157                   containingType, fieldElement, resolved)));
    158     }
    159 
    160     /** Returns an unresolved version of this binding. */
    161     MembersInjectionBinding unresolve(MembersInjectionBinding binding) {
    162       checkState(binding.hasNonDefaultTypeParameters());
    163       DeclaredType unresolved = MoreTypes.asDeclared(binding.bindingElement().asType());
    164       return forInjectedType(unresolved, Optional.<TypeMirror>absent());
    165     }
    166 
    167     /** Returns true if the type has some injected members in itself or any of its super classes. */
    168     boolean hasInjectedMembers(DeclaredType declaredType) {
    169       return !getInjectionSites(declaredType).isEmpty();
    170     }
    171 
    172     /**
    173      * Returns a MembersInjectionBinding for the given type. If {@code resolvedType} is present,
    174      * this will return a resolved binding, with the key & type resolved to the given type (using
    175      * {@link Types#asMemberOf(DeclaredType, Element)}).
    176      */
    177     MembersInjectionBinding forInjectedType(
    178         DeclaredType declaredType, Optional<TypeMirror> resolvedType) {
    179       // If the class this is injecting has some type arguments, resolve everything.
    180       if (!declaredType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
    181         DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
    182         // Validate that we're resolving from the correct type.
    183         checkState(
    184             types.isSameType(types.erasure(resolved), types.erasure(declaredType)),
    185             "erased expected type: %s, erased actual type: %s",
    186             types.erasure(resolved),
    187             types.erasure(declaredType));
    188         declaredType = resolved;
    189       }
    190       ImmutableSortedSet<InjectionSite> injectionSites = getInjectionSites(declaredType);
    191       ImmutableSet<DependencyRequest> dependencies =
    192           FluentIterable.from(injectionSites)
    193               .transformAndConcat(
    194                   new Function<InjectionSite, Set<DependencyRequest>>() {
    195                     @Override
    196                     public Set<DependencyRequest> apply(InjectionSite input) {
    197                       return input.dependencies();
    198                     }
    199                   })
    200               .toSet();
    201 
    202       Optional<DependencyRequest> parentInjectorRequest =
    203           MoreTypes.nonObjectSuperclass(types, elements, declaredType)
    204               .transform(
    205                   new Function<DeclaredType, DependencyRequest>() {
    206                     @Override
    207                     public DependencyRequest apply(DeclaredType input) {
    208                       return dependencyRequestFactory.forMembersInjectedType(input);
    209                     }
    210                   });
    211 
    212       Key key = keyFactory.forMembersInjectedType(declaredType);
    213       TypeElement typeElement = MoreElements.asType(declaredType.asElement());
    214       return new AutoValue_MembersInjectionBinding(
    215           key,
    216           dependencies,
    217           dependencies,
    218           findBindingPackage(key),
    219           hasNonDefaultTypeParameters(typeElement, key.type(), types),
    220           typeElement,
    221           injectionSites,
    222           parentInjectorRequest);
    223     }
    224 
    225     private ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
    226       Set<InjectionSite> injectionSites = new HashSet<>();
    227       final List<TypeElement> ancestors = new ArrayList<>();
    228       SetMultimap<String, ExecutableElement> overriddenMethodMap = LinkedHashMultimap.create();
    229       for (Optional<DeclaredType> currentType = Optional.of(declaredType);
    230           currentType.isPresent();
    231           currentType = MoreTypes.nonObjectSuperclass(types, elements, currentType.get())) {
    232         final DeclaredType type = currentType.get();
    233         ancestors.add(MoreElements.asType(type.asElement()));
    234         for (Element enclosedElement : type.asElement().getEnclosedElements()) {
    235           Optional<InjectionSite> maybeInjectionSite =
    236               injectionSiteVisitor.visit(enclosedElement, type);
    237           if (maybeInjectionSite.isPresent()) {
    238             InjectionSite injectionSite = maybeInjectionSite.get();
    239             if (shouldBeInjected(injectionSite.element(), overriddenMethodMap)) {
    240               injectionSites.add(injectionSite);
    241             }
    242             if (injectionSite.kind() == InjectionSite.Kind.METHOD) {
    243               ExecutableElement injectionSiteMethod =
    244                   MoreElements.asExecutable(injectionSite.element());
    245               overriddenMethodMap.put(
    246                   injectionSiteMethod.getSimpleName().toString(), injectionSiteMethod);
    247             }
    248           }
    249         }
    250       }
    251       return ImmutableSortedSet.copyOf(
    252           new Comparator<InjectionSite>() {
    253             @Override
    254             public int compare(InjectionSite left, InjectionSite right) {
    255               return ComparisonChain.start()
    256                   // supertypes before subtypes
    257                   .compare(
    258                       ancestors.indexOf(right.element().getEnclosingElement()),
    259                       ancestors.indexOf(left.element().getEnclosingElement()))
    260                   // fields before methods
    261                   .compare(left.element().getKind(), right.element().getKind())
    262                   // then sort by whichever element comes first in the parent
    263                   // this isn't necessary, but makes the processor nice and predictable
    264                   .compare(
    265                       left.indexAmongSiblingMembers(left), right.indexAmongSiblingMembers(right))
    266                   .result();
    267             }
    268           },
    269           injectionSites);
    270     }
    271 
    272     private boolean shouldBeInjected(
    273         Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap) {
    274       if (!isAnnotationPresent(injectionSite, Inject.class)
    275           || injectionSite.getModifiers().contains(PRIVATE)
    276           || injectionSite.getModifiers().contains(STATIC)) {
    277         return false;
    278       }
    279 
    280       if (injectionSite.getKind().isField()) { // Inject all fields (self and ancestors)
    281         return true;
    282       }
    283 
    284       // For each method with the same name belonging to any descendant class, return false if any
    285       // method has already overridden the injectionSite method. To decrease the number of methods
    286       // that are checked, we store the already injected methods in a SetMultimap and only
    287       // check the methods with the same name.
    288       ExecutableElement injectionSiteMethod = MoreElements.asExecutable(injectionSite);
    289       TypeElement injectionSiteType = MoreElements.asType(injectionSite.getEnclosingElement());
    290       for (ExecutableElement method :
    291           overriddenMethodMap.get(injectionSiteMethod.getSimpleName().toString())) {
    292         if (elements.overrides(method, injectionSiteMethod, injectionSiteType)) {
    293           return false;
    294         }
    295       }
    296       return true;
    297     }
    298 
    299     private final ElementVisitor<Optional<InjectionSite>, DeclaredType> injectionSiteVisitor =
    300         new ElementKindVisitor6<Optional<InjectionSite>, DeclaredType>(
    301             Optional.<InjectionSite>absent()) {
    302           @Override
    303           public Optional<InjectionSite> visitExecutableAsMethod(
    304               ExecutableElement e, DeclaredType type) {
    305             return Optional.of(injectionSiteForInjectMethod(e, type));
    306           }
    307 
    308           @Override
    309           public Optional<InjectionSite> visitVariableAsField(
    310               VariableElement e, DeclaredType type) {
    311             return (isAnnotationPresent(e, Inject.class)
    312                     && !e.getModifiers().contains(PRIVATE)
    313                     && !e.getModifiers().contains(STATIC))
    314                 ? Optional.of(injectionSiteForInjectField(e, type))
    315                 : Optional.<InjectionSite>absent();
    316           }
    317         };
    318   }
    319 }
    320