• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Dagger Authors.
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 
17 package dagger.internal.codegen.binding;
18 
19 import static com.google.auto.common.MoreElements.isAnnotationPresent;
20 import static dagger.internal.codegen.langmodel.DaggerElements.DECLARATION_ORDER;
21 import static javax.lang.model.element.Modifier.PRIVATE;
22 import static javax.lang.model.element.Modifier.STATIC;
23 
24 import com.google.auto.common.MoreElements;
25 import com.google.auto.common.MoreTypes;
26 import com.google.common.collect.ImmutableSortedSet;
27 import com.google.common.collect.LinkedHashMultimap;
28 import com.google.common.collect.SetMultimap;
29 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
30 import dagger.internal.codegen.langmodel.DaggerElements;
31 import dagger.internal.codegen.langmodel.DaggerTypes;
32 import java.util.ArrayList;
33 import java.util.Comparator;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Optional;
37 import java.util.Set;
38 import javax.inject.Inject;
39 import javax.lang.model.element.Element;
40 import javax.lang.model.element.ExecutableElement;
41 import javax.lang.model.element.TypeElement;
42 import javax.lang.model.element.VariableElement;
43 import javax.lang.model.type.DeclaredType;
44 import javax.lang.model.type.ExecutableType;
45 import javax.lang.model.type.TypeMirror;
46 import javax.lang.model.util.ElementKindVisitor8;
47 
48 /** A factory for {@link Binding} objects. */
49 final class InjectionSiteFactory {
50 
51   private final DaggerTypes types;
52   private final DaggerElements elements;
53   private final DependencyRequestFactory dependencyRequestFactory;
54 
55   @Inject
InjectionSiteFactory( DaggerTypes types, DaggerElements elements, DependencyRequestFactory dependencyRequestFactory)56   InjectionSiteFactory(
57       DaggerTypes types,
58       DaggerElements elements,
59       DependencyRequestFactory dependencyRequestFactory) {
60     this.types = types;
61     this.elements = elements;
62     this.dependencyRequestFactory = dependencyRequestFactory;
63   }
64 
65   /** Returns the injection sites for a type. */
getInjectionSites(DeclaredType declaredType)66   ImmutableSortedSet<InjectionSite> getInjectionSites(DeclaredType declaredType) {
67     Set<InjectionSite> injectionSites = new HashSet<>();
68     List<TypeElement> ancestors = new ArrayList<>();
69     InjectionSiteVisitor injectionSiteVisitor = new InjectionSiteVisitor();
70     for (Optional<DeclaredType> currentType = Optional.of(declaredType);
71         currentType.isPresent();
72         currentType = types.nonObjectSuperclass(currentType.get())) {
73       DeclaredType type = currentType.get();
74       ancestors.add(MoreElements.asType(type.asElement()));
75       for (Element enclosedElement : type.asElement().getEnclosedElements()) {
76         injectionSiteVisitor.visit(enclosedElement, type).ifPresent(injectionSites::add);
77       }
78     }
79     return ImmutableSortedSet.copyOf(
80         // supertypes before subtypes
81         Comparator.comparing(
82                 (InjectionSite injectionSite) ->
83                     ancestors.indexOf(injectionSite.element().getEnclosingElement()))
84             .reversed()
85             // fields before methods
86             .thenComparing(injectionSite -> injectionSite.element().getKind())
87             // then sort by whichever element comes first in the parent
88             // this isn't necessary, but makes the processor nice and predictable
89             .thenComparing(InjectionSite::element, DECLARATION_ORDER),
90         injectionSites);
91   }
92 
93   private final class InjectionSiteVisitor
94       extends ElementKindVisitor8<Optional<InjectionSite>, DeclaredType> {
95     private final SetMultimap<String, ExecutableElement> subclassMethodMap =
96         LinkedHashMultimap.create();
97 
InjectionSiteVisitor()98     InjectionSiteVisitor() {
99       super(Optional.empty());
100     }
101 
102     @Override
visitExecutableAsMethod( ExecutableElement method, DeclaredType type)103     public Optional<InjectionSite> visitExecutableAsMethod(
104         ExecutableElement method, DeclaredType type) {
105       subclassMethodMap.put(method.getSimpleName().toString(), method);
106       if (!shouldBeInjected(method)) {
107         return Optional.empty();
108       }
109       // This visitor assumes that subclass methods are visited before superclass methods, so we can
110       // skip any overridden method that has already been visited. To decrease the number of methods
111       // that are checked, we store the already injected methods in a SetMultimap and only check the
112       // methods with the same name.
113       String methodName = method.getSimpleName().toString();
114       TypeElement enclosingType = MoreElements.asType(method.getEnclosingElement());
115       for (ExecutableElement subclassMethod : subclassMethodMap.get(methodName)) {
116         if (method != subclassMethod && elements.overrides(subclassMethod, method, enclosingType)) {
117           return Optional.empty();
118         }
119       }
120       ExecutableType resolved = MoreTypes.asExecutable(types.asMemberOf(type, method));
121       return Optional.of(
122           InjectionSite.method(
123               method,
124               dependencyRequestFactory.forRequiredResolvedVariables(
125                   method.getParameters(), resolved.getParameterTypes())));
126     }
127 
128     @Override
visitVariableAsField( VariableElement field, DeclaredType type)129     public Optional<InjectionSite> visitVariableAsField(
130         VariableElement field, DeclaredType type) {
131       if (!shouldBeInjected(field)) {
132         return Optional.empty();
133       }
134       TypeMirror resolved = types.asMemberOf(type, field);
135       return Optional.of(
136           InjectionSite.field(
137               field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved)));
138     }
139 
shouldBeInjected(Element injectionSite)140     private boolean shouldBeInjected(Element injectionSite) {
141       return isAnnotationPresent(injectionSite, Inject.class)
142           && !injectionSite.getModifiers().contains(PRIVATE)
143           && !injectionSite.getModifiers().contains(STATIC);
144     }
145   }
146 }
147