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" override fun toString(): String = "Dynamic"
} }
class StorageComponentContainer(id: String) : ComponentContainer, ComponentProvider, Closeable { class StorageComponentContainer(id: String, parent: StorageComponentContainer? = null) : ComponentContainer, ComponentProvider, Closeable {
val unknownContext: ComponentResolveContext by lazy { ComponentResolveContext(this, DynamicComponentDescriptor) } val unknownContext: ComponentResolveContext by lazy {
val componentStorage = ComponentStorage(id) val parentContext = parent?.let { ComponentResolveContext(it, DynamicComponentDescriptor) }
ComponentResolveContext(this, DynamicComponentDescriptor, parentContext)
}
val componentStorage: ComponentStorage = ComponentStorage(id, parent?.componentStorage)
override fun createResolveContext(requestingDescriptor: ValueDescriptor): ValueResolveContext { override fun createResolveContext(requestingDescriptor: ValueDescriptor): ValueResolveContext {
if (requestingDescriptor == DynamicComponentDescriptor) // cache unknown component descriptor if (requestingDescriptor == DynamicComponentDescriptor) // cache unknown component descriptor
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.container
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
fun createContainer(id: String, init: StorageComponentContainer.() -> Unit): StorageComponentContainer { fun createContainer(id: String, parent: StorageComponentContainer? = null, init: StorageComponentContainer.() -> Unit): StorageComponentContainer {
val c = StorageComponentContainer(id) val c = StorageComponentContainer(id, parent)
c.init() c.init()
c.compose() c.compose()
return c return c
@@ -17,8 +17,7 @@
package org.jetbrains.kotlin.container package org.jetbrains.kotlin.container
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import java.util.ArrayList import java.lang.reflect.Type
import java.lang.reflect.*
internal class ComponentRegistry { internal class ComponentRegistry {
fun buildRegistrationMap(descriptors: Collection<ComponentDescriptor>): MultiMap<Type, ComponentDescriptor> { fun buildRegistrationMap(descriptors: Collection<ComponentDescriptor>): MultiMap<Type, ComponentDescriptor> {
@@ -40,4 +39,8 @@ internal class ComponentRegistry {
fun tryGetEntry(request: Type): Collection<ComponentDescriptor> { fun tryGetEntry(request: Type): Collection<ComponentDescriptor> {
return registrationMap.get(request) return registrationMap.get(request)
} }
fun addAll(other: ComponentRegistry) {
registrationMap.putAllValues(other.registrationMap)
}
} }
@@ -30,8 +30,12 @@ interface ValueResolveContext {
fun resolve(registration: Type): ValueDescriptor? fun resolve(registration: Type): ValueDescriptor?
} }
class ComponentResolveContext(val container: StorageComponentContainer, val requestingDescriptor: ValueDescriptor) : ValueResolveContext { class ComponentResolveContext(
override fun resolve(registration: Type): ValueDescriptor? = container.resolve(registration, this) 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" 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) 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 var state = ComponentStorageState.Initial
private val registry = ComponentRegistry() private val registry = ComponentRegistry()
init {
parent?.let { registry.addAll(it.registry) }
}
private val descriptors = LinkedHashSet<ComponentDescriptor>() private val descriptors = LinkedHashSet<ComponentDescriptor>()
private val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Type>() private val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Type>()
@@ -270,4 +270,46 @@ class ComponentContainerTest {
assertTrue(c is C) 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>())
}
} }