blob: 69dac084bf5916c2cff98006394472d41257d771 [file] [log] [blame]
/*
* Copyright (C) 2014 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.checkState;
import static dagger.internal.codegen.DaggerStreams.presentValues;
import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.graph.Traverser;
import dagger.Subcomponent;
import dagger.model.Key;
import dagger.model.RequestKind;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.stream.StreamSupport;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
/**
* The canonical representation of a full-resolved graph.
*/
@AutoValue
abstract class BindingGraph {
abstract ComponentDescriptor componentDescriptor();
/**
* The resolved bindings for all {@link ContributionBinding}s in this graph, keyed by {@link Key}.
*/
// TODO(ronshapiro): when MembersInjectionBinding no longer extends Binding, rename this to
// bindings()
abstract ImmutableMap<Key, ResolvedBindings> contributionBindings();
/**
* The resolved bindings for all {@link MembersInjectionBinding}s in this graph, keyed by {@link
* Key}.
*/
abstract ImmutableMap<Key, ResolvedBindings> membersInjectionBindings();
/**
* Returns the {@link ResolvedBindings resolved bindings} instance for {@code
* bindingExpressionKey}. If the bindings will be used for members injection, a {@link
* ResolvedBindings} with {@linkplain #membersInjectionBindings() members injection bindings} will
* be returned, otherwise a {@link ResolvedBindings} with {@link #contributionBindings()} will be
* returned.
*/
final ResolvedBindings resolvedBindings(BindingRequest request) {
return request.isRequestKind(RequestKind.MEMBERS_INJECTION)
? membersInjectionBindings().get(request.key())
: contributionBindings().get(request.key());
}
@Memoized
ImmutableSet<ResolvedBindings> resolvedBindings() {
return ImmutableSet.<ResolvedBindings>builder()
.addAll(membersInjectionBindings().values())
.addAll(contributionBindings().values())
.build();
}
abstract ImmutableList<BindingGraph> subgraphs();
/**
* The type that defines the component for this graph.
*
* @see ComponentDescriptor#typeElement()
*/
TypeElement componentTypeElement() {
return componentDescriptor().typeElement();
}
/**
* Returns the set of modules that are owned by this graph regardless of whether or not any of
* their bindings are used in this graph. For graphs representing top-level {@link
* dagger.Component components}, this set will be the same as {@linkplain
* ComponentDescriptor#modules() the component's transitive modules}. For {@linkplain Subcomponent
* subcomponents}, this set will be the transitive modules that are not owned by any of their
* ancestors.
*/
abstract ImmutableSet<ModuleDescriptor> ownedModules();
ImmutableSet<TypeElement> ownedModuleTypes() {
return FluentIterable.from(ownedModules()).transform(ModuleDescriptor::moduleElement).toSet();
}
/**
* Returns the factory method for this subcomponent, if it exists.
*
* <p>This factory method is the one defined in the parent component's interface.
*
* <p>In the example below, the {@link BindingGraph#factoryMethod} for {@code ChildComponent}
* would return the {@link ExecutableElement}: {@code childComponent(ChildModule1)} .
*
* <pre><code>
* {@literal @Component}
* interface ParentComponent {
* ChildComponent childComponent(ChildModule1 childModule);
* }
* </code></pre>
*/
// TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
abstract Optional<ExecutableElement> factoryMethod();
/**
* Returns a map between the {@linkplain ComponentRequirement component requirement} and the
* corresponding {@link VariableElement} for each module parameter in the {@linkplain
* BindingGraph#factoryMethod factory method}.
*/
// TODO(dpb): Consider disallowing modules if none of their bindings are used.
ImmutableMap<ComponentRequirement, VariableElement> factoryMethodParameters() {
checkState(factoryMethod().isPresent());
ImmutableMap.Builder<ComponentRequirement, VariableElement> builder = ImmutableMap.builder();
for (VariableElement parameter : factoryMethod().get().getParameters()) {
builder.put(ComponentRequirement.forModule(parameter.asType()), parameter);
}
return builder.build();
}
private static final Traverser<BindingGraph> SUBGRAPH_TRAVERSER =
Traverser.forTree(BindingGraph::subgraphs);
/**
* The types for which the component needs instances.
*
* <ul>
* <li>component dependencies
* <li>{@linkplain #ownedModules() owned modules} with concrete instance bindings that are used
* in the graph
* <li>bound instances
* </ul>
*/
@Memoized
ImmutableSet<ComponentRequirement> componentRequirements() {
return componentRequirements(
StreamSupport.stream(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this).spliterator(), false)
.flatMap(graph -> graph.contributionBindings().values().stream())
.flatMap(bindings -> bindings.contributionBindings().stream())
.collect(toImmutableSet()));
}
/**
* The types for which the component may need instances, depending on how it is resolved in a
* parent component.
*
* <ul>
* <li>{@linkplain #ownedModules() Owned modules} with concrete instance bindings. If the module
* is never used in the fully resolved binding graph, the instance will not be required
* unless a component builder requests it.
* <li>Bound instances (always required)
* </ul>
*/
@Memoized
ImmutableSet<ComponentRequirement> possiblyNecessaryRequirements() {
checkState(!componentDescriptor().kind().isRoot());
return componentRequirements(
StreamSupport.stream(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this).spliterator(), false)
.flatMap(graph -> graph.ownedModules().stream())
.flatMap(module -> module.bindings().stream())
.collect(toImmutableSet()));
}
/**
* The types for which the component needs instances.
*
* <ul>
* <li>component dependencies
* <li>The modules of {@code bindings} that require a module instance
* <li>bound instances
* </ul>
*/
private ImmutableSet<ComponentRequirement> componentRequirements(
ImmutableSet<ContributionBinding> bindings) {
ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
bindings.stream()
.filter(ContributionBinding::requiresModuleInstance)
.map(ContributionBinding::contributingModule)
.flatMap(presentValues())
.filter(module -> ownedModuleTypes().contains(module))
.map(module -> ComponentRequirement.forModule(module.asType()))
.forEach(requirements::add);
if (factoryMethod().isPresent()) {
factoryMethodParameters().keySet().forEach(requirements::add);
}
requirements.addAll(componentDescriptor().dependencies());
componentDescriptor()
.creatorDescriptor()
.ifPresent(
creatorDescriptor ->
creatorDescriptor.boundInstanceRequirements().forEach(requirements::add));
return requirements.build();
}
/** Returns the {@link ComponentDescriptor}s for this component and its subcomponents. */
ImmutableSet<ComponentDescriptor> componentDescriptors() {
return FluentIterable.from(SUBGRAPH_TRAVERSER.depthFirstPreOrder(this))
.transform(BindingGraph::componentDescriptor)
.toSet();
}
@Memoized
@Override
public abstract int hashCode();
@Override // Suppresses ErrorProne warning that hashCode was overridden w/o equals
public abstract boolean equals(Object other);
static BindingGraph create(
ComponentDescriptor componentDescriptor,
ImmutableMap<Key, ResolvedBindings> resolvedContributionBindingsMap,
ImmutableMap<Key, ResolvedBindings> resolvedMembersInjectionBindings,
ImmutableList<BindingGraph> subgraphs,
ImmutableSet<ModuleDescriptor> ownedModules,
Optional<ExecutableElement> factoryMethod) {
checkForDuplicates(subgraphs);
return new AutoValue_BindingGraph(
componentDescriptor,
resolvedContributionBindingsMap,
resolvedMembersInjectionBindings,
subgraphs,
ownedModules,
factoryMethod);
}
private static final void checkForDuplicates(Iterable<BindingGraph> graphs) {
Map<TypeElement, Collection<BindingGraph>> duplicateGraphs =
Maps.filterValues(
Multimaps.index(graphs, graph -> graph.componentDescriptor().typeElement()).asMap(),
overlapping -> overlapping.size() > 1);
if (!duplicateGraphs.isEmpty()) {
throw new IllegalArgumentException("Expected no duplicates: " + duplicateGraphs);
}
}
}