• 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;
18 
19 import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
20 import static com.google.common.base.Preconditions.checkArgument;
21 import static com.google.common.base.Preconditions.checkNotNull;
22 import static dagger.internal.codegen.SourceFiles.simpleVariableName;
23 import static dagger.internal.codegen.Util.componentCanMakeNewInstances;
24 import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent;
25 import static javax.lang.model.element.Modifier.ABSTRACT;
26 import static javax.lang.model.element.Modifier.STATIC;
27 
28 import com.google.auto.common.MoreTypes;
29 import com.google.auto.value.AutoValue;
30 import com.google.common.base.Equivalence;
31 import com.google.common.collect.ImmutableSet;
32 import com.squareup.javapoet.ParameterSpec;
33 import com.squareup.javapoet.TypeName;
34 import dagger.Binds;
35 import dagger.BindsOptionalOf;
36 import dagger.Provides;
37 import dagger.internal.codegen.langmodel.DaggerElements;
38 import dagger.internal.codegen.langmodel.DaggerTypes;
39 import dagger.internal.codegen.serialization.ComponentRequirementProto;
40 import dagger.internal.codegen.serialization.ComponentRequirementProto.BoundInstanceRequirement;
41 import dagger.model.BindingKind;
42 import dagger.model.Key;
43 import dagger.multibindings.Multibinds;
44 import dagger.producers.Produces;
45 import java.util.Optional;
46 import javax.lang.model.element.ExecutableElement;
47 import javax.lang.model.element.TypeElement;
48 import javax.lang.model.type.TypeMirror;
49 
50 /** A type that a component needs an instance of. */
51 @AutoValue
52 abstract class ComponentRequirement {
53   enum Kind {
54     /** A type listed in the component's {@code dependencies} attribute. */
55     DEPENDENCY,
56 
57     /** A type listed in the component or subcomponent's {@code modules} attribute. */
58     MODULE,
59 
60     /**
61      * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method.
62      */
63     BOUND_INSTANCE,
64     ;
65 
isBoundInstance()66     boolean isBoundInstance() {
67       return equals(BOUND_INSTANCE);
68     }
69 
isModule()70     boolean isModule() {
71       return equals(MODULE);
72     }
73   }
74 
75   /** The kind of requirement. */
kind()76   abstract Kind kind();
77 
78   /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */
79   // TODO(ronshapiro): consider removing this and inlining the usages
isBoundInstance()80   final boolean isBoundInstance() {
81     return kind().isBoundInstance();
82   }
83 
84   /**
85    * The type of the instance the component must have, wrapped so that requirements can be used as
86    * value types.
87    */
wrappedType()88   abstract Equivalence.Wrapper<TypeMirror> wrappedType();
89 
90   /** The type of the instance the component must have. */
type()91   TypeMirror type() {
92     return wrappedType().get();
93   }
94 
95   /** The element associated with the type of this requirement. */
typeElement()96   TypeElement typeElement() {
97     return MoreTypes.asTypeElement(type());
98   }
99 
100   /** The action a component builder should take if it {@code null} is passed. */
101   enum NullPolicy {
102     /** Make a new instance. */
103     NEW,
104     /** Throw an exception. */
105     THROW,
106     /** Allow use of null values. */
107     ALLOW,
108   }
109 
110   /**
111    * An override for the requirement's null policy. If set, this is used as the null policy instead
112    * of the default behavior in {@link #nullPolicy}.
113    *
114    * <p>Some implementations' null policy can be determined upon construction (e.g., for binding
115    * instances), but others' require Elements and Types, which must wait until {@link #nullPolicy}
116    * is called.
117    */
overrideNullPolicy()118   abstract Optional<NullPolicy> overrideNullPolicy();
119 
120   /** The requirement's null policy. */
nullPolicy(DaggerElements elements, DaggerTypes types)121   NullPolicy nullPolicy(DaggerElements elements, DaggerTypes types) {
122     if (overrideNullPolicy().isPresent()) {
123       return overrideNullPolicy().get();
124     }
125     switch (kind()) {
126       case MODULE:
127         return componentCanMakeNewInstances(typeElement())
128             ? NullPolicy.NEW
129             : requiresAPassedInstance(elements, types) ? 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(DaggerElements elements, DaggerTypes types)141   boolean requiresAPassedInstance(DaggerElements elements, DaggerTypes types) {
142     if (!kind().isModule()) {
143       // Bound instances and dependencies always require the user to provide an instance.
144       return true;
145     }
146     return requiresModuleInstance(elements, types) && !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    */
requiresModuleInstance(DaggerElements elements, DaggerTypes types)156   private boolean requiresModuleInstance(DaggerElements elements, DaggerTypes types) {
157     ImmutableSet<ExecutableElement> methods =
158         getLocalAndInheritedMethods(typeElement(), types, elements);
159     return methods.stream()
160         .filter(this::isBindingMethod)
161         .map(ExecutableElement::getModifiers)
162         .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC));
163   }
164 
isBindingMethod(ExecutableElement method)165   private boolean isBindingMethod(ExecutableElement method) {
166     // TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff
167     // in one place; listing individual annotations all over the place is brittle.
168     return isAnyAnnotationPresent(
169         method,
170         Provides.class,
171         Produces.class,
172         // TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
173         // these, like @AbstractBindingMethod
174         Binds.class,
175         Multibinds.class,
176         BindsOptionalOf.class);
177   }
178 
179   /** The key for this requirement, if one is available. */
key()180   abstract Optional<Key> key();
181 
182   /** Returns the name for this requirement that could be used as a variable. */
variableName()183   abstract String variableName();
184 
185   /** Returns a parameter spec for this requirement. */
toParameterSpec()186   ParameterSpec toParameterSpec() {
187     return ParameterSpec.builder(TypeName.get(type()), variableName()).build();
188   }
189 
190   /** Creates a proto representation of this requirement. */
toProto()191   ComponentRequirementProto toProto() {
192     switch (kind()) {
193       case DEPENDENCY:
194         return ComponentRequirementProto.newBuilder()
195             .setDependency(TypeProtoConverter.toProto(type()))
196             .build();
197       case MODULE:
198         return ComponentRequirementProto.newBuilder()
199             .setModule(TypeProtoConverter.toProto(type()))
200             .build();
201       case BOUND_INSTANCE:
202         return ComponentRequirementProto.newBuilder()
203             .setBoundInstance(
204                 BoundInstanceRequirement.newBuilder()
205                     .setKey(KeyFactory.toProto(key().get()))
206                     .setNullable(overrideNullPolicy().equals(Optional.of(NullPolicy.ALLOW)))
207                     .setVariableName(variableName()))
208             .build();
209     }
210     throw new AssertionError(this);
211   }
212 
forDependency(TypeMirror type)213   static ComponentRequirement forDependency(TypeMirror type) {
214     return new AutoValue_ComponentRequirement(
215         Kind.DEPENDENCY,
216         MoreTypes.equivalence().wrap(checkNotNull(type)),
217         Optional.empty(),
218         Optional.empty(),
219         simpleVariableName(MoreTypes.asTypeElement(type)));
220   }
221 
forModule(TypeMirror type)222   static ComponentRequirement forModule(TypeMirror type) {
223     return new AutoValue_ComponentRequirement(
224         Kind.MODULE,
225         MoreTypes.equivalence().wrap(checkNotNull(type)),
226         Optional.empty(),
227         Optional.empty(),
228         simpleVariableName(MoreTypes.asTypeElement(type)));
229   }
230 
forBoundInstance(Key key, boolean nullable, String variableName)231   static ComponentRequirement forBoundInstance(Key key, boolean nullable, String variableName) {
232     return new AutoValue_ComponentRequirement(
233         Kind.BOUND_INSTANCE,
234         MoreTypes.equivalence().wrap(key.type()),
235         nullable ? Optional.of(NullPolicy.ALLOW) : Optional.empty(),
236         Optional.of(key),
237         variableName);
238   }
239 
forBoundInstance(ContributionBinding binding)240   static ComponentRequirement forBoundInstance(ContributionBinding binding) {
241     checkArgument(binding.kind().equals(BindingKind.BOUND_INSTANCE));
242     return forBoundInstance(
243         binding.key(),
244         binding.nullableType().isPresent(),
245         binding.bindingElement().get().getSimpleName().toString());
246   }
247 }
248