• 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.checkState;
21 import static com.google.common.collect.Iterables.getOnlyElement;
22 import static dagger.internal.codegen.base.ProducerAnnotations.productionImplementationQualifier;
23 import static dagger.internal.codegen.base.ProducerAnnotations.productionQualifier;
24 import static dagger.internal.codegen.base.RequestKinds.extractKeyType;
25 import static dagger.internal.codegen.binding.MapKeys.getMapKey;
26 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
27 import static dagger.internal.codegen.javapoet.TypeNames.isFutureType;
28 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
29 import static dagger.internal.codegen.xprocessing.XTypes.unwrapType;
30 
31 import androidx.room.compiler.processing.XAnnotation;
32 import androidx.room.compiler.processing.XMethodElement;
33 import androidx.room.compiler.processing.XMethodType;
34 import androidx.room.compiler.processing.XProcessingEnv;
35 import androidx.room.compiler.processing.XType;
36 import androidx.room.compiler.processing.XTypeElement;
37 import com.squareup.javapoet.ClassName;
38 import dagger.Binds;
39 import dagger.BindsOptionalOf;
40 import dagger.internal.codegen.base.ContributionType;
41 import dagger.internal.codegen.base.FrameworkTypes;
42 import dagger.internal.codegen.base.MapType;
43 import dagger.internal.codegen.base.OptionalType;
44 import dagger.internal.codegen.base.RequestKinds;
45 import dagger.internal.codegen.base.SetType;
46 import dagger.internal.codegen.compileroption.CompilerOptions;
47 import dagger.internal.codegen.javapoet.TypeNames;
48 import dagger.internal.codegen.model.DaggerAnnotation;
49 import dagger.internal.codegen.model.DaggerExecutableElement;
50 import dagger.internal.codegen.model.DaggerType;
51 import dagger.internal.codegen.model.DaggerTypeElement;
52 import dagger.internal.codegen.model.Key;
53 import dagger.internal.codegen.model.RequestKind;
54 import dagger.internal.codegen.xprocessing.XAnnotations;
55 import dagger.multibindings.Multibinds;
56 import java.util.Optional;
57 import javax.inject.Inject;
58 
59 /** A factory for {@link Key}s. */
60 public final class KeyFactory {
61   private final XProcessingEnv processingEnv;
62   private final CompilerOptions compilerOptions;
63   private final InjectionAnnotations injectionAnnotations;
64 
65   @Inject
KeyFactory( XProcessingEnv processingEnv, CompilerOptions compilerOptions, InjectionAnnotations injectionAnnotations)66   KeyFactory(
67       XProcessingEnv processingEnv,
68       CompilerOptions compilerOptions,
69       InjectionAnnotations injectionAnnotations) {
70     this.processingEnv = processingEnv;
71     this.compilerOptions = compilerOptions;
72     this.injectionAnnotations = injectionAnnotations;
73   }
74 
setOf(XType elementType)75   private XType setOf(XType elementType) {
76     return processingEnv.getDeclaredType(
77         processingEnv.requireTypeElement(TypeNames.SET), elementType.boxed());
78   }
79 
mapOf(XType keyType, XType valueType)80   private XType mapOf(XType keyType, XType valueType) {
81     return processingEnv.getDeclaredType(
82         processingEnv.requireTypeElement(TypeNames.MAP), keyType.boxed(), valueType.boxed());
83   }
84 
85   /**
86    * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
87    * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, XType)}
88    * extracted} from {@code T}.
89    */
optionalOf(Key key)90   Key optionalOf(Key key) {
91     return key.withType(DaggerType.from(optionalOf(key.type().xprocessing())));
92   }
93 
optionalOf(XType type)94   private XType optionalOf(XType type) {
95     return processingEnv.getDeclaredType(
96         processingEnv.requireTypeElement(TypeNames.JDK_OPTIONAL), type.boxed());
97   }
98 
99   /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
mapOfFrameworkType(XType keyType, ClassName frameworkClassName, XType valueType)100   private XType mapOfFrameworkType(XType keyType, ClassName frameworkClassName, XType valueType) {
101     checkArgument(FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES.contains(frameworkClassName));
102     return mapOf(
103         keyType,
104         processingEnv.getDeclaredType(
105             processingEnv.requireTypeElement(frameworkClassName), valueType.boxed()));
106   }
107 
forComponentMethod(XMethodElement componentMethod)108   Key forComponentMethod(XMethodElement componentMethod) {
109     return forMethod(componentMethod, componentMethod.getReturnType());
110   }
111 
forProductionComponentMethod(XMethodElement componentMethod)112   Key forProductionComponentMethod(XMethodElement componentMethod) {
113     XType returnType = componentMethod.getReturnType();
114     XType keyType =
115         isFutureType(returnType) ? getOnlyElement(returnType.getTypeArguments()) : returnType;
116     return forMethod(componentMethod, keyType);
117   }
118 
forSubcomponentCreatorMethod( XMethodElement subcomponentCreatorMethod, XType declaredContainer)119   Key forSubcomponentCreatorMethod(
120       XMethodElement subcomponentCreatorMethod, XType declaredContainer) {
121     checkArgument(isDeclared(declaredContainer));
122     XMethodType resolvedMethod = subcomponentCreatorMethod.asMemberOf(declaredContainer);
123     return forType(resolvedMethod.getReturnType());
124   }
125 
forSubcomponentCreator(XType creatorType)126   public Key forSubcomponentCreator(XType creatorType) {
127     return forType(creatorType);
128   }
129 
forProvidesMethod(XMethodElement method, XTypeElement contributingModule)130   public Key forProvidesMethod(XMethodElement method, XTypeElement contributingModule) {
131     checkArgument(method.hasAnnotation(TypeNames.PROVIDES));
132     return forBindingMethod(method, contributingModule, Optional.of(TypeNames.PROVIDER));
133   }
134 
forProducesMethod(XMethodElement method, XTypeElement contributingModule)135   public Key forProducesMethod(XMethodElement method, XTypeElement contributingModule) {
136     checkArgument(method.hasAnnotation(TypeNames.PRODUCES));
137     return forBindingMethod(method, contributingModule, Optional.of(TypeNames.PRODUCER));
138   }
139 
140   /** Returns the key bound by a {@link Binds} method. */
forBindsMethod(XMethodElement method, XTypeElement contributingModule)141   Key forBindsMethod(XMethodElement method, XTypeElement contributingModule) {
142     checkArgument(method.hasAnnotation(TypeNames.BINDS));
143     return forBindingMethod(method, contributingModule, Optional.empty());
144   }
145 
146   /** Returns the base key bound by a {@link BindsOptionalOf} method. */
forBindsOptionalOfMethod(XMethodElement method, XTypeElement contributingModule)147   Key forBindsOptionalOfMethod(XMethodElement method, XTypeElement contributingModule) {
148     checkArgument(method.hasAnnotation(TypeNames.BINDS_OPTIONAL_OF));
149     return forBindingMethod(method, contributingModule, Optional.empty());
150   }
151 
forBindingMethod( XMethodElement method, XTypeElement contributingModule, Optional<ClassName> frameworkClassName)152   private Key forBindingMethod(
153       XMethodElement method,
154       XTypeElement contributingModule,
155       Optional<ClassName> frameworkClassName) {
156     XMethodType methodType = method.asMemberOf(contributingModule.getType());
157     ContributionType contributionType = ContributionType.fromBindingElement(method);
158     XType returnType = methodType.getReturnType();
159     if (frameworkClassName.isPresent() && frameworkClassName.get().equals(TypeNames.PRODUCER)) {
160       if (isFutureType(returnType)) {
161         returnType = getOnlyElement(returnType.getTypeArguments());
162       } else if (contributionType.equals(ContributionType.SET_VALUES)
163           && SetType.isSet(returnType)) {
164         SetType setType = SetType.from(returnType);
165         if (isFutureType(setType.elementType())) {
166           returnType = setOf(unwrapType(setType.elementType()));
167         }
168       }
169     }
170     XType keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkClassName);
171     Key key = forMethod(method, keyType);
172     return contributionType.equals(ContributionType.UNIQUE)
173         ? key
174         : key.withMultibindingContributionIdentifier(
175             DaggerTypeElement.from(contributingModule), DaggerExecutableElement.from(method));
176   }
177 
178   /**
179    * Returns the key for a {@link Multibinds @Multibinds} method.
180    *
181    * <p>The key's type is either {@code Set<T>} or {@code Map<K, Provider<V>>}. The latter works
182    * even for maps used by {@code Producer}s.
183    */
forMultibindsMethod(XMethodElement method, XMethodType methodType)184   Key forMultibindsMethod(XMethodElement method, XMethodType methodType) {
185     XType returnType = method.getReturnType();
186     XType keyType =
187         MapType.isMap(returnType)
188             ? mapOfFrameworkType(
189                 MapType.from(returnType).keyType(),
190                 TypeNames.PROVIDER,
191                 MapType.from(returnType).valueType())
192             : returnType;
193     return forMethod(method, keyType);
194   }
195 
bindingMethodKeyType( XType returnType, XMethodElement method, ContributionType contributionType, Optional<ClassName> frameworkClassName)196   private XType bindingMethodKeyType(
197       XType returnType,
198       XMethodElement method,
199       ContributionType contributionType,
200       Optional<ClassName> frameworkClassName) {
201     switch (contributionType) {
202       case UNIQUE:
203         return returnType;
204       case SET:
205         return setOf(returnType);
206       case MAP:
207         Optional<XType> mapKeyType = getMapKey(method).map(MapKeys::mapKeyType);
208         // TODO(bcorso): We've added a special checkState here since a number of people have run
209         // into this particular case, but technically it shouldn't be necessary if we are properly
210         // doing superficial validation and deferring on unresolvable types. We should revisit
211         // whether this is necessary once we're able to properly defer this case.
212         checkState(
213             mapKeyType.isPresent(),
214             "Missing map key annotation for method: %s#%s. That method was annotated with: %s. If a"
215                 + " map key annotation is included in that list, it means Dagger wasn't able to"
216                 + " detect that it was a map key because the dependency is missing from the"
217                 + " classpath of the current build. To fix, add a dependency for the map key to the"
218                 + " current build. For more details, see"
219                 + " https://github.com/google/dagger/issues/3133#issuecomment-1002790894.",
220             method.getEnclosingElement(),
221             method,
222             method.getAllAnnotations().stream()
223                 .map(XAnnotations::toString)
224                 .collect(toImmutableList()));
225         return (frameworkClassName.isPresent()
226                 && compilerOptions.useFrameworkTypeInMapMultibindingContributionKey())
227             ? mapOfFrameworkType(mapKeyType.get(), frameworkClassName.get(), returnType)
228             : mapOf(mapKeyType.get(), returnType);
229       case SET_VALUES:
230         // TODO(gak): do we want to allow people to use "covariant return" here?
231         checkArgument(SetType.isSet(returnType));
232         return returnType;
233     }
234     throw new AssertionError();
235   }
236 
237   /**
238    * Returns the key for a binding associated with a {@link DelegateDeclaration}.
239    *
240    * <p>If {@code delegateDeclaration} is a multibinding map contribution and
241    * {@link CompilerOptions#useFrameworkTypeInMapMultibindingContributionKey()} is enabled, then
242    * transforms the {@code Map<K, V>} key into {@code Map<K, FrameworkType<V>>}, otherwise returns
243    * the unaltered key.
244    */
forDelegateBinding(DelegateDeclaration delegateDeclaration, ClassName frameworkType)245   Key forDelegateBinding(DelegateDeclaration delegateDeclaration, ClassName frameworkType) {
246     return delegateDeclaration.contributionType().equals(ContributionType.MAP)
247             && compilerOptions.useFrameworkTypeInMapMultibindingContributionKey()
248         ? wrapMapValue(delegateDeclaration.key(), frameworkType)
249         : delegateDeclaration.key();
250   }
251 
forMethod(XMethodElement method, XType keyType)252   private Key forMethod(XMethodElement method, XType keyType) {
253     return forQualifiedType(injectionAnnotations.getQualifier(method), keyType);
254   }
255 
forInjectConstructorWithResolvedType(XType type)256   public Key forInjectConstructorWithResolvedType(XType type) {
257     return forType(type);
258   }
259 
forType(XType type)260   Key forType(XType type) {
261     return Key.builder(DaggerType.from(type)).build();
262   }
263 
forMembersInjectedType(XType type)264   public Key forMembersInjectedType(XType type) {
265     return forType(type);
266   }
267 
forQualifiedType(Optional<XAnnotation> qualifier, XType type)268   Key forQualifiedType(Optional<XAnnotation> qualifier, XType type) {
269     return Key.builder(DaggerType.from(type.boxed()))
270         .qualifier(qualifier.map(DaggerAnnotation::from))
271         .build();
272   }
273 
forProductionExecutor()274   public Key forProductionExecutor() {
275     return Key.builder(DaggerType.from(processingEnv.requireType(TypeNames.EXECUTOR)))
276         .qualifier(DaggerAnnotation.from(productionQualifier(processingEnv)))
277         .build();
278   }
279 
forProductionImplementationExecutor()280   public Key forProductionImplementationExecutor() {
281     return Key.builder(DaggerType.from(processingEnv.requireType(TypeNames.EXECUTOR)))
282         .qualifier(DaggerAnnotation.from(productionImplementationQualifier(processingEnv)))
283         .build();
284   }
285 
forProductionComponentMonitor()286   public Key forProductionComponentMonitor() {
287     return forType(processingEnv.requireType(TypeNames.PRODUCTION_COMPONENT_MONITOR));
288   }
289 
290   /**
291    * If {@code key}'s type is {@code Map<K, Provider<V>>}, {@code Map<K, Producer<V>>}, or {@code
292    * Map<K, Produced<V>>}, returns a key with the same qualifier and {@link
293    * Key#multibindingContributionIdentifier()} whose type is simply {@code Map<K, V>}.
294    *
295    * <p>Otherwise, returns {@code key}.
296    */
unwrapMapValueType(Key key)297   public Key unwrapMapValueType(Key key) {
298     if (MapType.isMap(key)) {
299       MapType mapType = MapType.from(key);
300       if (!mapType.isRawType() && mapType.valuesAreFrameworkType()) {
301         return key.withType(
302             DaggerType.from(mapOf(mapType.keyType(), mapType.unwrappedFrameworkValueType())));
303       }
304     }
305     return key;
306   }
307 
308   /**
309    * Returns a key with the type {@code Map<K, FrameworkType<V>>} if the given key has a type of
310    * {@code Map<K, V>}. Otherwise, returns the unaltered key.
311    *
312    * @throws IllegalArgumentException if the {@code frameworkClassName} is not a valid framework
313    * type for multibinding maps.
314    * @throws IllegalStateException if the {@code key} is already wrapped in a (different) framework
315    * type.
316    */
wrapMapValue(Key key, ClassName frameworkClassName)317   private Key wrapMapValue(Key key, ClassName frameworkClassName) {
318     checkArgument(FrameworkTypes.MAP_VALUE_FRAMEWORK_TYPES.contains(frameworkClassName));
319     if (MapType.isMap(key)) {
320       MapType mapType = MapType.from(key);
321       if (!mapType.isRawType() && !mapType.valuesAreTypeOf(frameworkClassName)) {
322         checkState(!mapType.valuesAreFrameworkType());
323         XTypeElement frameworkTypeElement = processingEnv.findTypeElement(frameworkClassName);
324         if (frameworkTypeElement == null) {
325           // This target might not be compiled with Producers, so wrappingClass might not have an
326           // associated element.
327           return key;
328         }
329         XType wrappedValueType =
330             processingEnv.getDeclaredType(frameworkTypeElement, mapType.valueType());
331         return key.withType(DaggerType.from(mapOf(mapType.keyType(), wrappedValueType)));
332       }
333     }
334     return key;
335   }
336 
337   /**
338    * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set
339    * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}.
340    */
unwrapSetKey(Key key, ClassName wrappingClassName)341   Optional<Key> unwrapSetKey(Key key, ClassName wrappingClassName) {
342     if (SetType.isSet(key)) {
343       SetType setType = SetType.from(key);
344       if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClassName)) {
345         return Optional.of(
346             key.withType(DaggerType.from(setOf(setType.unwrappedElementType(wrappingClassName)))));
347       }
348     }
349     return Optional.empty();
350   }
351 
352   /**
353    * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
354    * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, XType)}
355    * extracted} from {@code T}.
356    */
unwrapOptional(Key key)357   Optional<Key> unwrapOptional(Key key) {
358     if (!OptionalType.isOptional(key)) {
359       return Optional.empty();
360     }
361 
362     XType optionalValueType = OptionalType.from(key).valueType();
363     return Optional.of(key.withType(DaggerType.from(extractKeyType(optionalValueType))));
364   }
365 }
366