• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
bindingElement()64   @Override abstract TypeElement bindingElement();
65 
66   /** The set of individual sites where {@link Inject} is applied. */
injectionSites()67   abstract ImmutableSortedSet<InjectionSite> injectionSites();
68 
parentInjectorRequest()69   abstract Optional<DependencyRequest> parentInjectorRequest();
70 
71   enum Strategy {
72     NO_OP,
73     INJECT_MEMBERS,
74   }
75 
injectionStrategy()76   Strategy injectionStrategy() {
77     return injectionSites().isEmpty() ? Strategy.NO_OP : Strategy.INJECT_MEMBERS;
78   }
79 
withoutParentInjectorRequest()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
bindingType()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 
kind()104     abstract Kind kind();
105 
element()106     abstract Element element();
107 
dependencies()108     abstract ImmutableSet<DependencyRequest> dependencies();
109 
indexAmongSiblingMembers(InjectionSite injectionSite)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 
Factory(Elements elements, Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory)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 
injectionSiteForInjectMethod( ExecutableElement methodElement, DeclaredType containingType)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 
injectionSiteForInjectField( VariableElement fieldElement, DeclaredType containingType)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. */
unresolve(MembersInjectionBinding 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. */
hasInjectedMembers(DeclaredType declaredType)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      */
forInjectedType( DeclaredType declaredType, Optional<TypeMirror> resolvedType)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 
getInjectionSites(DeclaredType declaredType)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 
shouldBeInjected( Element injectionSite, SetMultimap<String, ExecutableElement> overriddenMethodMap)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