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.MoreTypes;
19 import com.google.auto.value.AutoValue;
20 import com.google.common.base.Optional;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Sets;
24 import com.google.common.util.concurrent.ListenableFuture;
25 import dagger.Provides;
26 import dagger.producers.Produces;
27 import java.util.Set;
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.Types;
35 
36 import static com.google.common.base.Preconditions.checkArgument;
37 import static com.google.common.base.Preconditions.checkNotNull;
38 import static javax.lang.model.element.ElementKind.METHOD;
39 
40 /**
41  * A value object representing the mechanism by which a {@link Key} can be produced. New instances
42  * should be created using an instance of the {@link Factory}.
43  *
44  * @author Jesse Beder
45  * @since 2.0
46  */
47 @AutoValue
48 abstract class ProductionBinding extends ContributionBinding {
49 
50   @Override
bindingType()51   Binding.Type bindingType() {
52     return Binding.Type.PRODUCTION;
53   }
54 
55   @Override
provisionType()56   Provides.Type provisionType() {
57     return Provides.Type.valueOf(productionType().name());
58   }
59 
60   @Override
implicitDependencies()61   Set<DependencyRequest> implicitDependencies() {
62     // Similar optimizations to ContributionBinding.implicitDependencies().
63     if (!monitorRequest().isPresent()) {
64       return super.implicitDependencies();
65     } else {
66       return Sets.union(monitorRequest().asSet(), super.implicitDependencies());
67     }
68   }
69 
70   /** Returns provision type that was used to bind the key. */
productionType()71   abstract Produces.Type productionType();
72 
73   /** Returns the list of types in the throws clause of the method. */
thrownTypes()74   abstract ImmutableList<? extends TypeMirror> thrownTypes();
75 
76   /** If this production requires a monitor, this will be the corresponding request. */
monitorRequest()77   abstract Optional<DependencyRequest> monitorRequest();
78 
79   @Override
contributionType()80   ContributionType contributionType() {
81     switch (productionType()) {
82       case SET:
83       case SET_VALUES:
84         return ContributionType.SET;
85       case MAP:
86         return ContributionType.MAP;
87       case UNIQUE:
88         return ContributionType.UNIQUE;
89       default:
90         throw new AssertionError("Unknown production type: " + productionType());
91     }
92   }
93 
94   static final class Factory {
95     private final Types types;
96     private final Key.Factory keyFactory;
97     private final DependencyRequest.Factory dependencyRequestFactory;
98 
Factory( Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory)99     Factory(
100         Types types, Key.Factory keyFactory, DependencyRequest.Factory dependencyRequestFactory) {
101       this.types = types;
102       this.keyFactory = keyFactory;
103       this.dependencyRequestFactory = dependencyRequestFactory;
104     }
105 
forProducesMethod( ExecutableElement producesMethod, TypeMirror contributedBy)106     ProductionBinding forProducesMethod(
107         ExecutableElement producesMethod, TypeMirror contributedBy) {
108       checkNotNull(producesMethod);
109       checkArgument(producesMethod.getKind().equals(METHOD));
110       checkArgument(contributedBy.getKind().equals(TypeKind.DECLARED));
111       Produces producesAnnotation = producesMethod.getAnnotation(Produces.class);
112       checkArgument(producesAnnotation != null);
113       DeclaredType declaredContainer = MoreTypes.asDeclared(contributedBy);
114       ExecutableType resolvedMethod =
115           MoreTypes.asExecutable(types.asMemberOf(declaredContainer, producesMethod));
116       Key key = keyFactory.forProducesMethod(resolvedMethod, producesMethod);
117       ImmutableSet<DependencyRequest> dependencies =
118           dependencyRequestFactory.forRequiredResolvedVariables(
119               declaredContainer,
120               producesMethod.getParameters(),
121               resolvedMethod.getParameterTypes());
122       DependencyRequest monitorRequest =
123           dependencyRequestFactory.forProductionComponentMonitorProvider();
124       Kind kind = MoreTypes.isTypeOf(ListenableFuture.class, producesMethod.getReturnType())
125           ? Kind.FUTURE_PRODUCTION
126           : Kind.IMMEDIATE;
127       return new AutoValue_ProductionBinding(
128           key,
129           producesMethod,
130           dependencies,
131           findBindingPackage(key),
132           false,
133           ConfigurationAnnotations.getNullableType(producesMethod),
134           Optional.of(MoreTypes.asTypeElement(declaredContainer)),
135           Optional.<DependencyRequest>absent(),
136           kind,
137           producesAnnotation.type(),
138           ImmutableList.copyOf(producesMethod.getThrownTypes()),
139           Optional.of(monitorRequest));
140     }
141 
implicitMapOfProducerBinding(DependencyRequest mapOfValueRequest)142     ProductionBinding implicitMapOfProducerBinding(DependencyRequest mapOfValueRequest) {
143       checkNotNull(mapOfValueRequest);
144       Optional<Key> implicitMapOfProducerKey =
145           keyFactory.implicitMapProducerKeyFrom(mapOfValueRequest.key());
146       checkArgument(
147           implicitMapOfProducerKey.isPresent(), "%s is not for a Map<K, V>", mapOfValueRequest);
148       DependencyRequest implicitMapOfProducerRequest =
149           dependencyRequestFactory.forImplicitMapBinding(
150               mapOfValueRequest, implicitMapOfProducerKey.get());
151       return new AutoValue_ProductionBinding(
152           mapOfValueRequest.key(),
153           implicitMapOfProducerRequest.requestElement(),
154           ImmutableSet.of(implicitMapOfProducerRequest),
155           findBindingPackage(mapOfValueRequest.key()),
156           false,
157           Optional.<DeclaredType>absent(),
158           Optional.<TypeElement>absent(),
159           Optional.<DependencyRequest>absent(),
160           Kind.SYNTHETIC,
161           Produces.Type.MAP,
162           ImmutableList.<TypeMirror>of(),
163           Optional.<DependencyRequest>absent());
164     }
165 
forComponentMethod(ExecutableElement componentMethod)166     ProductionBinding forComponentMethod(ExecutableElement componentMethod) {
167       checkNotNull(componentMethod);
168       checkArgument(componentMethod.getKind().equals(METHOD));
169       checkArgument(componentMethod.getParameters().isEmpty());
170       checkArgument(MoreTypes.isTypeOf(ListenableFuture.class, componentMethod.getReturnType()));
171       return new AutoValue_ProductionBinding(
172           keyFactory.forProductionComponentMethod(componentMethod),
173           componentMethod,
174           ImmutableSet.<DependencyRequest>of(),
175           Optional.<String>absent(),
176           false,
177           Optional.<DeclaredType>absent(),
178           Optional.<TypeElement>absent(),
179           Optional.<DependencyRequest>absent(),
180           Kind.COMPONENT_PRODUCTION,
181           Produces.Type.UNIQUE,
182           ImmutableList.copyOf(componentMethod.getThrownTypes()),
183           Optional.<DependencyRequest>absent());
184     }
185   }
186 }
187