/* * 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(); } } }