• 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.XElementKt.isField;
20 import static androidx.room.compiler.processing.XElementKt.isMethod;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static dagger.internal.codegen.xprocessing.XElements.asField;
23 import static dagger.internal.codegen.xprocessing.XElements.asMethod;
24 import static dagger.internal.codegen.xprocessing.XElements.closestEnclosingTypeElement;
25 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
26 import static dagger.internal.codegen.xprocessing.XProcessingEnvs.javacOverrides;
27 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
28 import static dagger.internal.codegen.xprocessing.XTypes.nonObjectSuperclass;
29 import static java.util.Comparator.comparing;
30 
31 import androidx.room.compiler.processing.XElement;
32 import androidx.room.compiler.processing.XFieldElement;
33 import androidx.room.compiler.processing.XMethodElement;
34 import androidx.room.compiler.processing.XMethodType;
35 import androidx.room.compiler.processing.XType;
36 import androidx.room.compiler.processing.XTypeElement;
37 import com.google.common.collect.ImmutableSortedSet;
38 import com.google.common.collect.LinkedHashMultimap;
39 import com.google.common.collect.SetMultimap;
40 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite;
41 import dagger.internal.codegen.xprocessing.XElements;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.Map;
45 import java.util.Optional;
46 import java.util.Set;
47 import javax.inject.Inject;
48 
49 /** A factory for {@link Binding} objects. */
50 final class InjectionSiteFactory {
51   private final DependencyRequestFactory dependencyRequestFactory;
52 
53   @Inject
InjectionSiteFactory(DependencyRequestFactory dependencyRequestFactory)54   InjectionSiteFactory(DependencyRequestFactory dependencyRequestFactory) {
55     this.dependencyRequestFactory = dependencyRequestFactory;
56   }
57 
58   /** Returns the injection sites for a type. */
getInjectionSites(XType type)59   ImmutableSortedSet<InjectionSite> getInjectionSites(XType type) {
60     checkArgument(isDeclared(type));
61     Set<InjectionSite> injectionSites = new HashSet<>();
62     InjectionSiteVisitor injectionSiteVisitor = new InjectionSiteVisitor();
63     Map<XTypeElement, Integer> enclosingTypeElementOrder = new HashMap<>();
64     Map<XElement, Integer> enclosedElementOrder = new HashMap<>();
65     for (Optional<XType> currentType = Optional.of(type);
66         currentType.isPresent();
67         currentType = nonObjectSuperclass(currentType.get())) {
68       XTypeElement typeElement = currentType.get().getTypeElement();
69       enclosingTypeElementOrder.put(typeElement, enclosingTypeElementOrder.size());
70       for (XElement enclosedElement : typeElement.getEnclosedElements()) {
71         injectionSiteVisitor
72             .visit(enclosedElement, currentType.get())
73             .ifPresent(
74                 injectionSite -> {
75                   enclosedElementOrder.put(enclosedElement, enclosedElementOrder.size());
76                   injectionSites.add(injectionSite);
77                 });
78       }
79     }
80     return ImmutableSortedSet.copyOf(
81         // supertypes before subtypes
82         comparing(
83                 InjectionSite::enclosingTypeElement,
84                 comparing(enclosingTypeElementOrder::get).reversed())
85             // fields before methods
86             .thenComparing(InjectionSite::kind)
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, comparing(enclosedElementOrder::get)),
90         injectionSites);
91   }
92 
93   private final class InjectionSiteVisitor {
94     private final SetMultimap<String, XMethodElement> subclassMethodMap =
95         LinkedHashMultimap.create();
96 
visit(XElement element, XType container)97     public Optional<InjectionSite> visit(XElement element, XType container) {
98       if (isMethod(element)) {
99         return visitMethod(asMethod(element), container);
100       } else if (isField(element)) {
101         return visitField(asField(element), container);
102       }
103       return Optional.empty();
104     }
105 
visitMethod(XMethodElement method, XType container)106     public Optional<InjectionSite> visitMethod(XMethodElement method, XType container) {
107       subclassMethodMap.put(getSimpleName(method), method);
108       if (!shouldBeInjected(method)) {
109         return Optional.empty();
110       }
111       // This visitor assumes that subclass methods are visited before superclass methods, so we can
112       // skip any overridden method that has already been visited. To decrease the number of methods
113       // that are checked, we store the already injected methods in a SetMultimap and only check the
114       // methods with the same name.
115       XTypeElement enclosingType = closestEnclosingTypeElement(method);
116       for (XMethodElement subclassMethod : subclassMethodMap.get(getSimpleName(method))) {
117         if (method != subclassMethod && javacOverrides(subclassMethod, method, enclosingType)) {
118           return Optional.empty();
119         }
120       }
121       XMethodType resolved = method.asMemberOf(container);
122       return Optional.of(
123           InjectionSite.method(
124               method,
125               dependencyRequestFactory.forRequiredResolvedVariables(
126                   method.getParameters(), resolved.getParameterTypes())));
127     }
128 
visitField(XFieldElement field, XType container)129     public Optional<InjectionSite> visitField(XFieldElement field, XType container) {
130       if (!shouldBeInjected(field)) {
131         return Optional.empty();
132       }
133       XType resolved = field.asMemberOf(container);
134       return Optional.of(
135           InjectionSite.field(
136               field, dependencyRequestFactory.forRequiredResolvedVariable(field, resolved)));
137     }
138 
shouldBeInjected(XElement injectionSite)139     private boolean shouldBeInjected(XElement injectionSite) {
140       return InjectionAnnotations.hasInjectAnnotation(injectionSite)
141           && !XElements.isPrivate(injectionSite)
142           && !XElements.isStatic(injectionSite);
143     }
144   }
145 }
146