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