StorageComponentContainer: allow to specify parent container which can be reused to provide components to several child containers

This commit is contained in:
Pavel V. Talanov
2016-09-22 16:00:56 +03:00
parent 47bfb8133d
commit be00a6ec49
6 changed files with 66 additions and 10 deletions
@@ -38,9 +38,12 @@ object DynamicComponentDescriptor : ValueDescriptor {
override fun toString(): String = "Dynamic"
}
class StorageComponentContainer(id: String) : ComponentContainer, ComponentProvider, Closeable {
val unknownContext: ComponentResolveContext by lazy { ComponentResolveContext(this, DynamicComponentDescriptor) }
val componentStorage = ComponentStorage(id)
class StorageComponentContainer(id: String, parent: StorageComponentContainer? = null) : ComponentContainer, ComponentProvider, Closeable {
val unknownContext: ComponentResolveContext by lazy {
val parentContext = parent?.let { ComponentResolveContext(it, DynamicComponentDescriptor) }
ComponentResolveContext(this, DynamicComponentDescriptor, parentContext)
}
val componentStorage: ComponentStorage = ComponentStorage(id, parent?.componentStorage)
override fun createResolveContext(requestingDescriptor: ValueDescriptor): ValueResolveContext {
if (requestingDescriptor == DynamicComponentDescriptor) // cache unknown component descriptor
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.container
import kotlin.reflect.KProperty
fun createContainer(id: String, init: StorageComponentContainer.() -> Unit): StorageComponentContainer {
val c = StorageComponentContainer(id)
fun createContainer(id: String, parent: StorageComponentContainer? = null, init: StorageComponentContainer.() -> Unit): StorageComponentContainer {
val c = StorageComponentContainer(id, parent)
c.init()
c.compose()
return c
@@ -17,8 +17,7 @@
package org.jetbrains.kotlin.container
import com.intellij.util.containers.MultiMap
import java.util.ArrayList
import java.lang.reflect.*
import java.lang.reflect.Type
internal class ComponentRegistry {
fun buildRegistrationMap(descriptors: Collection<ComponentDescriptor>): MultiMap<Type, ComponentDescriptor> {
@@ -40,4 +39,8 @@ internal class ComponentRegistry {
fun tryGetEntry(request: Type): Collection<ComponentDescriptor> {
return registrationMap.get(request)
}
fun addAll(other: ComponentRegistry) {
registrationMap.putAllValues(other.registrationMap)
}
}
@@ -30,8 +30,12 @@ interface ValueResolveContext {
fun resolve(registration: Type): ValueDescriptor?
}
class ComponentResolveContext(val container: StorageComponentContainer, val requestingDescriptor: ValueDescriptor) : ValueResolveContext {
override fun resolve(registration: Type): ValueDescriptor? = container.resolve(registration, this)
class ComponentResolveContext(
val container: StorageComponentContainer,
val requestingDescriptor: ValueDescriptor,
val parentContext: ValueResolveContext? = null
) : ValueResolveContext {
override fun resolve(registration: Type): ValueDescriptor? = container.resolve(registration, this) ?: parentContext?.resolve(registration)
override fun toString(): String = "for $requestingDescriptor in $container"
}
@@ -35,9 +35,13 @@ enum class ComponentStorageState {
internal class InvalidCardinalityException(message: String, val descriptors: Collection<ComponentDescriptor>) : Exception(message)
class ComponentStorage(val myId: String) : ValueResolver {
class ComponentStorage(val myId: String, parent: ComponentStorage?) : ValueResolver {
var state = ComponentStorageState.Initial
private val registry = ComponentRegistry()
init {
parent?.let { registry.addAll(it.registry) }
}
private val descriptors = LinkedHashSet<ComponentDescriptor>()
private val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Type>()
@@ -270,4 +270,46 @@ class ComponentContainerTest {
assertTrue(c is C)
}
@Test
fun use_parent_context_to_discover_dependencies() {
class A
class B(val a: A)
val ac = createContainer("a") {
useImpl<A>()
}
val bc = createContainer("b", ac) {
useImpl<B>()
}
val b = bc.get<B>()
val a = ac.get<A>()
assertTrue(b is B)
assertTrue(b.a is A)
assertTrue(a is A)
assertTrue(b.a === a)
}
@Test
fun several_child_dependencies() {
class A
class B(val a: A)
val ac = createContainer("a") {
useImpl<A>()
}
val bc1 = createContainer("b1", ac) {
useImpl<B>()
}
val bc2 = createContainer("b2", ac) {
useImpl<B>()
}
val a = ac.get<A>()
assertTrue(a is A)
assertTrue(a === bc2.get<B>().a)
assertTrue(a === bc1.get<B>().a)
assertTrue(a === bc1.get<A>())
}
}