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