/* * Copyright (C) 2016 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen.binding; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName; import static dagger.internal.codegen.xprocessing.XElements.getSimpleName; import static dagger.internal.codegen.xprocessing.XElements.hasAnyAnnotation; import static dagger.internal.codegen.xprocessing.XTypeElements.isNested; import static dagger.internal.codegen.xprocessing.XTypes.isDeclared; import androidx.room.compiler.processing.XElement; import androidx.room.compiler.processing.XMethodElement; import androidx.room.compiler.processing.XType; import androidx.room.compiler.processing.XTypeElement; import com.google.auto.value.AutoValue; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeName; import dagger.internal.codegen.javapoet.TypeNames; import dagger.internal.codegen.model.BindingKind; import dagger.internal.codegen.model.Key; import dagger.internal.codegen.xprocessing.XTypeElements; import java.util.Optional; /** A type that a component needs an instance of. */ @AutoValue public abstract class ComponentRequirement { /** The kind of the {@link ComponentRequirement}. */ public enum Kind { /** A type listed in the component's {@code dependencies} attribute. */ DEPENDENCY, /** A type listed in the component or subcomponent's {@code modules} attribute. */ MODULE, /** * An object that is passed to a builder's {@link dagger.BindsInstance @BindsInstance} method. */ BOUND_INSTANCE, ; public boolean isBoundInstance() { return equals(BOUND_INSTANCE); } public boolean isModule() { return equals(MODULE); } } private XType type; /** The kind of requirement. */ public abstract Kind kind(); /** Returns true if this is a {@link Kind#BOUND_INSTANCE} requirement. */ // TODO(ronshapiro): consider removing this and inlining the usages final boolean isBoundInstance() { return kind().isBoundInstance(); } /** The type of the instance the component must have. */ abstract TypeName typeName(); /** The type of the instance the component must have. */ public XType type() { return type; } /** The element associated with the type of this requirement. */ public XTypeElement typeElement() { return type.getTypeElement(); } /** The action a component builder should take if it {@code null} is passed. */ public enum NullPolicy { /** Make a new instance. */ NEW, /** Throw an exception. */ THROW, /** Allow use of null values. */ ALLOW, } /** * An override for the requirement's null policy. If set, this is used as the null policy instead * of the default behavior in {@link #nullPolicy}. * *
Some implementations' null policy can be determined upon construction (e.g., for binding
* instances), but others' require Elements which must wait until {@link #nullPolicy} is called.
*/
abstract Optional An instance is only needed if there is a binding method on the module that is neither {@code
* abstract} nor {@code static}; if all bindings are one of those, then there should be no
* possible dependency on instance state in the module's bindings.
*
* Alternatively, if the module is a Kotlin Object then the binding methods are considered
* {@code static}, requiring no module instance.
*/
public boolean requiresModuleInstance() {
if (typeElement().isKotlinObject() || typeElement().isCompanionObject()) {
return false;
}
return XTypeElements.getAllNonPrivateInstanceMethods(typeElement()).stream()
.filter(this::isBindingMethod)
.anyMatch(method -> !method.isAbstract() && !method.isStatic());
}
private boolean isBindingMethod(XMethodElement method) {
// TODO(cgdecker): At the very least, we should have utility methods to consolidate this stuff
// in one place; listing individual annotations all over the place is brittle.
return hasAnyAnnotation(
method,
TypeNames.PROVIDES,
TypeNames.PRODUCES,
// TODO(ronshapiro): it would be cool to have internal meta-annotations that could describe
// these, like @AbstractBindingMethod
TypeNames.BINDS,
TypeNames.MULTIBINDS,
TypeNames.BINDS_OPTIONAL_OF);
}
/** The key for this requirement, if one is available. */
public abstract Optional