/* * 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.functional.subcomponent; import dagger.Binds; import dagger.Component; import dagger.Module; import dagger.Provides; import dagger.Subcomponent; import dagger.multibindings.IntoMap; import dagger.multibindings.IntoSet; import dagger.multibindings.StringKey; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.inject.Inject; final class MultibindingSubcomponents { /** Multibindings for this type are bound only in the parent component. */ enum BoundInParent { INSTANCE; } /** Multibindings for this type are bound only in the child component. */ enum BoundInChild { INSTANCE; } /** Multibindings for this type are bound in the parent component and the child component. */ enum BoundInParentAndChild { IN_PARENT, IN_CHILD; } static final class RequiresMultibindings { private final Set set; private final Map map; @Inject RequiresMultibindings(Set set, Map map) { this.set = set; this.map = map; } Set set() { return set; } Map map() { return map; } @Override public boolean equals(Object obj) { return obj instanceof RequiresMultibindings && set.equals(((RequiresMultibindings) obj).set) && map.equals(((RequiresMultibindings) obj).map); } @Override public int hashCode() { return Objects.hash(set, map); } @Override public String toString() { return String.format( "%s{set=%s, map=%s}", RequiresMultibindings.class.getSimpleName(), set, map); } } @Module abstract static class ParentMultibindingModule { @Provides @IntoSet static BoundInParent onlyInParentElement() { return BoundInParent.INSTANCE; } @Provides @IntoMap @StringKey("parent key") static BoundInParent onlyInParentEntry() { return BoundInParent.INSTANCE; } @Provides @IntoSet static BoundInParentAndChild inParentAndChildElement() { return BoundInParentAndChild.IN_PARENT; } @Provides @IntoMap @StringKey("parent key") static BoundInParentAndChild inParentAndChildEntry() { return BoundInParentAndChild.IN_PARENT; } /* This is not static because otherwise we have no tests that cover the case where a * subcomponent uses a module instance installed onto a parent component. */ @Binds @IntoSet abstract RequiresMultibindings requiresMultibindingsInParentAndChildElement( RequiresMultibindings requiresMultibindingsInParentAndChild); } @Module static final class ChildMultibindingModule { @Provides @IntoSet static BoundInParentAndChild inParentAndChildElement() { return BoundInParentAndChild.IN_CHILD; } @Provides @IntoMap @StringKey("child key") static BoundInParentAndChild inParentAndChildEntry() { return BoundInParentAndChild.IN_CHILD; } @Provides @IntoSet static BoundInChild onlyInChildElement() { return BoundInChild.INSTANCE; } @Provides @IntoMap @StringKey("child key") static BoundInChild onlyInChildEntry() { return BoundInChild.INSTANCE; } } @Module abstract static class ChildMultibindingModuleWithOnlyBindsMultibindings { @Provides static BoundInParentAndChild provideBoundInParentAndChildForBinds() { return BoundInParentAndChild.IN_CHILD; } @Binds @IntoSet abstract BoundInParentAndChild bindsLocalContribution(BoundInParentAndChild instance); @Binds @IntoMap @StringKey("child key") abstract BoundInParentAndChild inParentAndChildEntry(BoundInParentAndChild instance); @Provides static BoundInChild provideBoundInChildForBinds() { return BoundInChild.INSTANCE; } @Binds @IntoSet abstract BoundInChild inChild(BoundInChild instance); @Binds @IntoMap @StringKey("child key") abstract BoundInChild inChildEntry(BoundInChild instance); } interface ProvidesBoundInParent { RequiresMultibindings requiresMultibindingsBoundInParent(); } interface ProvidesBoundInChild { RequiresMultibindings requiresMultibindingsBoundInChild(); } interface ProvidesBoundInParentAndChild { RequiresMultibindings requiresMultibindingsBoundInParentAndChild(); } interface ProvidesSetOfRequiresMultibindings { Set> setOfRequiresMultibindingsInParentAndChild(); } interface ParentWithProvision extends ProvidesBoundInParent, ProvidesBoundInParentAndChild, ProvidesSetOfRequiresMultibindings {} interface HasChildWithProvision { ChildWithProvision childWithProvision(); } interface HasChildWithoutProvision { ChildWithoutProvision childWithoutProvision(); } @Component(modules = ParentMultibindingModule.class) interface ParentWithoutProvisionHasChildWithoutProvision extends HasChildWithoutProvision {} @Component(modules = ParentMultibindingModule.class) interface ParentWithoutProvisionHasChildWithProvision extends HasChildWithProvision {} @Component(modules = ParentMultibindingModule.class) interface ParentWithProvisionHasChildWithoutProvision extends ParentWithProvision, HasChildWithoutProvision {} @Component(modules = ParentMultibindingModule.class) interface ParentWithProvisionHasChildWithProvision extends ParentWithProvision, HasChildWithProvision {} @Subcomponent(modules = ChildMultibindingModule.class) interface ChildWithoutProvision { Grandchild grandchild(); } @Subcomponent(modules = ChildMultibindingModule.class) interface ChildWithProvision extends ProvidesBoundInParent, ProvidesBoundInParentAndChild, ProvidesBoundInChild, ProvidesSetOfRequiresMultibindings { Grandchild grandchild(); } @Subcomponent interface Grandchild extends ProvidesBoundInParent, ProvidesBoundInParentAndChild, ProvidesBoundInChild, ProvidesSetOfRequiresMultibindings {} @Component(modules = ParentMultibindingModule.class) interface ParentWithProvisionHasChildWithBinds extends ParentWithProvision { ChildWithBinds childWithBinds(); } @Subcomponent(modules = ChildMultibindingModuleWithOnlyBindsMultibindings.class) interface ChildWithBinds extends ChildWithProvision {} }