dagger.internal.codegen.ResolvedBindings.java Source code

Java tutorial

Introduction

Here is the source code for dagger.internal.codegen.ResolvedBindings.java

Source

/*
 * Copyright (C) 2015 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.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.util.stream.Collectors.toSet;

import com.google.auto.value.AutoValue;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import dagger.internal.codegen.BindingType.HasBindingType;
import dagger.internal.codegen.ContributionType.HasContributionType;
import dagger.internal.codegen.Key.HasKey;
import java.util.Optional;
import java.util.Set;

/**
 * The collection of bindings that have been resolved for a binding key. For valid graphs, contains
 * exactly one binding.
 *
 * @author Gregory Kick
 */
@AutoValue
abstract class ResolvedBindings implements HasBindingType, HasContributionType, HasKey {
    /**
     * The binding key for which the {@link #bindings()} have been resolved.
     */
    abstract BindingKey bindingKey();

    /**
     * The component in which the bindings in {@link #ownedBindings()},
     * {@link #ownedContributionBindings()}, and {@link #ownedMembersInjectionBinding()} were
     * resolved.
     */
    abstract ComponentDescriptor owningComponent();

    /**
     * The contribution bindings for {@link #bindingKey()} that were resolved in {@link
     * #owningComponent()} or its ancestor components, indexed by the component in which the binding
     * was resolved. If {@link #bindingKey()}'s kind is not {@link BindingKey.Kind#CONTRIBUTION}, this
     * is empty.
     */
    abstract ImmutableSetMultimap<ComponentDescriptor, ContributionBinding> allContributionBindings();

    /**
     * The members-injection bindings for {@link #bindingKey()} that were resolved in {@link
     * #owningComponent()} or its ancestor components, indexed by the component in which the binding
     * was resolved. If {@link #bindingKey()}'s kind is not {@link BindingKey.Kind#MEMBERS_INJECTION},
     * this is empty.
     */
    abstract ImmutableMap<ComponentDescriptor, MembersInjectionBinding> allMembersInjectionBindings();

    @Override
    public Key key() {
        return bindingKey().key();
    }

    /**
     * The multibinding declarations for {@link #bindingKey()}. If {@link #bindingKey()}'s kind is not
     * {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
     */
    abstract ImmutableSet<MultibindingDeclaration> multibindingDeclarations();

    /**
     * The subcomponent declarations for {@link #bindingKey()}. If {@link #bindingKey()}'s kind is not
     * {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
     */
    abstract ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations();

    /**
     * The optional binding declarations for {@link #bindingKey()}. If {@link #bindingKey()}'s kind is
     * not {@link BindingKey.Kind#CONTRIBUTION}, this is empty.
     */
    abstract ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations();

    /**
     * All bindings for {@link #bindingKey()}, indexed by the component in which the binding was
     * resolved.
     */
    ImmutableSetMultimap<ComponentDescriptor, ? extends Binding> allBindings() {
        switch (bindingKey().kind()) {
        case CONTRIBUTION:
            return allContributionBindings();

        case MEMBERS_INJECTION:
            return allMembersInjectionBindings().asMultimap();

        default:
            throw new AssertionError(bindingKey());
        }
    }

    /**
     * All bindings for {@link #bindingKey()}, regardless of in which component they were resolved.
     */
    ImmutableSet<? extends Binding> bindings() {
        return ImmutableSet.copyOf(allBindings().values());
    }

    /**
     * Returns the single binding.
     *
     * @throws IllegalStateException if there is not exactly one element in {@link #bindings()},
     *     which will never happen for contributions in valid graphs
     */
    Binding binding() {
        return getOnlyElement(bindings());
    }

    /**
     * {@code true} if there are no {@link #bindings()}, {@link #multibindingDeclarations()}, or
     * {@link #subcomponentDeclarations()}.
     */
    boolean isEmpty() {
        return bindings().isEmpty() && multibindingDeclarations().isEmpty() && subcomponentDeclarations().isEmpty();
    }

    /**
     * All bindings for {@link #bindingKey()} that were resolved in {@link #owningComponent()}.
     */
    ImmutableSet<? extends Binding> ownedBindings() {
        switch (bindingKey().kind()) {
        case CONTRIBUTION:
            return ownedContributionBindings();

        case MEMBERS_INJECTION:
            return ownedMembersInjectionBinding().isPresent()
                    ? ImmutableSet.of(ownedMembersInjectionBinding().get())
                    : ImmutableSet.of();

        default:
            throw new AssertionError(bindingKey());
        }
    }

    /**
     * All contribution bindings, regardless of owning component. Empty if this is a members-injection
     * binding.
     */
    ImmutableSet<ContributionBinding> contributionBindings() {
        return ImmutableSet.copyOf(allContributionBindings().values());
    }

    /**
     * The contribution bindings that were resolved in {@link #owningComponent()}. Empty if this is a
     * members-injection binding.
     */
    ImmutableSet<ContributionBinding> ownedContributionBindings() {
        return allContributionBindings().get(owningComponent());
    }

    /** The component that owns {@code binding}. */
    ComponentDescriptor owningComponent(ContributionBinding binding) {
        checkArgument(contributionBindings().contains(binding), "binding is not resolved for %s: %s", bindingKey(),
                binding);
        return getOnlyElement(allContributionBindings().inverse().get(binding));
    }

    /**
     * The members-injection binding, regardless of owning component. Absent if these are contribution
     * bindings, or if there is no members-injection binding because the type fails validation.
     */
    Optional<MembersInjectionBinding> membersInjectionBinding() {
        ImmutableSet<MembersInjectionBinding> membersInjectionBindings = FluentIterable
                .from(allMembersInjectionBindings().values()).toSet();
        return membersInjectionBindings.isEmpty() ? Optional.empty()
                : Optional.of(Iterables.getOnlyElement(membersInjectionBindings));
    }

    /**
     * The members-injection binding that was resolved in {@link #owningComponent()}. Empty if these
     * are contribution bindings.
     */
    Optional<MembersInjectionBinding> ownedMembersInjectionBinding() {
        return Optional.ofNullable(allMembersInjectionBindings().get(owningComponent()));
    }

    /** Creates a {@link ResolvedBindings} for contribution bindings. */
    static ResolvedBindings forContributionBindings(BindingKey bindingKey, ComponentDescriptor owningComponent,
            Multimap<ComponentDescriptor, ? extends ContributionBinding> contributionBindings,
            Iterable<MultibindingDeclaration> multibindings,
            Iterable<SubcomponentDeclaration> subcomponentDeclarations,
            Iterable<OptionalBindingDeclaration> optionalBindingDeclarations) {
        checkArgument(bindingKey.kind().equals(BindingKey.Kind.CONTRIBUTION));
        return new AutoValue_ResolvedBindings(bindingKey, owningComponent,
                ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>copyOf(contributionBindings),
                ImmutableMap.<ComponentDescriptor, MembersInjectionBinding>of(), ImmutableSet.copyOf(multibindings),
                ImmutableSet.copyOf(subcomponentDeclarations), ImmutableSet.copyOf(optionalBindingDeclarations));
    }

    /**
     * Creates a {@link ResolvedBindings} for members injection bindings.
     */
    static ResolvedBindings forMembersInjectionBinding(BindingKey bindingKey, ComponentDescriptor owningComponent,
            MembersInjectionBinding ownedMembersInjectionBinding) {
        checkArgument(bindingKey.kind().equals(BindingKey.Kind.MEMBERS_INJECTION));
        return new AutoValue_ResolvedBindings(bindingKey, owningComponent,
                ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>of(),
                ImmutableMap.of(owningComponent, ownedMembersInjectionBinding),
                ImmutableSet.<MultibindingDeclaration>of(), ImmutableSet.<SubcomponentDeclaration>of(),
                ImmutableSet.<OptionalBindingDeclaration>of());
    }

    /**
     * Creates a {@link ResolvedBindings} appropriate for when there are no bindings for the key.
     */
    static ResolvedBindings noBindings(BindingKey bindingKey, ComponentDescriptor owningComponent) {
        return new AutoValue_ResolvedBindings(bindingKey, owningComponent,
                ImmutableSetMultimap.<ComponentDescriptor, ContributionBinding>of(),
                ImmutableMap.<ComponentDescriptor, MembersInjectionBinding>of(),
                ImmutableSet.<MultibindingDeclaration>of(), ImmutableSet.<SubcomponentDeclaration>of(),
                ImmutableSet.<OptionalBindingDeclaration>of());
    }

    /**
     * Returns a {@code ResolvedBindings} with the same {@link #bindingKey()} and {@link #bindings()}
     * as this one, but no {@link #ownedBindings()}.
     */
    ResolvedBindings asInheritedIn(ComponentDescriptor owningComponent) {
        return new AutoValue_ResolvedBindings(bindingKey(), owningComponent, allContributionBindings(),
                allMembersInjectionBindings(), multibindingDeclarations(), subcomponentDeclarations(),
                optionalBindingDeclarations());
    }

    /**
     * {@code true} if this is a multibinding contribution.
     */
    boolean isMultibindingContribution() {
        return contributionBindings().size() == 1 && contributionBinding().contributionType().isMultibinding();
    }

    /**
     * Returns the single contribution binding.
     *
     * @throws IllegalStateException if there is not exactly one element in
     *     {@link #contributionBindings()}, which will never happen for contributions in valid graphs
     */
    ContributionBinding contributionBinding() {
        return getOnlyElement(contributionBindings());
    }

    /**
     * The binding type for these bindings. If there are {@link #multibindingDeclarations()} or {@link
     * #subcomponentDeclarations()} but no {@link #bindings()}, returns {@link BindingType#PROVISION}.
     *
     * @throws IllegalStateException if {@link #isEmpty()} or the binding types conflict
     */
    @Override
    public BindingType bindingType() {
        checkState(!isEmpty(), "empty bindings for %s", bindingKey());
        if (bindings().isEmpty()
                && (!multibindingDeclarations().isEmpty() || !subcomponentDeclarations().isEmpty())) {
            // Only multibinding declarations, so assume provision.
            return BindingType.PROVISION;
        }
        ImmutableSet<BindingType> bindingTypes = bindingTypes();
        checkState(bindingTypes.size() == 1, "conflicting binding types: %s", bindings());
        return getOnlyElement(bindingTypes);
    }

    /** The binding types for {@link #bindings()}. */
    ImmutableSet<BindingType> bindingTypes() {
        return FluentIterable.from(bindings()).transform(HasBindingType::bindingType).toSet();
    }

    /**
     * The contribution type for these bindings.
     *
     * @throws IllegalStateException if there is not exactly one element in {@link
     *     #contributionBindings()}, which will never happen for contributions in valid graphs
     */
    @Override
    public ContributionType contributionType() {
        return contributionBinding().contributionType();
    }

    /**
     * The name of the package in which these bindings must be managed, for
     * example if a binding references non-public types.
     * 
     * @throws IllegalArgumentException if the bindings must be managed in more than one package
     */
    Optional<String> bindingPackage() {
        Set<String> bindingPackages = bindings().stream().map(Binding::bindingPackage).filter(Optional::isPresent)
                .map(Optional::get).collect(toSet());
        checkArgument(bindingPackages.size() <= 1);
        return bindingPackages.stream().findFirst();
    }

    /**
     * The framework class associated with these bindings.
     */
    Class<?> frameworkClass() {
        return bindingType().frameworkClass();
    }

    /**
     * The scope associated with the single binding.
     *
     * @throws IllegalStateException if {@link #bindings()} does not have exactly one element
     */
    Optional<Scope> scope() {
        return getOnlyElement(bindings()).scope();
    }
}