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