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