• 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.MoreElements.isAnnotationPresent;
20 import static com.google.auto.common.MoreTypes.asExecutable;
21 import static com.google.auto.common.MoreTypes.isType;
22 import static com.google.common.base.Preconditions.checkArgument;
23 import static com.google.common.base.Preconditions.checkNotNull;
24 import static com.google.common.collect.Iterables.getOnlyElement;
25 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
26 import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
27 import static dagger.internal.codegen.MapKeys.getMapKey;
28 import static dagger.internal.codegen.MapKeys.mapKeyType;
29 import static dagger.internal.codegen.Optionals.firstPresent;
30 import static dagger.internal.codegen.RequestKinds.extractKeyType;
31 import static dagger.internal.codegen.RequestKinds.getRequestKind;
32 import static dagger.internal.codegen.langmodel.DaggerTypes.isFutureType;
33 import static java.util.Arrays.asList;
34 import static javax.lang.model.element.ElementKind.METHOD;
35 
36 import com.google.auto.common.MoreTypes;
37 import com.google.common.collect.ImmutableSet;
38 import dagger.Binds;
39 import dagger.BindsOptionalOf;
40 import dagger.internal.codegen.langmodel.DaggerElements;
41 import dagger.internal.codegen.langmodel.DaggerTypes;
42 import dagger.internal.codegen.serialization.KeyProto;
43 import dagger.model.Key;
44 import dagger.model.Key.MultibindingContributionIdentifier;
45 import dagger.model.RequestKind;
46 import dagger.multibindings.Multibinds;
47 import dagger.producers.Produced;
48 import dagger.producers.Producer;
49 import dagger.producers.Production;
50 import dagger.producers.internal.ProductionImplementation;
51 import dagger.producers.monitoring.ProductionComponentMonitor;
52 import java.util.Map;
53 import java.util.Optional;
54 import java.util.Set;
55 import java.util.concurrent.Executor;
56 import java.util.stream.Stream;
57 import javax.inject.Inject;
58 import javax.inject.Provider;
59 import javax.lang.model.element.AnnotationMirror;
60 import javax.lang.model.element.ExecutableElement;
61 import javax.lang.model.element.TypeElement;
62 import javax.lang.model.type.DeclaredType;
63 import javax.lang.model.type.ExecutableType;
64 import javax.lang.model.type.PrimitiveType;
65 import javax.lang.model.type.TypeMirror;
66 
67 /** A factory for {@link Key}s. */
68 final class KeyFactory {
69   private final DaggerTypes types;
70   private final DaggerElements elements;
71   private final TypeProtoConverter typeProtoConverter;
72   private final AnnotationProtoConverter annotationProtoConverter;
73 
74   @Inject
KeyFactory( DaggerTypes types, DaggerElements elements, TypeProtoConverter typeProtoConverter, AnnotationProtoConverter annotationProtoConverter)75   KeyFactory(
76       DaggerTypes types,
77       DaggerElements elements,
78       TypeProtoConverter typeProtoConverter,
79       AnnotationProtoConverter annotationProtoConverter) {
80     this.types = checkNotNull(types);
81     this.elements = checkNotNull(elements);
82     this.typeProtoConverter = typeProtoConverter;
83     this.annotationProtoConverter = annotationProtoConverter;
84   }
85 
boxPrimitives(TypeMirror type)86   private TypeMirror boxPrimitives(TypeMirror type) {
87     return type.getKind().isPrimitive() ? types.boxedClass((PrimitiveType) type).asType() : type;
88   }
89 
setOf(TypeMirror elementType)90   private DeclaredType setOf(TypeMirror elementType) {
91     return types.getDeclaredType(elements.getTypeElement(Set.class), boxPrimitives(elementType));
92   }
93 
mapOf(TypeMirror keyType, TypeMirror valueType)94   private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
95     return types.getDeclaredType(
96         elements.getTypeElement(Map.class), boxPrimitives(keyType), boxPrimitives(valueType));
97   }
98 
99   /** Returns {@code Map<KeyType, FrameworkType<ValueType>>}. */
mapOfFrameworkType( TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType)100   private TypeMirror mapOfFrameworkType(
101       TypeMirror keyType, TypeElement frameworkType, TypeMirror valueType) {
102     return mapOf(keyType, types.getDeclaredType(frameworkType, boxPrimitives(valueType)));
103   }
104 
forComponentMethod(ExecutableElement componentMethod)105   Key forComponentMethod(ExecutableElement componentMethod) {
106     checkArgument(componentMethod.getKind().equals(METHOD));
107     return forMethod(componentMethod, componentMethod.getReturnType());
108   }
109 
forProductionComponentMethod(ExecutableElement componentMethod)110   Key forProductionComponentMethod(ExecutableElement componentMethod) {
111     checkArgument(componentMethod.getKind().equals(METHOD));
112     TypeMirror returnType = componentMethod.getReturnType();
113     TypeMirror keyType =
114         isFutureType(returnType)
115             ? getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments())
116             : returnType;
117     return forMethod(componentMethod, keyType);
118   }
119 
forSubcomponentCreatorMethod( ExecutableElement subcomponentCreatorMethod, DeclaredType declaredContainer)120   Key forSubcomponentCreatorMethod(
121       ExecutableElement subcomponentCreatorMethod, DeclaredType declaredContainer) {
122     checkArgument(subcomponentCreatorMethod.getKind().equals(METHOD));
123     ExecutableType resolvedMethod =
124         asExecutable(types.asMemberOf(declaredContainer, subcomponentCreatorMethod));
125     return Key.builder(resolvedMethod.getReturnType()).build();
126   }
127 
forSubcomponentCreator(TypeMirror creatorType)128   Key forSubcomponentCreator(TypeMirror creatorType) {
129     return Key.builder(creatorType).build();
130   }
131 
forProvidesMethod(ExecutableElement method, TypeElement contributingModule)132   Key forProvidesMethod(ExecutableElement method, TypeElement contributingModule) {
133     return forBindingMethod(
134         method, contributingModule, Optional.of(elements.getTypeElement(Provider.class)));
135   }
136 
forProducesMethod(ExecutableElement method, TypeElement contributingModule)137   Key forProducesMethod(ExecutableElement method, TypeElement contributingModule) {
138     return forBindingMethod(
139         method, contributingModule, Optional.of(elements.getTypeElement(Producer.class)));
140   }
141 
142   /** Returns the key bound by a {@link Binds} method. */
forBindsMethod(ExecutableElement method, TypeElement contributingModule)143   Key forBindsMethod(ExecutableElement method, TypeElement contributingModule) {
144     checkArgument(isAnnotationPresent(method, Binds.class));
145     return forBindingMethod(method, contributingModule, Optional.empty());
146   }
147 
148   /** Returns the base key bound by a {@link BindsOptionalOf} method. */
forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule)149   Key forBindsOptionalOfMethod(ExecutableElement method, TypeElement contributingModule) {
150     checkArgument(isAnnotationPresent(method, BindsOptionalOf.class));
151     return forBindingMethod(method, contributingModule, Optional.empty());
152   }
153 
forBindingMethod( ExecutableElement method, TypeElement contributingModule, Optional<TypeElement> frameworkType)154   private Key forBindingMethod(
155       ExecutableElement method,
156       TypeElement contributingModule,
157       Optional<TypeElement> frameworkType) {
158     checkArgument(method.getKind().equals(METHOD));
159     ExecutableType methodType =
160         MoreTypes.asExecutable(
161             types.asMemberOf(MoreTypes.asDeclared(contributingModule.asType()), method));
162     ContributionType contributionType = ContributionType.fromBindingElement(method);
163     TypeMirror returnType = methodType.getReturnType();
164     if (frameworkType.isPresent()
165         && frameworkType.get().equals(elements.getTypeElement(Producer.class))
166         && isType(returnType)) {
167       if (isFutureType(methodType.getReturnType())) {
168         returnType = getOnlyElement(MoreTypes.asDeclared(returnType).getTypeArguments());
169       } else if (contributionType.equals(ContributionType.SET_VALUES)
170           && SetType.isSet(returnType)) {
171         SetType setType = SetType.from(returnType);
172         if (isFutureType(setType.elementType())) {
173           returnType =
174               types.getDeclaredType(
175                   elements.getTypeElement(Set.class), types.unwrapType(setType.elementType()));
176         }
177       }
178     }
179     TypeMirror keyType = bindingMethodKeyType(returnType, method, contributionType, frameworkType);
180     Key key = forMethod(method, keyType);
181     return contributionType.equals(ContributionType.UNIQUE)
182         ? key
183         : key.toBuilder()
184             .multibindingContributionIdentifier(
185                 new MultibindingContributionIdentifier(method, contributingModule))
186             .build();
187   }
188 
189   /**
190    * Returns the key for a {@link Multibinds @Multibinds} method.
191    *
192    * <p>The key's type is either {@code Set<T>} or {@code Map<K, Provider<V>>}. The latter works
193    * even for maps used by {@code Producer}s.
194    */
forMultibindsMethod(ExecutableType executableType, ExecutableElement method)195   Key forMultibindsMethod(ExecutableType executableType, ExecutableElement method) {
196     checkArgument(method.getKind().equals(METHOD), "%s must be a method", method);
197     TypeMirror returnType = executableType.getReturnType();
198     TypeMirror keyType =
199         MapType.isMap(returnType)
200             ? mapOfFrameworkType(
201                 MapType.from(returnType).keyType(),
202                 elements.getTypeElement(Provider.class),
203                 MapType.from(returnType).valueType())
204             : returnType;
205     return forMethod(method, keyType);
206   }
207 
bindingMethodKeyType( TypeMirror returnType, ExecutableElement method, ContributionType contributionType, Optional<TypeElement> frameworkType)208   private TypeMirror bindingMethodKeyType(
209       TypeMirror returnType,
210       ExecutableElement method,
211       ContributionType contributionType,
212       Optional<TypeElement> frameworkType) {
213     switch (contributionType) {
214       case UNIQUE:
215         return returnType;
216       case SET:
217         return setOf(returnType);
218       case MAP:
219         TypeMirror mapKeyType = mapKeyType(getMapKey(method).get(), types);
220         return frameworkType.isPresent()
221             ? mapOfFrameworkType(mapKeyType, frameworkType.get(), returnType)
222             : mapOf(mapKeyType, returnType);
223       case SET_VALUES:
224         // TODO(gak): do we want to allow people to use "covariant return" here?
225         checkArgument(SetType.isSet(returnType));
226         return returnType;
227     }
228     throw new AssertionError();
229   }
230 
231   /**
232    * Returns the key for a binding associated with a {@link DelegateDeclaration}.
233    *
234    * <p>If {@code delegateDeclaration} is {@code @IntoMap}, transforms the {@code Map<K, V>} key
235    * from {@link DelegateDeclaration#key()} to {@code Map<K, FrameworkType<V>>}. If {@code
236    * delegateDeclaration} is not a map contribution, its key is returned.
237    */
forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType)238   Key forDelegateBinding(DelegateDeclaration delegateDeclaration, Class<?> frameworkType) {
239     return delegateDeclaration.contributionType().equals(ContributionType.MAP)
240         ? wrapMapValue(delegateDeclaration.key(), frameworkType)
241         : delegateDeclaration.key();
242   }
243 
forMethod(ExecutableElement method, TypeMirror keyType)244   private Key forMethod(ExecutableElement method, TypeMirror keyType) {
245     return forQualifiedType(getQualifier(method), keyType);
246   }
247 
forInjectConstructorWithResolvedType(TypeMirror type)248   Key forInjectConstructorWithResolvedType(TypeMirror type) {
249     return Key.builder(type).build();
250   }
251 
252   // TODO(ronshapiro): Remove these conveniences which are simple wrappers around Key.Builder
forType(TypeMirror type)253   Key forType(TypeMirror type) {
254     return Key.builder(type).build();
255   }
256 
forMembersInjectedType(TypeMirror type)257   Key forMembersInjectedType(TypeMirror type) {
258     return Key.builder(type).build();
259   }
260 
forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type)261   Key forQualifiedType(Optional<AnnotationMirror> qualifier, TypeMirror type) {
262     return Key.builder(boxPrimitives(type)).qualifier(qualifier).build();
263   }
264 
forProductionExecutor()265   Key forProductionExecutor() {
266     return Key.builder(elements.getTypeElement(Executor.class).asType())
267         .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(Production.class)))
268         .build();
269   }
270 
forProductionImplementationExecutor()271   Key forProductionImplementationExecutor() {
272     return Key.builder(elements.getTypeElement(Executor.class).asType())
273         .qualifier(SimpleAnnotationMirror.of(elements.getTypeElement(ProductionImplementation.class)))
274         .build();
275   }
276 
forProductionComponentMonitor()277   Key forProductionComponentMonitor() {
278     return Key.builder(elements.getTypeElement(ProductionComponentMonitor.class).asType()).build();
279   }
280 
281   /**
282    * If {@code requestKey} is for a {@code Map<K, V>} or {@code Map<K, Produced<V>>}, returns keys
283    * for {@code Map<K, Provider<V>>} and {@code Map<K, Producer<V>>} (if Dagger-Producers is on
284    * the classpath).
285    */
implicitFrameworkMapKeys(Key requestKey)286   ImmutableSet<Key> implicitFrameworkMapKeys(Key requestKey) {
287     return Stream.of(implicitMapProviderKeyFrom(requestKey), implicitMapProducerKeyFrom(requestKey))
288         .filter(Optional::isPresent)
289         .map(Optional::get)
290         .collect(toImmutableSet());
291   }
292 
293   /**
294    * Optionally extract a {@link Key} for the underlying provision binding(s) if such a valid key
295    * can be inferred from the given key. Specifically, if the key represents a {@link Map}{@code
296    * <K, V>} or {@code Map<K, Producer<V>>}, a key of {@code Map<K, Provider<V>>} will be
297    * returned.
298    */
implicitMapProviderKeyFrom(Key possibleMapKey)299   Optional<Key> implicitMapProviderKeyFrom(Key possibleMapKey) {
300     return firstPresent(
301         rewrapMapKey(possibleMapKey, Produced.class, Provider.class),
302         wrapMapKey(possibleMapKey, Provider.class));
303   }
304 
305   /**
306    * Optionally extract a {@link Key} for the underlying production binding(s) if such a
307    * valid key can be inferred from the given key.  Specifically, if the key represents a
308    * {@link Map}{@code <K, V>} or {@code Map<K, Produced<V>>}, a key of
309    * {@code Map<K, Producer<V>>} will be returned.
310    */
implicitMapProducerKeyFrom(Key possibleMapKey)311   Optional<Key> implicitMapProducerKeyFrom(Key possibleMapKey) {
312     return firstPresent(
313         rewrapMapKey(possibleMapKey, Produced.class, Producer.class),
314         wrapMapKey(possibleMapKey, Producer.class));
315   }
316 
317   /**
318    * If {@code key}'s type is {@code Map<K, Provider<V>>}, {@code Map<K, Producer<V>>}, or {@code
319    * Map<K, Produced<V>>}, returns a key with the same qualifier and {@link
320    * Key#multibindingContributionIdentifier()} whose type is simply {@code Map<K, V>}.
321    *
322    * <p>Otherwise, returns {@code key}.
323    */
unwrapMapValueType(Key key)324   Key unwrapMapValueType(Key key) {
325     if (MapType.isMap(key)) {
326       MapType mapType = MapType.from(key);
327       if (!mapType.isRawType()) {
328         for (Class<?> frameworkClass : asList(Provider.class, Producer.class, Produced.class)) {
329           if (mapType.valuesAreTypeOf(frameworkClass)) {
330             return key.toBuilder()
331                 .type(mapOf(mapType.keyType(), mapType.unwrappedValueType(frameworkClass)))
332                 .build();
333           }
334         }
335       }
336     }
337     return key;
338   }
339 
340   /**
341    * Converts a {@link Key} of type {@code Map<K, V>} to {@code Map<K, Provider<V>>}.
342    */
wrapMapValue(Key key, Class<?> newWrappingClass)343   private Key wrapMapValue(Key key, Class<?> newWrappingClass) {
344     checkArgument(
345         FrameworkTypes.isFrameworkType(elements.getTypeElement(newWrappingClass).asType()));
346     return wrapMapKey(key, newWrappingClass).get();
347   }
348 
349   /**
350    * If {@code key}'s type is {@code Map<K, CurrentWrappingClass<Bar>>}, returns a key with type
351    * {@code Map<K, NewWrappingClass<Bar>>} with the same qualifier. Otherwise returns {@link
352    * Optional#empty()}.
353    *
354    * <p>Returns {@link Optional#empty()} if {@code newWrappingClass} is not in the classpath.
355    *
356    * @throws IllegalArgumentException if {@code newWrappingClass} is the same as {@code
357    *     currentWrappingClass}
358    */
rewrapMapKey( Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass)359   Optional<Key> rewrapMapKey(
360       Key possibleMapKey, Class<?> currentWrappingClass, Class<?> newWrappingClass) {
361     checkArgument(!currentWrappingClass.equals(newWrappingClass));
362     if (MapType.isMap(possibleMapKey)) {
363       MapType mapType = MapType.from(possibleMapKey);
364       if (!mapType.isRawType() && mapType.valuesAreTypeOf(currentWrappingClass)) {
365         TypeElement wrappingElement = elements.getTypeElement(newWrappingClass);
366         if (wrappingElement == null) {
367           // This target might not be compiled with Producers, so wrappingClass might not have an
368           // associated element.
369           return Optional.empty();
370         }
371         DeclaredType wrappedValueType =
372             types.getDeclaredType(
373                 wrappingElement, mapType.unwrappedValueType(currentWrappingClass));
374         return Optional.of(
375             possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
376       }
377     }
378     return Optional.empty();
379   }
380 
381   /**
382    * If {@code key}'s type is {@code Map<K, Foo>} and {@code Foo} is not {@code WrappingClass
383    * <Bar>}, returns a key with type {@code Map<K, WrappingClass<Foo>>} with the same qualifier.
384    * Otherwise returns {@link Optional#empty()}.
385    *
386    * <p>Returns {@link Optional#empty()} if {@code WrappingClass} is not in the classpath.
387    */
wrapMapKey(Key possibleMapKey, Class<?> wrappingClass)388   private Optional<Key> wrapMapKey(Key possibleMapKey, Class<?> wrappingClass) {
389     if (MapType.isMap(possibleMapKey)) {
390       MapType mapType = MapType.from(possibleMapKey);
391       if (!mapType.isRawType() && !mapType.valuesAreTypeOf(wrappingClass)) {
392         TypeElement wrappingElement = elements.getTypeElement(wrappingClass);
393         if (wrappingElement == null) {
394           // This target might not be compiled with Producers, so wrappingClass might not have an
395           // associated element.
396           return Optional.empty();
397         }
398         DeclaredType wrappedValueType = types.getDeclaredType(wrappingElement, mapType.valueType());
399         return Optional.of(
400             possibleMapKey.toBuilder().type(mapOf(mapType.keyType(), wrappedValueType)).build());
401       }
402     }
403     return Optional.empty();
404   }
405 
406   /**
407    * If {@code key}'s type is {@code Set<WrappingClass<Bar>>}, returns a key with type {@code Set
408    * <Bar>} with the same qualifier. Otherwise returns {@link Optional#empty()}.
409    */
unwrapSetKey(Key key, Class<?> wrappingClass)410   Optional<Key> unwrapSetKey(Key key, Class<?> wrappingClass) {
411     if (SetType.isSet(key)) {
412       SetType setType = SetType.from(key);
413       if (!setType.isRawType() && setType.elementsAreTypeOf(wrappingClass)) {
414         return Optional.of(
415             key.toBuilder().type(setOf(setType.unwrappedElementType(wrappingClass))).build());
416       }
417     }
418     return Optional.empty();
419   }
420 
421   /**
422    * If {@code key}'s type is {@code Optional<T>} for some {@code T}, returns a key with the same
423    * qualifier whose type is {@linkplain RequestKinds#extractKeyType(RequestKind, TypeMirror)}
424    * extracted} from {@code T}.
425    */
unwrapOptional(Key key)426   Optional<Key> unwrapOptional(Key key) {
427     if (!OptionalType.isOptional(key)) {
428       return Optional.empty();
429     }
430 
431     TypeMirror optionalValueType = OptionalType.from(key).valueType();
432     return Optional.of(
433         key.toBuilder()
434             .type(extractKeyType(getRequestKind(optionalValueType), optionalValueType))
435             .build());
436   }
437 
438   /** Translates a {@link Key} to a proto representation. */
toProto(Key key)439   static KeyProto toProto(Key key) {
440     KeyProto.Builder builder =
441         KeyProto.newBuilder().setType(TypeProtoConverter.toProto(key.type()));
442     key.qualifier().map(AnnotationProtoConverter::toProto).ifPresent(builder::setQualifier);
443     key.multibindingContributionIdentifier()
444         .ifPresent(
445             mci ->
446                 builder
447                     .getMultibindingContributionIdentifierBuilder()
448                     .setModule(mci.module())
449                     .setBindingElement(mci.bindingElement()));
450     return builder.build();
451   }
452 
453   /** Creates a {@link Key} from its proto representation. */
fromProto(KeyProto key)454   Key fromProto(KeyProto key) {
455     Key.Builder builder = Key.builder(typeProtoConverter.fromProto(key.getType()));
456     if (key.hasQualifier()) {
457       builder.qualifier(annotationProtoConverter.fromProto(key.getQualifier()));
458     }
459     if (key.hasMultibindingContributionIdentifier()) {
460       KeyProto.MultibindingContributionIdentifier multibindingContributionIdentifier =
461           key.getMultibindingContributionIdentifier();
462       builder.multibindingContributionIdentifier(
463           new MultibindingContributionIdentifier(
464               multibindingContributionIdentifier.getBindingElement(),
465               multibindingContributionIdentifier.getModule()));
466     }
467     return builder.build();
468   }
469 }
470