• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName;
22 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
23 import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation;
24 import static dagger.internal.codegen.xprocessing.XTypeElements.isNested;
25 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
26 
27 import androidx.room.compiler.processing.XElement;
28 import androidx.room.compiler.processing.XMethodElement;
29 import androidx.room.compiler.processing.XType;
30 import androidx.room.compiler.processing.XTypeElement;
31 import com.google.auto.value.AutoValue;
32 import com.squareup.javapoet.ParameterSpec;
33 import com.squareup.javapoet.TypeName;
34 import dagger.internal.codegen.javapoet.TypeNames;
35 import dagger.internal.codegen.model.BindingKind;
36 import dagger.internal.codegen.model.Key;
37 import dagger.internal.codegen.xprocessing.Nullability;
38 import dagger.internal.codegen.xprocessing.XTypeElements;
39 import java.util.Optional;
40 
41 /** A type that a component needs an instance of. */
42 @AutoValue
43 public abstract class ComponentRequirement {
44   /** The kind of the {@link ComponentRequirement}. */
45   public enum Kind {
46     /** A type listed in the component's {@code dependencies} attribute. */
47     DEPENDENCY,
48 
49     /** A type listed in the component or subcomponent's {@code modules} attribute. */
50     MODULE,
51 
52     /**
53      * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method.
54      */
55     BOUND_INSTANCE,
56     ;
57 
isBoundInstance()58     public boolean isBoundInstance() {
59       return equals(BOUND_INSTANCE);
60     }
61 
isModule()62     public boolean isModule() {
63       return equals(MODULE);
64     }
65   }
66 
67   private XType type;
68 
69   /** The kind of requirement. */
kind()70   public abstract Kind kind();
71 
72   /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */
73   // TODO(ronshapiro): consider removing this and inlining the usages
isBoundInstance()74   final boolean isBoundInstance() {
75     return kind().isBoundInstance();
76   }
77 
78   /** The type of the instance the component must have. */
typeName()79   abstract TypeName typeName();
80 
81   /** The type of the instance the component must have. */
type()82   public XType type() {
83     return type;
84   }
85 
86   /** The element associated with the type of this requirement. */
typeElement()87   public XTypeElement typeElement() {
88     return type.getTypeElement();
89   }
90 
91   /** The action a component builder should take if it {@code null} is passed. */
92   public enum NullPolicy {
93     /** Make a new instance. */
94     NEW,
95     /** Throw an exception. */
96     THROW,
97     /** Allow use of null values. */
98     ALLOW,
99   }
100 
101   /**
102    * An override for the requirement's null policy. If set, this is used as the null policy instead
103    * of the default behavior in {@link #nullPolicy}.
104    *
105    * <p>Some implementations' null policy can be determined upon construction (e.g., for binding
106    * instances), but others' require Elements which must wait until {@link #nullPolicy} is called.
107    */
overrideNullPolicy()108   abstract Optional<NullPolicy> overrideNullPolicy();
109 
110   /**
111    * The nullability of the requirement. If set, this is used to determine the nullability of the
112    * requirement's type.
113    */
getNullability()114   public Nullability getNullability() {
115     return nullability;
116   }
117 
118   private Nullability nullability = Nullability.NOT_NULLABLE;
119 
120   /** The requirement's null policy. */
nullPolicy()121   public NullPolicy nullPolicy() {
122     if (overrideNullPolicy().isPresent()) {
123       return overrideNullPolicy().get();
124     }
125     switch (kind()) {
126       case MODULE:
127         return componentCanMakeNewInstances(typeElement())
128             ? NullPolicy.NEW
129             : requiresAPassedInstance() ? NullPolicy.THROW : NullPolicy.ALLOW;
130       case DEPENDENCY:
131       case BOUND_INSTANCE:
132         return NullPolicy.THROW;
133     }
134     throw new AssertionError();
135   }
136 
137   /**
138    * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to
139    * be used within a component.
140    */
requiresAPassedInstance()141   public boolean requiresAPassedInstance() {
142     if (!kind().isModule()) {
143       // Bound instances and dependencies always require the user to provide an instance.
144       return true;
145     }
146     return requiresModuleInstance() && !componentCanMakeNewInstances(typeElement());
147   }
148 
149   /**
150    * Returns {@code true} if an instance is needed for this (module) requirement.
151    *
152    * <p>An instance is only needed if there is a binding method on the module that is neither {@code
153    * abstract} nor {@code static}; if all bindings are one of those, then there should be no
154    * possible dependency on instance state in the module's bindings.
155    *
156    * <p>Alternatively, if the module is a Kotlin Object then the binding methods are considered
157    * {@code static}, requiring no module instance.
158    */
requiresModuleInstance()159   public boolean requiresModuleInstance() {
160     if (typeElement().isKotlinObject() || typeElement().isCompanionObject()) {
161       return false;
162     }
163     return XTypeElements.getAllNonPrivateInstanceMethods(typeElement()).stream()
164         .filter(this::isBindingMethod)
165         .anyMatch(method -> !method.isAbstract() && !method.isStatic());
166   }
167 
isBindingMethod(XMethodElement method)168   private boolean isBindingMethod(XMethodElement method) {
169     // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff
170     // in one place; listing individual annotations all over the place is brittle.
171     return hasAnyAnnotation(
172         method,
173         TypeNames.PROVIDES,
174         TypeNames.PRODUCES,
175         // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
176         // these, like @AbstractBindingMethod
177         TypeNames.BINDS,
178         TypeNames.MULTIBINDS,
179         TypeNames.BINDS_OPTIONAL_OF);
180   }
181 
182   /** The key for this requirement, if one is available. */
key()183   public abstract Optional<Key> key();
184 
185   /** Returns the name for this requirement that could be used as a variable. */
variableName()186   public abstract String variableName();
187 
188   /** Returns a parameter spec for this requirement. */
toParameterSpec()189   public ParameterSpec toParameterSpec() {
190     return ParameterSpec.builder(type().getTypeName(), variableName()).build();
191   }
192 
forDependency(ComponentDependencyBinding binding)193   public static ComponentRequirement forDependency(ComponentDependencyBinding binding) {
194     return forDependency(binding.key().type().xprocessing());
195   }
196 
forDependency(XType type)197   public static ComponentRequirement forDependency(XType type) {
198     checkArgument(isDeclared(checkNotNull(type)));
199     return create(Kind.DEPENDENCY, type);
200   }
201 
forModule(XType type)202   public static ComponentRequirement forModule(XType type) {
203     checkArgument(isDeclared(checkNotNull(type)));
204     return create(Kind.MODULE, type);
205   }
206 
forBoundInstance(BoundInstanceBinding binding)207   public static ComponentRequirement forBoundInstance(BoundInstanceBinding binding) {
208     checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE));
209     return forBoundInstance(
210         binding.key(), binding.isNullable(), binding.bindingElement().get(), binding.nullability());
211   }
212 
forBoundInstance( Key key, boolean nullable, XElement elementForVariableName, Nullability nullability)213   static ComponentRequirement forBoundInstance(
214       Key key, boolean nullable, XElement elementForVariableName, Nullability nullability) {
215     return create(
216         Kind.BOUND_INSTANCE,
217         key.type().xprocessing(),
218         nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(),
219         Optional.of(key),
220         nullability,
221         getSimpleName(elementForVariableName));
222   }
223 
create(Kind kind, XType type)224   private static ComponentRequirement create(Kind kind, XType type) {
225     return create(
226         kind,
227         type,
228         /* overrideNullPolicy= */ Optional.empty(),
229         /* key= */ Optional.empty(),
230         Nullability.NOT_NULLABLE,
231         simpleVariableName(type.getTypeElement().asClassName()));
232   }
233 
create( Kind kind, XType type, Optional<NullPolicy> overrideNullPolicy, Optional<Key> key, Nullability nullability, String variableName)234   private static ComponentRequirement create(
235       Kind kind,
236       XType type,
237       Optional<NullPolicy> overrideNullPolicy,
238       Optional<Key> key,
239       Nullability nullability,
240       String variableName) {
241     ComponentRequirement requirement =
242         new AutoValue_ComponentRequirement(
243             kind, type.getTypeName(), overrideNullPolicy, key, variableName);
244     requirement.nullability = nullability;
245     requirement.type = type;
246     return requirement;
247   }
248 
249   /**
250    * Returns true if and only if a component can instantiate new instances (typically of a module)
251    * rather than requiring that they be passed.
252    */
253   // TODO(bcorso): Should this method throw if its called knowing that an instance is not needed?
componentCanMakeNewInstances(XTypeElement typeElement)254   public static boolean componentCanMakeNewInstances(XTypeElement typeElement) {
255     // TODO(gak): still need checks for visibility
256     return typeElement.isClass()
257         && !typeElement.isAbstract()
258         && !requiresEnclosingInstance(typeElement)
259         && hasVisibleDefaultConstructor(typeElement);
260   }
261 
requiresEnclosingInstance(XTypeElement typeElement)262   private static boolean requiresEnclosingInstance(XTypeElement typeElement) {
263     return isNested(typeElement) && !typeElement.isStatic();
264   }
265 
hasVisibleDefaultConstructor(XTypeElement typeElement)266   private static boolean hasVisibleDefaultConstructor(XTypeElement typeElement) {
267     return typeElement.getConstructors().stream()
268         .anyMatch(constructor -> !constructor.isPrivate() && constructor.getParameters().isEmpty());
269   }
270 }
271