• 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.common.base.Preconditions.checkState;
20 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.CLASS_CONSTRUCTOR;
21 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.DELEGATE;
22 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
23 import static dagger.internal.codegen.MapKeys.unwrapValue;
24 import static dagger.internal.codegen.MoreAnnotationMirrors.unwrapOptionalEquivalence;
25 import static java.util.Arrays.asList;
26 
27 import com.google.auto.common.MoreElements;
28 import com.google.common.base.Equivalence;
29 import com.google.errorprone.annotations.CanIgnoreReturnValue;
30 import com.google.errorprone.annotations.CheckReturnValue;
31 import dagger.internal.codegen.ContributionType.HasContributionType;
32 import dagger.model.BindingKind;
33 import dagger.model.DependencyRequest;
34 import dagger.model.Key;
35 import java.util.Optional;
36 import javax.lang.model.element.AnnotationMirror;
37 import javax.lang.model.element.AnnotationValue;
38 import javax.lang.model.element.Element;
39 import javax.lang.model.element.ExecutableElement;
40 import javax.lang.model.element.TypeElement;
41 import javax.lang.model.type.DeclaredType;
42 import javax.lang.model.type.TypeMirror;
43 
44 /**
45  * An abstract class for a value object representing the mechanism by which a {@link Key} can be
46  * contributed to a dependency graph.
47  */
48 abstract class ContributionBinding extends Binding implements HasContributionType {
49 
50   /** Returns the type that specifies this' nullability, absent if not nullable. */
nullableType()51   abstract Optional<DeclaredType> nullableType();
52 
wrappedMapKeyAnnotation()53   abstract Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation();
54 
mapKeyAnnotation()55   final Optional<AnnotationMirror> mapKeyAnnotation() {
56     return unwrapOptionalEquivalence(wrappedMapKeyAnnotation());
57   }
58 
59   /**
60    * If this is a map contribution, returns the key of its map entry.
61    *
62    * @throws IllegalStateException if {@link #mapKeyAnnotation()} returns an empty value.
63    */
mapKey()64   final Object mapKey() {
65     checkState(mapKeyAnnotation().isPresent());
66     AnnotationMirror mapKeyAnnotation = mapKeyAnnotation().get();
67     return unwrapValue(mapKeyAnnotation).map(AnnotationValue::getValue).orElse(mapKeyAnnotation);
68   }
69 
70   /** If {@link #bindingElement()} is a method that returns a primitive type, returns that type. */
contributedPrimitiveType()71   final Optional<TypeMirror> contributedPrimitiveType() {
72     return bindingElement()
73         .filter(bindingElement -> bindingElement instanceof ExecutableElement)
74         .map(bindingElement -> MoreElements.asExecutable(bindingElement).getReturnType())
75         .filter(type -> type.getKind().isPrimitive());
76   }
77 
78   @Override
isNullable()79   public final boolean isNullable() {
80     return nullableType().isPresent();
81   }
82 
83   /**
84    * The strategy for getting an instance of a factory for a {@link ContributionBinding}.
85    */
86   enum FactoryCreationStrategy {
87     /** The factory class is a single instance. */
88     SINGLETON_INSTANCE,
89     /** The factory must be created by calling the constructor. */
90     CLASS_CONSTRUCTOR,
91     /** The factory is simply delegated to another. */
92     DELEGATE,
93   }
94 
95   /**
96    * Returns the {@link FactoryCreationStrategy} appropriate for a binding.
97    *
98    * <p>Delegate bindings use the {@link FactoryCreationStrategy#DELEGATE} strategy.
99    *
100    * <p>Bindings without dependencies that don't require a module instance use the {@link
101    * FactoryCreationStrategy#SINGLETON_INSTANCE} strategy.
102    *
103    * <p>All other bindings use the {@link FactoryCreationStrategy#CLASS_CONSTRUCTOR} strategy.
104    */
factoryCreationStrategy()105   final FactoryCreationStrategy factoryCreationStrategy() {
106     switch (kind()) {
107       case DELEGATE:
108         return DELEGATE;
109       case PROVISION:
110         return dependencies().isEmpty() && !requiresModuleInstance()
111             ? SINGLETON_INSTANCE
112             : CLASS_CONSTRUCTOR;
113       case INJECTION:
114       case MULTIBOUND_SET:
115       case MULTIBOUND_MAP:
116         return dependencies().isEmpty() ? SINGLETON_INSTANCE : CLASS_CONSTRUCTOR;
117       default:
118         return CLASS_CONSTRUCTOR;
119     }
120   }
121 
122   /**
123    * The {@link TypeMirror type} for the {@code Factory<T>} or {@code Producer<T>} which is created
124    * for this binding. Uses the binding's key, V in the case of {@code Map<K, FrameworkClass<V>>>},
125    * and E {@code Set<E>} for {@link dagger.multibindings.IntoSet @IntoSet} methods.
126    */
contributedType()127   final TypeMirror contributedType() {
128     switch (contributionType()) {
129       case MAP:
130         return MapType.from(key()).unwrappedFrameworkValueType();
131       case SET:
132         return SetType.from(key()).elementType();
133       case SET_VALUES:
134       case UNIQUE:
135         return key().type();
136     }
137     throw new AssertionError();
138   }
139 
isSyntheticMultibinding()140   final boolean isSyntheticMultibinding() {
141     switch (kind()) {
142       case MULTIBOUND_SET:
143       case MULTIBOUND_MAP:
144         return true;
145       default:
146         return false;
147     }
148   }
149 
150   /** Whether the bound type has a generated implementation. */
requiresGeneratedInstance()151   final boolean requiresGeneratedInstance() {
152     switch (kind()) {
153       case COMPONENT:
154       case SUBCOMPONENT_CREATOR:
155         return true;
156       default:
157         return false;
158     }
159   }
160 
161   /**
162    * Returns {@link BindingKind#MULTIBOUND_SET} or {@link
163    * BindingKind#MULTIBOUND_MAP} if the key is a set or map.
164    *
165    * @throws IllegalArgumentException if {@code key} is neither a set nor a map
166    */
bindingKindForMultibindingKey(Key key)167   static BindingKind bindingKindForMultibindingKey(Key key) {
168     if (SetType.isSet(key)) {
169       return BindingKind.MULTIBOUND_SET;
170     } else if (MapType.isMap(key)) {
171       return BindingKind.MULTIBOUND_MAP;
172     } else {
173       throw new IllegalArgumentException(String.format("key is not for a set or map: %s", key));
174     }
175   }
176 
177   /**
178    * Base builder for {@link com.google.auto.value.AutoValue @AutoValue} subclasses of {@link
179    * ContributionBinding}.
180    */
181   @CanIgnoreReturnValue
182   abstract static class Builder<C extends ContributionBinding, B extends Builder<C, B>> {
dependencies(Iterable<DependencyRequest> dependencies)183     abstract B dependencies(Iterable<DependencyRequest> dependencies);
184 
dependencies(DependencyRequest... dependencies)185     B dependencies(DependencyRequest... dependencies) {
186       return dependencies(asList(dependencies));
187     }
188 
unresolved(C unresolved)189     abstract B unresolved(C unresolved);
190 
contributionType(ContributionType contributionType)191     abstract B contributionType(ContributionType contributionType);
192 
bindingElement(Element bindingElement)193     abstract B bindingElement(Element bindingElement);
194 
contributingModule(TypeElement contributingModule)195     abstract B contributingModule(TypeElement contributingModule);
196 
key(Key key)197     abstract B key(Key key);
198 
nullableType(Optional<DeclaredType> nullableType)199     abstract B nullableType(Optional<DeclaredType> nullableType);
200 
wrappedMapKeyAnnotation( Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation)201     abstract B wrappedMapKeyAnnotation(
202         Optional<Equivalence.Wrapper<AnnotationMirror>> wrappedMapKeyAnnotation);
203 
kind(BindingKind kind)204     abstract B kind(BindingKind kind);
205 
206     @CheckReturnValue
build()207     abstract C build();
208   }
209 }
210