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