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