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