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 */
16package dagger.internal.codegen;
17
18import com.google.auto.common.MoreElements;
19import com.google.auto.common.MoreTypes;
20import com.google.auto.value.AutoValue;
21import com.google.common.base.Optional;
22import com.google.common.collect.ImmutableSet;
23import com.google.common.collect.Sets;
24import dagger.Provides;
25import javax.inject.Inject;
26import javax.lang.model.element.Element;
27import javax.lang.model.element.ElementKind;
28import javax.lang.model.element.ExecutableElement;
29import javax.lang.model.element.TypeElement;
30import javax.lang.model.type.DeclaredType;
31import javax.lang.model.type.ExecutableType;
32import javax.lang.model.type.TypeKind;
33import javax.lang.model.type.TypeMirror;
34import javax.lang.model.util.Elements;
35import javax.lang.model.util.Types;
36
37import static com.google.auto.common.MoreElements.isAnnotationPresent;
38import static com.google.auto.common.MoreTypes.asDeclared;
39import static com.google.common.base.Preconditions.checkArgument;
40import static com.google.common.base.Preconditions.checkNotNull;
41import static com.google.common.base.Preconditions.checkState;
42import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
43import static dagger.internal.codegen.Scope.scopeOf;
44import static javax.lang.model.element.ElementKind.CONSTRUCTOR;
45import static javax.lang.model.element.ElementKind.FIELD;
46import 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
56abstract class ProvisionBinding extends ContributionBinding {
57
58  @Override
59  Binding.Type bindingType() {
60    return Binding.Type.PROVISION;
61  }
62
63  @Override
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
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. */
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     */
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
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
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
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
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
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
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