dagger.internal.codegen.writing.DelegateRequestRepresentation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dagger-compiler Show documentation
Show all versions of dagger-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2017 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.writing;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.getOnlyElement;
import static dagger.internal.codegen.base.RequestKinds.requestType;
import static dagger.internal.codegen.binding.BindingRequest.bindingRequest;
import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom;
import static dagger.internal.codegen.model.BindingKind.DELEGATE;
import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XType;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
import dagger.internal.codegen.binding.Binding;
import dagger.internal.codegen.binding.BindingGraph;
import dagger.internal.codegen.binding.BindsTypeChecker;
import dagger.internal.codegen.binding.ContributionBinding;
import dagger.internal.codegen.javapoet.Expression;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.internal.codegen.model.RequestKind;
import dagger.internal.codegen.xprocessing.XTypes;
/** A {@link dagger.internal.codegen.writing.RequestRepresentation} for {@code @Binds} methods. */
final class DelegateRequestRepresentation extends RequestRepresentation {
private final ContributionBinding binding;
private final RequestKind requestKind;
private final ComponentRequestRepresentations componentRequestRepresentations;
private final XProcessingEnv processingEnv;
private final BindsTypeChecker bindsTypeChecker;
@AssistedInject
DelegateRequestRepresentation(
@Assisted ContributionBinding binding,
@Assisted RequestKind requestKind,
ComponentRequestRepresentations componentRequestRepresentations,
BindsTypeChecker bindsTypeChecker,
XProcessingEnv processingEnv) {
this.binding = checkNotNull(binding);
this.requestKind = checkNotNull(requestKind);
this.componentRequestRepresentations = componentRequestRepresentations;
this.processingEnv = processingEnv;
this.bindsTypeChecker = bindsTypeChecker;
}
/**
* Returns {@code true} if the {@code @Binds} binding's scope is stronger than the scope of the
* binding it depends on.
*/
static boolean isBindsScopeStrongerThanDependencyScope(
ContributionBinding bindsBinding, BindingGraph graph) {
checkArgument(bindsBinding.kind().equals(DELEGATE));
Binding dependencyBinding =
graph.contributionBinding(getOnlyElement(bindsBinding.dependencies()).key());
ScopeKind bindsScope = ScopeKind.get(bindsBinding);
ScopeKind dependencyScope = ScopeKind.get(dependencyBinding);
return bindsScope.isStrongerScopeThan(dependencyScope);
}
@Override
Expression getDependencyExpression(ClassName requestingClass) {
Expression delegateExpression =
componentRequestRepresentations.getDependencyExpression(
bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
requestingClass);
XType contributedType = binding.contributedType();
switch (requestKind) {
case INSTANCE:
return instanceRequiresCast(binding, delegateExpression, requestingClass, bindsTypeChecker)
? delegateExpression.castTo(contributedType)
: delegateExpression;
default:
XType requestedType = requestType(requestKind, contributedType, processingEnv);
if (XTypes.isTypeOf(requestedType, TypeNames.PROVIDER)) {
// Even though the user may have requested a javax Provider, our generated code and
// factories only work in the Dagger Provider type, so swap to that one before doing
// a cast.
requestedType = XTypes.rewrapType(requestedType, TypeNames.DAGGER_PROVIDER);
}
return castToRawTypeIfNecessary(delegateExpression, requestedType);
}
}
static boolean instanceRequiresCast(
ContributionBinding binding,
Expression delegateExpression,
ClassName requestingClass,
BindsTypeChecker bindsTypeChecker) {
// delegateExpression.type() could be Object if expression is satisfied with a raw
// Provider's get() method.
XType contributedType = binding.contributedType();
return !bindsTypeChecker.isAssignable(
delegateExpression.type(), contributedType, binding.contributionType())
&& isTypeAccessibleFrom(contributedType, requestingClass.packageName());
}
/**
* If {@code delegateExpression} can be assigned to {@code desiredType} safely, then {@code
* delegateExpression} is returned unchanged. If the {@code delegateExpression} is already a raw
* type, returns {@code delegateExpression} as well, as casting would have no effect. Otherwise,
* returns a {@link Expression#castTo(XType) casted} version of {@code delegateExpression} to the
* raw type of {@code desiredType}.
*/
// TODO(ronshapiro): this probably can be generalized for usage in InjectionMethods
private Expression castToRawTypeIfNecessary(Expression delegateExpression, XType desiredType) {
if (delegateExpression.type().isAssignableTo(desiredType)) {
return delegateExpression;
}
Expression castedExpression = delegateExpression.castTo(desiredType.getRawType());
// Casted raw type provider expression has to be wrapped parentheses, otherwise there
// will be an error when DerivedFromFrameworkInstanceRequestRepresentation appends a `get()` to
// it.
// TODO(wanyingd): change the logic to only add parenthesis when necessary.
return Expression.create(
castedExpression.type(), CodeBlock.of("($L)", castedExpression.codeBlock()));
}
private enum ScopeKind {
UNSCOPED,
SINGLE_CHECK,
DOUBLE_CHECK,
;
static ScopeKind get(Binding binding) {
return binding
.scope()
.map(scope -> scope.isReusable() ? SINGLE_CHECK : DOUBLE_CHECK)
.orElse(UNSCOPED);
}
boolean isStrongerScopeThan(ScopeKind other) {
return this.ordinal() > other.ordinal();
}
}
@AssistedFactory
static interface Factory {
DelegateRequestRepresentation create(ContributionBinding binding, RequestKind requestKind);
}
}