1 /* 2 * Copyright (C) 2023 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.functional.kotlinsrc.subcomponent.multibindings 18 19 import dagger.Binds 20 import dagger.Component 21 import dagger.Module 22 import dagger.Provides 23 import dagger.Subcomponent 24 import dagger.multibindings.IntoMap 25 import dagger.multibindings.IntoSet 26 import dagger.multibindings.StringKey 27 import java.util.Objects 28 import javax.inject.Inject 29 30 class MultibindingSubcomponents { 31 /** Multibindings for this type are bound only in the parent component. */ 32 enum class BoundInParent { 33 INSTANCE 34 } 35 36 /** Multibindings for this type are bound only in the child component. */ 37 enum class BoundInChild { 38 INSTANCE 39 } 40 41 /** Multibindings for this type are bound in the parent component and the child component. */ 42 enum class BoundInParentAndChild { 43 IN_PARENT, 44 IN_CHILD 45 } 46 47 class RequiresMultibindings<T> 48 @Inject 49 constructor( 50 val set: Set<@JvmSuppressWildcards T>, 51 val map: Map<String, @JvmSuppressWildcards T> 52 ) { equalsnull53 override fun equals(other: Any?): Boolean = 54 other is RequiresMultibindings<*> && set == other.set && map == other.map 55 56 override fun hashCode(): Int = Objects.hash(set, map) 57 58 override fun toString(): String = 59 "${RequiresMultibindings::class.java.simpleName}{set=$set, map=$map}" 60 } 61 62 @Module 63 internal abstract class ParentMultibindingModule { 64 // This is not static because otherwise we have no tests that cover the case where a 65 // subcomponent uses a module instance installed onto a parent component. 66 @Binds 67 @IntoSet 68 abstract fun requiresMultibindingsInParentAndChildElement( 69 requiresMultibindingsInParentAndChild: RequiresMultibindings<BoundInParentAndChild> 70 ): RequiresMultibindings<BoundInParentAndChild> 71 72 companion object { 73 @Provides @IntoSet fun onlyInParentElement(): BoundInParent = BoundInParent.INSTANCE 74 75 @Provides 76 @IntoMap 77 @StringKey("parent key") 78 fun onlyInParentEntry(): BoundInParent = BoundInParent.INSTANCE 79 80 @Provides 81 @IntoSet 82 fun inParentAndChildElement(): BoundInParentAndChild = BoundInParentAndChild.IN_PARENT 83 84 @Provides 85 @IntoMap 86 @StringKey("parent key") 87 fun inParentAndChildEntry(): BoundInParentAndChild = BoundInParentAndChild.IN_PARENT 88 } 89 } 90 91 @Module 92 internal object ChildMultibindingModule { 93 @Provides 94 @IntoSet inParentAndChildElementnull95 fun inParentAndChildElement(): BoundInParentAndChild = BoundInParentAndChild.IN_CHILD 96 97 @Provides 98 @IntoMap 99 @StringKey("child key") 100 fun inParentAndChildEntry(): BoundInParentAndChild = BoundInParentAndChild.IN_CHILD 101 102 @Provides @IntoSet fun onlyInChildElement(): BoundInChild = BoundInChild.INSTANCE 103 104 @Provides 105 @IntoMap 106 @StringKey("child key") 107 fun onlyInChildEntry(): BoundInChild = BoundInChild.INSTANCE 108 } 109 110 @Module 111 internal abstract class ChildMultibindingModuleWithOnlyBindsMultibindings { 112 @Binds 113 @IntoSet 114 abstract fun bindsLocalContribution(instance: BoundInParentAndChild): BoundInParentAndChild 115 116 @Binds 117 @IntoMap 118 @StringKey("child key") 119 abstract fun inParentAndChildEntry(instance: BoundInParentAndChild): BoundInParentAndChild 120 121 @Binds @IntoSet abstract fun inChild(instance: BoundInChild): BoundInChild 122 123 @Binds 124 @IntoMap 125 @StringKey("child key") 126 abstract fun inChildEntry(instance: BoundInChild): BoundInChild 127 128 companion object { 129 @Provides 130 fun provideBoundInParentAndChildForBinds(): BoundInParentAndChild = 131 BoundInParentAndChild.IN_CHILD 132 133 @Provides fun provideBoundInChildForBinds(): BoundInChild = BoundInChild.INSTANCE 134 } 135 } 136 137 interface ProvidesBoundInParent { requiresMultibindingsBoundInParentnull138 fun requiresMultibindingsBoundInParent(): RequiresMultibindings<BoundInParent> 139 } 140 141 interface ProvidesBoundInChild { 142 fun requiresMultibindingsBoundInChild(): RequiresMultibindings<BoundInChild> 143 } 144 145 interface ProvidesBoundInParentAndChild { requiresMultibindingsBoundInParentAndChildnull146 fun requiresMultibindingsBoundInParentAndChild(): RequiresMultibindings<BoundInParentAndChild> 147 } 148 149 interface ProvidesSetOfRequiresMultibindings { 150 fun setOfRequiresMultibindingsInParentAndChild(): 151 Set<RequiresMultibindings<BoundInParentAndChild>> 152 } 153 154 interface ParentWithProvision : 155 ProvidesBoundInParent, ProvidesBoundInParentAndChild, ProvidesSetOfRequiresMultibindings 156 157 interface HasChildWithProvision { childWithProvisionnull158 fun childWithProvision(): ChildWithProvision 159 } 160 161 interface HasChildWithoutProvision { 162 fun childWithoutProvision(): ChildWithoutProvision 163 } 164 165 @Component(modules = [ParentMultibindingModule::class]) 166 interface ParentWithoutProvisionHasChildWithoutProvision : HasChildWithoutProvision 167 168 @Component(modules = [ParentMultibindingModule::class]) 169 interface ParentWithoutProvisionHasChildWithProvision : HasChildWithProvision 170 171 @Component(modules = [ParentMultibindingModule::class]) 172 interface ParentWithProvisionHasChildWithoutProvision : 173 ParentWithProvision, HasChildWithoutProvision 174 175 @Component(modules = [ParentMultibindingModule::class]) 176 interface ParentWithProvisionHasChildWithProvision : 177 ParentWithProvision, HasChildWithProvision 178 179 @Subcomponent(modules = [ChildMultibindingModule::class]) 180 interface ChildWithoutProvision { grandchildnull181 fun grandchild(): Grandchild 182 } 183 184 @Subcomponent(modules = [ChildMultibindingModule::class]) 185 interface ChildWithProvision : 186 ProvidesBoundInParent, 187 ProvidesBoundInParentAndChild, 188 ProvidesBoundInChild, 189 ProvidesSetOfRequiresMultibindings { 190 fun grandchild(): Grandchild 191 } 192 193 @Subcomponent 194 interface Grandchild : 195 ProvidesBoundInParent, 196 ProvidesBoundInParentAndChild, 197 ProvidesBoundInChild, 198 ProvidesSetOfRequiresMultibindings 199 200 @Component(modules = [ParentMultibindingModule::class]) 201 interface ParentWithProvisionHasChildWithBinds : ParentWithProvision { childWithBindsnull202 fun childWithBinds(): ChildWithBinds 203 } 204 205 @Subcomponent(modules = [ChildMultibindingModuleWithOnlyBindsMultibindings::class]) 206 interface ChildWithBinds : ChildWithProvision 207 } 208