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