• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 androidx.room.compiler.processing.compat.XConverters.toXProcessing;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.base.Preconditions.checkNotNull;
23 import static com.google.common.base.Preconditions.checkState;
24 import static com.google.common.collect.Iterables.getOnlyElement;
25 import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
26 import static dagger.internal.codegen.base.RequestKinds.frameworkClassName;
27 import static dagger.internal.codegen.base.RequestKinds.getRequestKind;
28 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.isAssistedParameter;
29 import static dagger.internal.codegen.binding.ConfigurationAnnotations.getNullableType;
30 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
31 import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType;
32 import static dagger.internal.codegen.xprocessing.XTypes.isTypeOf;
33 import static dagger.spi.model.RequestKind.FUTURE;
34 import static dagger.spi.model.RequestKind.INSTANCE;
35 import static dagger.spi.model.RequestKind.MEMBERS_INJECTION;
36 import static dagger.spi.model.RequestKind.PRODUCER;
37 import static dagger.spi.model.RequestKind.PROVIDER;
38 
39 import androidx.room.compiler.processing.XAnnotation;
40 import androidx.room.compiler.processing.XElement;
41 import androidx.room.compiler.processing.XMethodElement;
42 import androidx.room.compiler.processing.XMethodType;
43 import androidx.room.compiler.processing.XProcessingEnv;
44 import androidx.room.compiler.processing.XType;
45 import androidx.room.compiler.processing.XVariableElement;
46 import androidx.room.compiler.processing.compat.XConverters;
47 import com.google.common.collect.ImmutableSet;
48 import dagger.Lazy;
49 import dagger.internal.codegen.base.MapType;
50 import dagger.internal.codegen.base.OptionalType;
51 import dagger.internal.codegen.javapoet.TypeNames;
52 import dagger.spi.model.DaggerElement;
53 import dagger.spi.model.DependencyRequest;
54 import dagger.spi.model.Key;
55 import dagger.spi.model.RequestKind;
56 import java.util.List;
57 import java.util.Optional;
58 import javax.inject.Inject;
59 import javax.inject.Provider;
60 import javax.lang.model.element.AnnotationMirror;
61 import javax.lang.model.element.Element;
62 import javax.lang.model.element.VariableElement;
63 import javax.lang.model.type.DeclaredType;
64 import javax.lang.model.type.TypeMirror;
65 
66 /**
67  * Factory for {@link DependencyRequest}s.
68  *
69  * <p>Any factory method may throw {@link TypeNotPresentException} if a type is not available, which
70  * may mean that the type will be generated in a later round of processing.
71  */
72 public final class DependencyRequestFactory {
73   private final XProcessingEnv processingEnv;
74   private final KeyFactory keyFactory;
75   private final InjectionAnnotations injectionAnnotations;
76 
77   @Inject
DependencyRequestFactory( XProcessingEnv processingEnv, KeyFactory keyFactory, InjectionAnnotations injectionAnnotations)78   DependencyRequestFactory(
79       XProcessingEnv processingEnv,
80       KeyFactory keyFactory,
81       InjectionAnnotations injectionAnnotations) {
82     this.processingEnv = processingEnv;
83     this.keyFactory = keyFactory;
84     this.injectionAnnotations = injectionAnnotations;
85   }
86 
forRequiredResolvedXVariables( List<? extends XVariableElement> variables, List<XType> resolvedTypes)87   ImmutableSet<DependencyRequest> forRequiredResolvedXVariables(
88       List<? extends XVariableElement> variables, List<XType> resolvedTypes) {
89     return forRequiredResolvedVariables(
90         variables.stream().map(XConverters::toJavac).collect(toImmutableList()),
91         resolvedTypes.stream().map(XConverters::toJavac).collect(toImmutableList()));
92   }
93 
forRequiredResolvedVariables( List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes)94   ImmutableSet<DependencyRequest> forRequiredResolvedVariables(
95       List<? extends VariableElement> variables, List<? extends TypeMirror> resolvedTypes) {
96     checkState(resolvedTypes.size() == variables.size());
97     ImmutableSet.Builder<DependencyRequest> builder = ImmutableSet.builder();
98     for (int i = 0; i < variables.size(); i++) {
99       builder.add(forRequiredResolvedVariable(variables.get(i), resolvedTypes.get(i)));
100     }
101     return builder.build();
102   }
103 
104   /**
105    * Creates synthetic dependency requests for each individual multibinding contribution in {@code
106    * multibindingContributions}.
107    */
forMultibindingContributions( Key multibindingKey, Iterable<ContributionBinding> multibindingContributions)108   ImmutableSet<DependencyRequest> forMultibindingContributions(
109       Key multibindingKey, Iterable<ContributionBinding> multibindingContributions) {
110     ImmutableSet.Builder<DependencyRequest> requests = ImmutableSet.builder();
111     for (ContributionBinding multibindingContribution : multibindingContributions) {
112       requests.add(forMultibindingContribution(multibindingKey, multibindingContribution));
113     }
114     return requests.build();
115   }
116 
117   /** Creates a synthetic dependency request for one individual {@code multibindingContribution}. */
forMultibindingContribution( Key multibindingKey, ContributionBinding multibindingContribution)118   private DependencyRequest forMultibindingContribution(
119       Key multibindingKey, ContributionBinding multibindingContribution) {
120     checkArgument(
121         multibindingContribution.key().multibindingContributionIdentifier().isPresent(),
122         "multibindingContribution's key must have a multibinding contribution identifier: %s",
123         multibindingContribution);
124     return DependencyRequest.builder()
125         .kind(multibindingContributionRequestKind(multibindingKey, multibindingContribution))
126         .key(multibindingContribution.key())
127         .build();
128   }
129 
130   // TODO(b/28555349): support PROVIDER_OF_LAZY here too
131   private static final ImmutableSet<RequestKind> WRAPPING_MAP_VALUE_FRAMEWORK_TYPES =
132       ImmutableSet.of(PROVIDER, PRODUCER);
133 
multibindingContributionRequestKind( Key multibindingKey, ContributionBinding multibindingContribution)134   private RequestKind multibindingContributionRequestKind(
135       Key multibindingKey, ContributionBinding multibindingContribution) {
136     switch (multibindingContribution.contributionType()) {
137       case MAP:
138         MapType mapType = MapType.from(multibindingKey);
139         for (RequestKind kind : WRAPPING_MAP_VALUE_FRAMEWORK_TYPES) {
140           if (mapType.valuesAreTypeOf(frameworkClassName(kind))) {
141             return kind;
142           }
143         }
144         // fall through
145       case SET:
146       case SET_VALUES:
147         return INSTANCE;
148       case UNIQUE:
149         throw new IllegalArgumentException(
150             "multibindingContribution must be a multibinding: " + multibindingContribution);
151     }
152     throw new AssertionError(multibindingContribution.toString());
153   }
154 
forRequiredResolvedVariable( XVariableElement variableElement, XType resolvedType)155   DependencyRequest forRequiredResolvedVariable(
156       XVariableElement variableElement, XType resolvedType) {
157     return forRequiredResolvedVariable(toJavac(variableElement), toJavac(resolvedType));
158   }
159 
forRequiredResolvedVariable( VariableElement variableElement, TypeMirror resolvedType)160   DependencyRequest forRequiredResolvedVariable(
161       VariableElement variableElement, TypeMirror resolvedType) {
162     checkNotNull(variableElement);
163     checkNotNull(resolvedType);
164     // Ban @Assisted parameters, they are not considered dependency requests.
165     checkArgument(!isAssistedParameter(toXProcessing(variableElement, processingEnv)));
166     Optional<AnnotationMirror> qualifier = injectionAnnotations.getQualifier(variableElement);
167     return newDependencyRequest(variableElement, resolvedType, qualifier);
168   }
169 
forComponentProvisionMethod( XMethodElement provisionMethod, XMethodType provisionMethodType)170   public DependencyRequest forComponentProvisionMethod(
171       XMethodElement provisionMethod, XMethodType provisionMethodType) {
172     checkNotNull(provisionMethod);
173     checkNotNull(provisionMethodType);
174     checkArgument(
175         provisionMethod.getParameters().isEmpty(),
176         "Component provision methods must be empty: %s",
177         provisionMethod);
178     Optional<XAnnotation> qualifier = injectionAnnotations.getQualifier(provisionMethod);
179     return newDependencyRequest(provisionMethod, provisionMethodType.getReturnType(), qualifier);
180   }
181 
forComponentProductionMethod( XMethodElement productionMethod, XMethodType productionMethodType)182   public DependencyRequest forComponentProductionMethod(
183       XMethodElement productionMethod, XMethodType productionMethodType) {
184     checkNotNull(productionMethod);
185     checkNotNull(productionMethodType);
186     checkArgument(
187         productionMethod.getParameters().isEmpty(),
188         "Component production methods must be empty: %s",
189         productionMethod);
190     XType type = productionMethodType.getReturnType();
191     Optional<XAnnotation> qualifier = injectionAnnotations.getQualifier(productionMethod);
192     // Only a component production method can be a request for a ListenableFuture, so we
193     // special-case it here.
194     if (isTypeOf(type, TypeNames.LISTENABLE_FUTURE)) {
195       return DependencyRequest.builder()
196           .kind(FUTURE)
197           .key(keyFactory.forQualifiedType(qualifier, unwrapType(type)))
198           .requestElement(DaggerElement.from(productionMethod))
199           .build();
200     } else {
201       return newDependencyRequest(productionMethod, type, qualifier);
202     }
203   }
204 
forComponentMembersInjectionMethod( XMethodElement membersInjectionMethod, XMethodType membersInjectionMethodType)205   DependencyRequest forComponentMembersInjectionMethod(
206       XMethodElement membersInjectionMethod, XMethodType membersInjectionMethodType) {
207     checkNotNull(membersInjectionMethod);
208     checkNotNull(membersInjectionMethodType);
209     Optional<XAnnotation> qualifier = injectionAnnotations.getQualifier(membersInjectionMethod);
210     checkArgument(!qualifier.isPresent());
211     XType membersInjectedType = getOnlyElement(membersInjectionMethodType.getParameterTypes());
212     return DependencyRequest.builder()
213         .kind(MEMBERS_INJECTION)
214         .key(keyFactory.forMembersInjectedType(membersInjectedType))
215         .requestElement(DaggerElement.from(membersInjectionMethod))
216         .build();
217   }
218 
forProductionImplementationExecutor()219   DependencyRequest forProductionImplementationExecutor() {
220     return DependencyRequest.builder()
221         .kind(PROVIDER)
222         .key(keyFactory.forProductionImplementationExecutor())
223         .build();
224   }
225 
forProductionComponentMonitor()226   DependencyRequest forProductionComponentMonitor() {
227     return DependencyRequest.builder()
228         .kind(PROVIDER)
229         .key(keyFactory.forProductionComponentMonitor())
230         .build();
231   }
232 
233   /**
234    * Returns a synthetic request for the present value of an optional binding generated from a
235    * {@link dagger.BindsOptionalOf} declaration.
236    */
forSyntheticPresentOptionalBinding(Key requestKey, RequestKind kind)237   DependencyRequest forSyntheticPresentOptionalBinding(Key requestKey, RequestKind kind) {
238     Optional<Key> key = keyFactory.unwrapOptional(requestKey);
239     checkArgument(key.isPresent(), "not a request for optional: %s", requestKey);
240     return DependencyRequest.builder()
241         .kind(kind)
242         .key(key.get())
243         .isNullable(
244             allowsNull(getRequestKind(OptionalType.from(requestKey).valueType()), Optional.empty()))
245         .build();
246   }
247 
newDependencyRequest( XElement requestElement, XType type, Optional<XAnnotation> qualifier)248   private DependencyRequest newDependencyRequest(
249       XElement requestElement, XType type, Optional<XAnnotation> qualifier) {
250     return newDependencyRequest(
251         toJavac(requestElement), toJavac(type), qualifier.map(XConverters::toJavac));
252   }
253 
newDependencyRequest( Element requestElement, TypeMirror type, Optional<AnnotationMirror> qualifier)254   private DependencyRequest newDependencyRequest(
255       Element requestElement, TypeMirror type, Optional<AnnotationMirror> qualifier) {
256     RequestKind requestKind = getRequestKind(type);
257     return DependencyRequest.builder()
258         .kind(requestKind)
259         .key(keyFactory.forQualifiedType(qualifier, extractKeyType(type)))
260         .requestElement(DaggerElement.from(toXProcessing(requestElement, processingEnv)))
261         .isNullable(allowsNull(requestKind, getNullableType(requestElement)))
262         .build();
263   }
264 
265   /**
266    * Returns {@code true} if a given request element allows null values. {@link
267    * RequestKind#INSTANCE} requests must be annotated with {@code @Nullable} in order to allow null
268    * values. All other request kinds implicitly allow null values because they are are wrapped
269    * inside {@link Provider}, {@link Lazy}, etc.
270    */
allowsNull(RequestKind kind, Optional<DeclaredType> nullableType)271   private boolean allowsNull(RequestKind kind, Optional<DeclaredType> nullableType) {
272     return nullableType.isPresent() || !kind.equals(INSTANCE);
273   }
274 }
275