blob: 4d06d38ca140682acaf296d178c57a16d8e05020 [file] [log] [blame]
/*
* 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;
import static com.google.common.base.Preconditions.checkNotNull;
import static dagger.internal.codegen.AnnotationSpecs.Suppression.RAWTYPES;
import static dagger.internal.codegen.FrameworkInstanceBindingExpression.producerFromProviderBindingExpression;
import static dagger.internal.codegen.MemberSelect.staticMemberSelect;
import static dagger.internal.codegen.TypeNames.PRODUCER;
import static javax.lang.model.element.Modifier.PRIVATE;
import com.google.common.collect.ImmutableMap;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import java.util.Optional;
import javax.lang.model.util.Elements;
/** A factory of code expressions used to access a single binding in a component. */
abstract class BindingExpression {
// TODO(dpb): Put the Binding or ResolvedBindings itself here.
private final BindingKey bindingKey;
BindingExpression(BindingKey bindingKey) {
this.bindingKey = checkNotNull(bindingKey);
}
/** The key for which this instance can fulfill requests. */
final BindingKey bindingKey() {
return bindingKey;
}
/**
* Returns an expression that evaluates to the value of a request for a given kind of dependency
* on this binding.
*
* @param requestingClass the class that will contain the expression
*/
abstract CodeBlock getDependencyExpression(
DependencyRequest.Kind requestKind, ClassName requestingClass);
/** Factory for building a {@link BindingExpression}. */
static final class Factory {
private final CompilerOptions compilerOptions;
private final ClassName componentName;
private final UniqueNameSet componentFieldNames;
private final ComponentBindingExpressions componentBindingExpressions;
private final GeneratedComponentModel generatedComponentModel;
private final ImmutableMap<BindingKey, String> subcomponentNames;
private final BindingGraph graph;
private final Elements elements;
Factory(
CompilerOptions compilerOptions,
ClassName componentName,
UniqueNameSet componentFieldNames,
ComponentBindingExpressions componentBindingExpressions,
GeneratedComponentModel generatedComponentModel,
ImmutableMap<BindingKey, String> subcomponentNames,
BindingGraph graph,
Elements elements) {
this.compilerOptions = checkNotNull(compilerOptions);
this.componentName = checkNotNull(componentName);
this.componentFieldNames = checkNotNull(componentFieldNames);
this.componentBindingExpressions = checkNotNull(componentBindingExpressions);
this.generatedComponentModel = checkNotNull(generatedComponentModel);
this.subcomponentNames = checkNotNull(subcomponentNames);
this.graph = checkNotNull(graph);
this.elements = checkNotNull(elements);
}
/** Creates a binding expression for a field. */
BindingExpression forField(ResolvedBindings resolvedBindings) {
FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.empty());
MemberSelect memberSelect = MemberSelect.localField(componentName, fieldSpec.name);
return create(resolvedBindings, Optional.of(fieldSpec), memberSelect);
}
FrameworkInstanceBindingExpression forProducerFromProviderField(
ResolvedBindings resolvedBindings) {
FieldSpec fieldSpec = generateFrameworkField(resolvedBindings, Optional.of(PRODUCER));
MemberSelect memberSelect = MemberSelect.localField(componentName, fieldSpec.name);
return producerFromProviderBindingExpression(
resolvedBindings.bindingKey(),
Optional.of(fieldSpec),
generatedComponentModel,
memberSelect);
}
/** Creates a binding expression for a static method call. */
Optional<BindingExpression> forStaticMethod(ResolvedBindings resolvedBindings) {
return staticMemberSelect(resolvedBindings)
.map(memberSelect -> create(resolvedBindings, Optional.empty(), memberSelect));
}
/**
* Adds a field representing the resolved bindings, optionally forcing it to use a particular
* binding type (instead of the type the resolved bindings would typically use).
*/
private FieldSpec generateFrameworkField(
ResolvedBindings resolvedBindings, Optional<ClassName> frameworkClass) {
boolean useRawType = useRawType(resolvedBindings);
FrameworkField contributionBindingField =
FrameworkField.forResolvedBindings(resolvedBindings, frameworkClass);
FieldSpec.Builder contributionField =
FieldSpec.builder(
useRawType
? contributionBindingField.type().rawType
: contributionBindingField.type(),
componentFieldNames.getUniqueName(contributionBindingField.name()));
contributionField.addModifiers(PRIVATE);
if (useRawType) {
contributionField.addAnnotation(AnnotationSpecs.suppressWarnings(RAWTYPES));
}
return contributionField.build();
}
private boolean useRawType(ResolvedBindings resolvedBindings) {
Optional<String> bindingPackage = resolvedBindings.bindingPackage();
return bindingPackage.isPresent()
&& !bindingPackage.get().equals(componentName.packageName());
}
private BindingExpression create(
ResolvedBindings resolvedBindings,
Optional<FieldSpec> fieldSpec,
MemberSelect memberSelect) {
FrameworkInstanceBindingExpression bindingExpression =
FrameworkInstanceBindingExpression.create(
resolvedBindings, fieldSpec, generatedComponentModel, memberSelect);
if (!resolvedBindings.bindingType().equals(BindingType.PROVISION)) {
return bindingExpression;
}
ProvisionBinding provisionBinding = (ProvisionBinding) resolvedBindings.contributionBinding();
switch (provisionBinding.bindingKind()) {
case SUBCOMPONENT_BUILDER:
return new SubcomponentBuilderBindingExpression(
bindingExpression, subcomponentNames.get(resolvedBindings.bindingKey()));
case SYNTHETIC_MULTIBOUND_SET:
return new SetBindingExpression(
provisionBinding, graph, componentBindingExpressions, bindingExpression, elements);
case SYNTHETIC_OPTIONAL_BINDING:
return new OptionalBindingExpression(
provisionBinding, bindingExpression, componentBindingExpressions);
case INJECTION:
case PROVISION:
if (!provisionBinding.scope().isPresent()
&& !provisionBinding.requiresModuleInstance()
&& provisionBinding.bindingElement().isPresent()) {
return new SimpleMethodBindingExpression(
compilerOptions,
provisionBinding,
bindingExpression,
componentBindingExpressions,
generatedComponentModel);
}
// fall through
default:
return bindingExpression;
}
}
}
}