1 /*
2  * Copyright (C) 2014 Google, Inc.
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 package dagger.internal.codegen;
17 
18 import com.google.auto.common.MoreElements;
19 import com.google.auto.common.MoreTypes;
20 import com.google.auto.value.AutoValue;
21 import com.google.common.base.Optional;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Sets;
24 import dagger.Provides;
25 import javax.inject.Inject;
26 import javax.lang.model.element.Element;
27 import javax.lang.model.element.ElementKind;
28 import javax.lang.model.element.ExecutableElement;
29 import javax.lang.model.element.TypeElement;
30 import javax.lang.model.type.DeclaredType;
31 import javax.lang.model.type.ExecutableType;
32 import javax.lang.model.type.TypeKind;
33 import javax.lang.model.type.TypeMirror;
34 import javax.lang.model.util.Elements;
35 import javax.lang.model.util.Types;
36 
37 import static com.google.auto.common.MoreElements.isAnnotationPresent;
38 import static com.google.auto.common.MoreTypes.asDeclared;
39 import static com.google.common.base.Preconditions.checkArgument;
40 import static com.google.common.base.Preconditions.checkNotNull;
41 import static com.google.common.base.Preconditions.checkState;
42 import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
43 import static dagger.internal.codegen.Scope.scopeOf;
44 import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
45 import static javax.lang.model.element.ElementKind.FIELD;
46 import static javax.lang.model.element.ElementKind.METHOD;
47 
48 /**
49  * A value object representing the mechanism by which a {@link Key} can be provided. New instances
50  * should be created using an instance of the {@link Factory}.
51  *
52  * @author Gregory Kick
53  * @since 2.0
54  */
55 @AutoValue
56 abstract class ProvisionBinding extends ContributionBinding {
57 
58   @Override
bindingType()59   Binding.Type bindingType() {
60     return Binding.Type.PROVISION;
61   }
62 
63   @Override
scope()64   abstract Scope scope();
65 
66   static final class Factory {
67     private final Elements elements;
68     private final Types types;
69     private final Key.Factory keyFactory;
70     private final DependencyRequest.Factory dependencyRequestFactory;
71 
Factory(Elements elements, Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory)72     Factory(Elements elements, Types types, Key.Factory keyFactory,
73         DependencyRequest.Factory dependencyRequestFactory) {
74       this.elements = elements;
75       this.types = types;
76       this.keyFactory = keyFactory;
77       this.dependencyRequestFactory = dependencyRequestFactory;
78     }
79 
80     /** Returns an unresolved version of this binding. */
unresolve(ProvisionBinding binding)81     ProvisionBinding unresolve(ProvisionBinding binding) {
82       checkState(binding.hasNonDefaultTypeParameters());
83       return forInjectConstructor((ExecutableElement) binding.bindingElement(),
84           Optional.<TypeMirror>absent());
85     }
86 
87     /**
88      * Returns a ProvisionBinding for the given element. If {@code resolvedType} is present, this
89      * will return a resolved binding, with the key & type resolved to the given type (using
90      * {@link Types#asMemberOf(DeclaredType, Element)}).
91      */
forInjectConstructor(ExecutableElement constructorElement, Optional<TypeMirror> resolvedType)92     ProvisionBinding forInjectConstructor(ExecutableElement constructorElement,
93         Optional<TypeMirror> resolvedType) {
94       checkNotNull(constructorElement);
95       checkArgument(constructorElement.getKind().equals(CONSTRUCTOR));
96       checkArgument(isAnnotationPresent(constructorElement, Inject.class));
97       checkArgument(!getQualifier(constructorElement).isPresent());
98 
99       ExecutableType cxtorType = MoreTypes.asExecutable(constructorElement.asType());
100       DeclaredType enclosingCxtorType =
101           MoreTypes.asDeclared(constructorElement.getEnclosingElement().asType());
102       // If the class this is constructing has some type arguments, resolve everything.
103       if (!enclosingCxtorType.getTypeArguments().isEmpty() && resolvedType.isPresent()) {
104         DeclaredType resolved = MoreTypes.asDeclared(resolvedType.get());
105         // Validate that we're resolving from the correct type.
106         checkState(types.isSameType(types.erasure(resolved), types.erasure(enclosingCxtorType)),
107             "erased expected type: %s, erased actual type: %s",
108             types.erasure(resolved), types.erasure(enclosingCxtorType));
109         cxtorType = MoreTypes.asExecutable(types.asMemberOf(resolved, constructorElement));
110         enclosingCxtorType = resolved;
111       }
112 
113       Key key = keyFactory.forInjectConstructorWithResolvedType(enclosingCxtorType);
114       checkArgument(!key.qualifier().isPresent());
115       ImmutableSet<DependencyRequest> dependencies =
116           dependencyRequestFactory.forRequiredResolvedVariables(enclosingCxtorType,
117               constructorElement.getParameters(),
118               cxtorType.getParameterTypes());
119       Optional<DependencyRequest> membersInjectionRequest =
120           membersInjectionRequest(enclosingCxtorType);
121       Scope scope = Scope.scopeOf(constructorElement.getEnclosingElement());
122 
123       TypeElement bindingTypeElement =
124           MoreElements.asType(constructorElement.getEnclosingElement());
125 
126       return new AutoValue_ProvisionBinding(
127           key,
128           constructorElement,
129           dependencies,
130           findBindingPackage(key),
131           hasNonDefaultTypeParameters(bindingTypeElement, key.type(), types),
132           Optional.<DeclaredType>absent(),
133           Optional.<TypeElement>absent(),
134           membersInjectionRequest,
135           Kind.INJECTION,
136           Provides.Type.UNIQUE,
137           scope);
138     }
139 
140     private static final ImmutableSet<ElementKind> MEMBER_KINDS =
141         Sets.immutableEnumSet(METHOD, FIELD);
142 
membersInjectionRequest(DeclaredType type)143     private Optional<DependencyRequest> membersInjectionRequest(DeclaredType type) {
144       TypeElement typeElement = MoreElements.asType(type.asElement());
145       if (!types.isSameType(elements.getTypeElement(Object.class.getCanonicalName()).asType(),
146           typeElement.getSuperclass())) {
147         return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
148       }
149       for (Element enclosedElement : typeElement.getEnclosedElements()) {
150         if (MEMBER_KINDS.contains(enclosedElement.getKind())
151             && (isAnnotationPresent(enclosedElement, Inject.class))) {
152           return Optional.of(dependencyRequestFactory.forMembersInjectedType(type));
153         }
154       }
155       return Optional.absent();
156     }
157 
forProvidesMethod(ExecutableElement providesMethod, TypeMirror contributedBy)158     ProvisionBinding forProvidesMethod(ExecutableElement providesMethod, TypeMirror contributedBy) {
159       checkNotNull(providesMethod);
160       checkArgument(providesMethod.getKind().equals(METHOD));
161       checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED));
162       Provides providesAnnotation = providesMethod.getAnnotation(Provides.class);
163       checkArgument(providesAnnotation != null);
164       DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy);
165       ExecutableType resolvedMethod =
166           MoreTypes.asExecutable(types.asMemberOf(declaredContainer, providesMethod));
167       Key key = keyFactory.forProvidesMethod(resolvedMethod, providesMethod);
168       ImmutableSet<DependencyRequest> dependencies =
169           dependencyRequestFactory.forRequiredResolvedVariables(
170               declaredContainer,
171               providesMethod.getParameters(),
172               resolvedMethod.getParameterTypes());
173       Scope scope = Scope.scopeOf(providesMethod);
174       return new AutoValue_ProvisionBinding(
175           key,
176           providesMethod,
177           dependencies,
178           findBindingPackage(key),
179           false /* no non-default parameter types */,
180           ConfigurationAnnotations.getNullableType(providesMethod),
181           Optional.of(MoreTypes.asTypeElement(declaredContainer)),
182           Optional.<DependencyRequest>absent(),
183           Kind.PROVISION,
184           providesAnnotation.type(),
185           scope);
186     }
187 
implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest)188     ProvisionBinding implicitMapOfProviderBinding(DependencyRequest mapOfValueRequest) {
189       checkNotNull(mapOfValueRequest);
190       Optional<Key> implicitMapOfProviderKey =
191           keyFactory.implicitMapProviderKeyFrom(mapOfValueRequest.key());
192       checkArgument(
193           implicitMapOfProviderKey.isPresent(),
194           "%s is not a request for Map<K, V>",
195           mapOfValueRequest);
196       DependencyRequest implicitMapOfProviderRequest =
197           dependencyRequestFactory.forImplicitMapBinding(
198               mapOfValueRequest, implicitMapOfProviderKey.get());
199       return new AutoValue_ProvisionBinding(
200           mapOfValueRequest.key(),
201           implicitMapOfProviderRequest.requestElement(),
202           ImmutableSet.of(implicitMapOfProviderRequest),
203           findBindingPackage(mapOfValueRequest.key()),
204           false /* no non-default parameter types */,
205           Optional.<DeclaredType>absent(),
206           Optional.<TypeElement>absent(),
207           Optional.<DependencyRequest>absent(),
208           Kind.SYNTHETIC,
209           Provides.Type.MAP,
210           scopeOf(implicitMapOfProviderRequest.requestElement()));
211     }
212 
forComponent(TypeElement componentDefinitionType)213     ProvisionBinding forComponent(TypeElement componentDefinitionType) {
214       checkNotNull(componentDefinitionType);
215       return new AutoValue_ProvisionBinding(
216           keyFactory.forComponent(componentDefinitionType.asType()),
217           componentDefinitionType,
218           ImmutableSet.<DependencyRequest>of(),
219           Optional.<String>absent(),
220           false /* no non-default parameter types */,
221           Optional.<DeclaredType>absent(),
222           Optional.<TypeElement>absent(),
223           Optional.<DependencyRequest>absent(),
224           Kind.COMPONENT,
225           Provides.Type.UNIQUE,
226           Scope.unscoped());
227     }
228 
forComponentMethod(ExecutableElement componentMethod)229     ProvisionBinding forComponentMethod(ExecutableElement componentMethod) {
230       checkNotNull(componentMethod);
231       checkArgument(componentMethod.getKind().equals(METHOD));
232       checkArgument(componentMethod.getParameters().isEmpty());
233       Scope scope = Scope.scopeOf(componentMethod);
234       return new AutoValue_ProvisionBinding(
235           keyFactory.forComponentMethod(componentMethod),
236           componentMethod,
237           ImmutableSet.<DependencyRequest>of(),
238           Optional.<String>absent(),
239           false /* no non-default parameter types */,
240           ConfigurationAnnotations.getNullableType(componentMethod),
241           Optional.<TypeElement>absent(),
242           Optional.<DependencyRequest>absent(),
243           Kind.COMPONENT_PROVISION,
244           Provides.Type.UNIQUE,
245           scope);
246     }
247 
forSubcomponentBuilderMethod( ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy)248     ProvisionBinding forSubcomponentBuilderMethod(
249         ExecutableElement subcomponentBuilderMethod, TypeElement contributedBy) {
250       checkNotNull(subcomponentBuilderMethod);
251       checkArgument(subcomponentBuilderMethod.getKind().equals(METHOD));
252       checkArgument(subcomponentBuilderMethod.getParameters().isEmpty());
253       DeclaredType declaredContainer = asDeclared(contributedBy.asType());
254       return new AutoValue_ProvisionBinding(
255           keyFactory.forSubcomponentBuilderMethod(subcomponentBuilderMethod, declaredContainer),
256           subcomponentBuilderMethod,
257           ImmutableSet.<DependencyRequest>of(),
258           Optional.<String>absent(),
259           false /* no non-default parameter types */,
260           Optional.<DeclaredType>absent(),
261           Optional.of(contributedBy),
262           Optional.<DependencyRequest>absent(),
263           Kind.SUBCOMPONENT_BUILDER,
264           Provides.Type.UNIQUE,
265           Scope.unscoped());
266     }
267   }
268 }
269