134 lines
5.4 KiB
Java
134 lines
5.4 KiB
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.model.BindingKind.DELEGATE;
|
|
|
|
import com.squareup.javapoet.ClassName;
|
|
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.langmodel.DaggerElements;
|
|
import dagger.internal.codegen.langmodel.DaggerTypes;
|
|
import dagger.model.RequestKind;
|
|
import javax.lang.model.type.TypeMirror;
|
|
|
|
/** A {@link dagger.internal.codegen.writing.BindingExpression} for {@code @Binds} methods. */
|
|
final class DelegateBindingExpression extends BindingExpression {
|
|
private final ContributionBinding binding;
|
|
private final RequestKind requestKind;
|
|
private final ComponentBindingExpressions componentBindingExpressions;
|
|
private final DaggerTypes types;
|
|
private final BindsTypeChecker bindsTypeChecker;
|
|
|
|
DelegateBindingExpression(
|
|
ContributionBinding binding,
|
|
RequestKind requestKind,
|
|
ComponentBindingExpressions componentBindingExpressions,
|
|
DaggerTypes types,
|
|
DaggerElements elements) {
|
|
this.binding = checkNotNull(binding);
|
|
this.requestKind = checkNotNull(requestKind);
|
|
this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
|
|
this.types = checkNotNull(types);
|
|
this.bindsTypeChecker = new BindsTypeChecker(types, elements);
|
|
}
|
|
|
|
/**
|
|
* 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 =
|
|
componentBindingExpressions.getDependencyExpression(
|
|
bindingRequest(getOnlyElement(binding.dependencies()).key(), requestKind),
|
|
requestingClass);
|
|
|
|
TypeMirror contributedType = binding.contributedType();
|
|
switch (requestKind) {
|
|
case INSTANCE:
|
|
return instanceRequiresCast(delegateExpression, requestingClass)
|
|
? delegateExpression.castTo(contributedType)
|
|
: delegateExpression;
|
|
default:
|
|
return castToRawTypeIfNecessary(
|
|
delegateExpression, requestType(requestKind, contributedType, types));
|
|
}
|
|
}
|
|
|
|
private boolean instanceRequiresCast(Expression delegateExpression, ClassName requestingClass) {
|
|
// delegateExpression.type() could be Object if expression is satisfied with a raw
|
|
// Provider's get() method.
|
|
return !bindsTypeChecker.isAssignable(
|
|
delegateExpression.type(), binding.contributedType(), binding.contributionType())
|
|
&& isTypeAccessibleFrom(binding.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(TypeMirror) 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, TypeMirror desiredType) {
|
|
if (types.isAssignable(delegateExpression.type(), desiredType)) {
|
|
return delegateExpression;
|
|
}
|
|
return delegateExpression.castTo(types.erasure(desiredType));
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|