Implement injection of Iterable<T> and some improvements in generic handling in general
This commit is contained in:
committed by
Pavel V. Talanov
parent
0c41f4f736
commit
07139879cd
@@ -17,9 +17,7 @@
|
||||
package org.jetbrains.kotlin.container
|
||||
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import java.lang.reflect.Constructor
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Modifier
|
||||
import java.lang.reflect.*
|
||||
import java.util.ArrayList
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
@@ -44,17 +42,17 @@ fun Class<*>.getInfo(): ClassInfo {
|
||||
data class ClassInfo(
|
||||
val constructorInfo: ConstructorInfo?,
|
||||
val setterInfos: List<SetterInfo>,
|
||||
val registrations: List<Class<*>>
|
||||
val registrations: List<Type>
|
||||
)
|
||||
|
||||
data class ConstructorInfo(
|
||||
val constructor: Constructor<*>,
|
||||
val parameters: List<Class<*>>
|
||||
val parameters: List<Type>
|
||||
)
|
||||
|
||||
data class SetterInfo(
|
||||
val method: Method,
|
||||
val parameters: List<Class<*>>
|
||||
val parameters: List<Type>
|
||||
)
|
||||
|
||||
private fun traverseClass(c: Class<*>): ClassInfo {
|
||||
@@ -66,7 +64,7 @@ private fun getSetterInfos(c: Class<*>): List<SetterInfo> {
|
||||
for (method in c.getMethods()) {
|
||||
for (annotation in method.getDeclaredAnnotations()) {
|
||||
if (annotation.annotationType().getName().endsWith(".Inject")) {
|
||||
setterInfos.add(SetterInfo(method, method.getParameterTypes().toList()))
|
||||
setterInfos.add(SetterInfo(method, method.getGenericParameterTypes().toList()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,25 +81,37 @@ private fun getConstructorInfo(c: Class<*>): ConstructorInfo? {
|
||||
return null
|
||||
|
||||
val constructor = constructors.single()
|
||||
return ConstructorInfo(constructor, constructor.getParameterTypes().toList())
|
||||
return ConstructorInfo(constructor, constructor.getGenericParameterTypes().toList())
|
||||
}
|
||||
|
||||
|
||||
private fun collectInterfacesRecursive(cl: Class<*>, result: MutableSet<Class<*>>) {
|
||||
cl.getInterfaces().forEach {
|
||||
private fun collectInterfacesRecursive(type: Type, result: MutableSet<Type>) {
|
||||
// TODO: should apply generic substitution through hierarchy
|
||||
val klass : Class<*>? = when(type) {
|
||||
is Class<*> -> type
|
||||
is ParameterizedType -> type.getRawType() as? Class<*>
|
||||
else -> null
|
||||
}
|
||||
klass?.getGenericInterfaces()?.forEach {
|
||||
if (result.add(it)) {
|
||||
collectInterfacesRecursive(it, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRegistrations(klass: Class<*>): List<Class<*>> {
|
||||
val registrations = ArrayList<Class<*>>()
|
||||
private fun getRegistrations(klass: Class<*>): List<Type> {
|
||||
val registrations = ArrayList<Type>()
|
||||
|
||||
val superClasses = sequence(klass) { (it as Class<Any>).getSuperclass() }
|
||||
val superClasses = sequence<Type>(klass) {
|
||||
when (it) {
|
||||
is Class<*> -> it.getGenericSuperclass()
|
||||
is ParameterizedType -> (it.getRawType() as? Class<*>)?.getGenericSuperclass()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
registrations.addAll(superClasses)
|
||||
|
||||
val interfaces = LinkedHashSet<Class<*>>()
|
||||
val interfaces = LinkedHashSet<Type>()
|
||||
superClasses.forEach { collectInterfacesRecursive(it, interfaces) }
|
||||
registrations.addAll(interfaces)
|
||||
registrations.remove(javaClass<Any>())
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.container
|
||||
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.*
|
||||
|
||||
public class InstanceComponentDescriptor(val instance: Any) : ComponentDescriptor {
|
||||
|
||||
override fun getValue(): Any = instance
|
||||
override fun getRegistrations(): Iterable<Class<*>> = instance.javaClass.getInfo().registrations
|
||||
override fun getRegistrations(): Iterable<Type> = instance.javaClass.getInfo().registrations
|
||||
|
||||
override fun getDependencies(context: ValueResolveContext): Collection<Class<*>> = emptyList()
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package org.jetbrains.kotlin.container
|
||||
|
||||
import java.io.Closeable
|
||||
import java.lang.reflect.Modifier
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import java.lang.reflect.WildcardType
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class ContainerConsistencyException(message: String) : Exception(message)
|
||||
@@ -49,20 +51,22 @@ public class StorageComponentContainer(id: String) : ComponentContainer, Closeab
|
||||
|
||||
override fun close() = componentStorage.dispose()
|
||||
|
||||
jvmOverloads public fun resolve(request: Class<*>, context: ValueResolveContext = unknownContext): ValueDescriptor? {
|
||||
val storageResolve = componentStorage.resolve(request, context)
|
||||
if (storageResolve != null)
|
||||
return storageResolve
|
||||
jvmOverloads public fun resolve(request: Type, context: ValueResolveContext = unknownContext): ValueDescriptor? {
|
||||
return componentStorage.resolve(request, context) ?: resolveIterable(request, context)
|
||||
}
|
||||
|
||||
val hasSinglePublicConstructor = request.getConstructors().singleOrNull()?.let { Modifier.isPublic(it.getModifiers()) } ?: false
|
||||
if (!hasSinglePublicConstructor)
|
||||
return null
|
||||
|
||||
val modifiers = request.getModifiers()
|
||||
if (Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers) || request.isPrimitive())
|
||||
return null
|
||||
|
||||
return SingletonTypeComponentDescriptor(this, request)
|
||||
private fun resolveIterable(request: Type, context: ValueResolveContext): ValueDescriptor? {
|
||||
if (request !is ParameterizedType) return null
|
||||
val rawType = request.getRawType()
|
||||
if (rawType != javaClass<Iterable<*>>()) return null
|
||||
val typeArguments = request.getActualTypeArguments()
|
||||
if (typeArguments.size() != 1) return null
|
||||
val iterableTypeArgument = typeArguments[0]
|
||||
if (!(iterableTypeArgument is WildcardType)) return null
|
||||
val upperBounds = iterableTypeArgument.getUpperBounds()
|
||||
if (upperBounds.size() != 1) return null
|
||||
val iterableType = upperBounds[0]
|
||||
return IterableDescriptor(componentStorage.resolveMultiple(iterableType, context))
|
||||
}
|
||||
|
||||
public fun resolveMultiple(request: Class<*>, context: ValueResolveContext = unknownContext): Iterable<ValueDescriptor> {
|
||||
|
||||
@@ -16,14 +16,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.container
|
||||
|
||||
import java.lang.reflect.*
|
||||
|
||||
public interface ValueDescriptor {
|
||||
public fun getValue(): Any
|
||||
}
|
||||
|
||||
internal interface ComponentDescriptor : ValueDescriptor {
|
||||
fun getRegistrations(): Iterable<Class<*>>
|
||||
fun getDependencies(context: ValueResolveContext): Collection<Class<*>>
|
||||
fun getRegistrations(): Iterable<Type>
|
||||
fun getDependencies(context: ValueResolveContext): Collection<Type>
|
||||
val shouldInjectProperties: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
public class IterableDescriptor(val descriptors: Iterable<ValueDescriptor>) : ValueDescriptor {
|
||||
override fun getValue(): Any {
|
||||
return descriptors.map { it.getValue() }
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,11 @@ package org.jetbrains.kotlin.container
|
||||
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import java.util.ArrayList
|
||||
import java.lang.reflect.*
|
||||
|
||||
internal class ComponentRegistry {
|
||||
fun buildRegistrationMap(descriptors: Collection<ComponentDescriptor>): MultiMap<Class<*>, ComponentDescriptor> {
|
||||
val registrationMap = MultiMap<Class<*>, ComponentDescriptor>()
|
||||
fun buildRegistrationMap(descriptors: Collection<ComponentDescriptor>): MultiMap<Type, ComponentDescriptor> {
|
||||
val registrationMap = MultiMap<Type, ComponentDescriptor>()
|
||||
for (descriptor in descriptors) {
|
||||
for (registration in descriptor.getRegistrations()) {
|
||||
registrationMap.putValue(registration, descriptor)
|
||||
@@ -30,13 +31,13 @@ internal class ComponentRegistry {
|
||||
return registrationMap
|
||||
}
|
||||
|
||||
private var registrationMap = MultiMap.createLinkedSet<Class<*>, ComponentDescriptor>()
|
||||
private var registrationMap = MultiMap.createLinkedSet<Type, ComponentDescriptor>()
|
||||
|
||||
public fun addAll(descriptors: Collection<ComponentDescriptor>) {
|
||||
registrationMap.putAllValues(buildRegistrationMap(descriptors))
|
||||
}
|
||||
|
||||
public fun tryGetEntry(request: Class<*>): Collection<ComponentDescriptor> {
|
||||
public fun tryGetEntry(request: Type): Collection<ComponentDescriptor> {
|
||||
return registrationMap.get(request)
|
||||
}
|
||||
}
|
||||
@@ -23,15 +23,15 @@ import java.lang.reflect.Type
|
||||
import java.util.ArrayList
|
||||
|
||||
public interface ValueResolver {
|
||||
fun resolve(request: Class<*>, context: ValueResolveContext): ValueDescriptor?
|
||||
fun resolve(request: Type, context: ValueResolveContext): ValueDescriptor?
|
||||
}
|
||||
|
||||
public interface ValueResolveContext {
|
||||
fun resolve(registration: Class<*>): ValueDescriptor?
|
||||
fun resolve(registration: Type): ValueDescriptor?
|
||||
}
|
||||
|
||||
internal class ComponentResolveContext(val container: StorageComponentContainer, val requestingDescriptor: ValueDescriptor) : ValueResolveContext {
|
||||
override fun resolve(registration: Class<*>): ValueDescriptor? = container.resolve(registration, this)
|
||||
override fun resolve(registration: Type): ValueDescriptor? = container.resolve(registration, this)
|
||||
|
||||
override fun toString(): String = "for $requestingDescriptor in $container"
|
||||
}
|
||||
@@ -54,10 +54,10 @@ fun Class<*>.bindToConstructor(context: ValueResolveContext): ConstructorBinding
|
||||
}
|
||||
|
||||
fun Method.bindToMethod(context: ValueResolveContext): MethodBinding {
|
||||
return MethodBinding(this, bindArguments(getParameterTypes().toList(), context))
|
||||
return MethodBinding(this, bindArguments(getGenericParameterTypes().toList(), context))
|
||||
}
|
||||
|
||||
private fun Member.bindArguments(parameters: List<Class<*>>, context: ValueResolveContext): List<ValueDescriptor> {
|
||||
private fun Member.bindArguments(parameters: List<Type>, context: ValueResolveContext): List<ValueDescriptor> {
|
||||
val bound = ArrayList<ValueDescriptor>(parameters.size())
|
||||
var unsatisfied: MutableList<Type>? = null
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.container
|
||||
import java.io.Closeable
|
||||
import java.util.ArrayList
|
||||
import kotlin.properties.Delegates
|
||||
import java.lang.reflect.*
|
||||
|
||||
enum class ComponentState {
|
||||
Null,
|
||||
@@ -116,7 +117,7 @@ public abstract class SingletonDescriptor(val container: ComponentContainer) : C
|
||||
}
|
||||
|
||||
public abstract class SingletonComponentDescriptor(container: ComponentContainer, val klass: Class<*>) : SingletonDescriptor(container) {
|
||||
public override fun getRegistrations(): Iterable<Class<*>> = klass.getInfo().registrations
|
||||
public override fun getRegistrations(): Iterable<Type> = klass.getInfo().registrations
|
||||
}
|
||||
|
||||
public class SingletonTypeComponentDescriptor(container: ComponentContainer, klass: Class<*>) : SingletonComponentDescriptor(container, klass) {
|
||||
@@ -139,7 +140,7 @@ public class SingletonTypeComponentDescriptor(container: ComponentContainer, kla
|
||||
return instance
|
||||
}
|
||||
|
||||
override fun getDependencies(context: ValueResolveContext): Collection<Class<*>> {
|
||||
override fun getDependencies(context: ValueResolveContext): Collection<Type> {
|
||||
val classInfo = klass.getInfo()
|
||||
return classInfo.constructorInfo?.parameters.orEmpty() + classInfo.setterInfos.flatMap { it.parameters }
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.jetbrains.kotlin.container
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import java.io.Closeable
|
||||
import java.lang.reflect.Modifier
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
import java.util.ArrayList
|
||||
import java.util.HashSet
|
||||
import java.util.LinkedHashSet
|
||||
@@ -30,13 +32,15 @@ public enum class ComponentStorageState {
|
||||
Disposed
|
||||
}
|
||||
|
||||
class InvalidCardinalityException(message: String, val descriptors: Collection<ComponentDescriptor>) : Exception(message)
|
||||
|
||||
public class ComponentStorage(val myId: String) : ValueResolver {
|
||||
var state = ComponentStorageState.Initial
|
||||
val registry = ComponentRegistry()
|
||||
val descriptors = LinkedHashSet<ComponentDescriptor>()
|
||||
val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Class<*>>()
|
||||
val dependencies = MultiMap.createLinkedSet<ComponentDescriptor, Type>()
|
||||
|
||||
override fun resolve(request: Class<*>, context: ValueResolveContext): ValueDescriptor? {
|
||||
override fun resolve(request: Type, context: ValueResolveContext): ValueDescriptor? {
|
||||
if (state == ComponentStorageState.Initial)
|
||||
throw ContainerConsistencyException("Container was not composed before resolving")
|
||||
|
||||
@@ -44,12 +48,14 @@ public class ComponentStorage(val myId: String) : ValueResolver {
|
||||
if (entry.isNotEmpty()) {
|
||||
registerDependency(request, context)
|
||||
|
||||
if (entry.size() > 1)
|
||||
throw InvalidCardinalityException("Request $request cannot be satisfied because there is more than one type registered", entry)
|
||||
return entry.singleOrNull()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun registerDependency(request: Class<*>, context: ValueResolveContext) {
|
||||
private fun registerDependency(request: Type, context: ValueResolveContext) {
|
||||
if (context is ComponentResolveContext) {
|
||||
val descriptor = context.requestingDescriptor
|
||||
if (descriptor is ComponentDescriptor) {
|
||||
@@ -58,7 +64,7 @@ public class ComponentStorage(val myId: String) : ValueResolver {
|
||||
}
|
||||
}
|
||||
|
||||
public fun resolveMultiple(request: Class<*>, context: ValueResolveContext): Iterable<ValueDescriptor> {
|
||||
public fun resolveMultiple(request: Type, context: ValueResolveContext): Iterable<ValueDescriptor> {
|
||||
registerDependency(request, context)
|
||||
return registry.tryGetEntry(request)
|
||||
}
|
||||
@@ -89,7 +95,7 @@ public class ComponentStorage(val myId: String) : ValueResolver {
|
||||
|
||||
registry.addAll(descriptors)
|
||||
|
||||
val implicits = inspectDependenciesAndRegisterImplicits(context, descriptors)
|
||||
val implicits = inspectDependenciesAndRegisterAdhoc(context, descriptors)
|
||||
|
||||
injectProperties(context, descriptors + implicits)
|
||||
}
|
||||
@@ -102,30 +108,38 @@ public class ComponentStorage(val myId: String) : ValueResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private fun inspectDependenciesAndRegisterImplicits(context: ComponentResolveContext, descriptors: Collection<ComponentDescriptor>): LinkedHashSet<ComponentDescriptor> {
|
||||
val implicits = LinkedHashSet<ComponentDescriptor>()
|
||||
val visitedTypes = HashSet<Class<*>>()
|
||||
private fun inspectDependenciesAndRegisterAdhoc(context: ComponentResolveContext, descriptors: Collection<ComponentDescriptor>): LinkedHashSet<ComponentDescriptor> {
|
||||
val adhoc = LinkedHashSet<ComponentDescriptor>()
|
||||
val visitedTypes = HashSet<Type>()
|
||||
for (descriptor in descriptors) {
|
||||
registerImplicits(context, descriptor, visitedTypes, implicits)
|
||||
collectAdhocComponents(context, descriptor, visitedTypes, adhoc)
|
||||
}
|
||||
registry.addAll(implicits)
|
||||
return implicits
|
||||
registry.addAll(adhoc)
|
||||
return adhoc
|
||||
}
|
||||
|
||||
private fun registerImplicits(
|
||||
context: ComponentResolveContext, descriptor: ComponentDescriptor,
|
||||
visitedTypes: HashSet<Class<*>>, implicitDescriptors: LinkedHashSet<ComponentDescriptor>
|
||||
private fun collectAdhocComponents(context: ComponentResolveContext, descriptor: ComponentDescriptor,
|
||||
visitedTypes: HashSet<Type>, adhocDescriptors: LinkedHashSet<ComponentDescriptor>
|
||||
) {
|
||||
val dependencies = descriptor.getDependencies(context)
|
||||
for (type in dependencies) {
|
||||
if (!visitedTypes.add(type)) continue
|
||||
if (!visitedTypes.add(type))
|
||||
continue
|
||||
|
||||
val entry = registry.tryGetEntry(type)
|
||||
if (entry.isEmpty()) {
|
||||
if (!Modifier.isAbstract(type.getModifiers()) && !type.isPrimitive()) {
|
||||
val implicitDescriptor = SingletonTypeComponentDescriptor(context.container, type)
|
||||
implicitDescriptors.add(implicitDescriptor)
|
||||
registerImplicits(context, implicitDescriptor, visitedTypes, implicitDescriptors)
|
||||
val rawType : Class<*>? = when(type){
|
||||
is Class<*> -> type
|
||||
is ParameterizedType -> type.getRawType() as? Class<*>
|
||||
else -> null
|
||||
}
|
||||
if (rawType == null)
|
||||
continue
|
||||
|
||||
if (!Modifier.isAbstract(rawType.getModifiers()) && !rawType.isPrimitive()) {
|
||||
val implicitDescriptor = SingletonTypeComponentDescriptor(context.container, rawType)
|
||||
adhocDescriptors.add(implicitDescriptor)
|
||||
collectAdhocComponents(context, implicitDescriptor, visitedTypes, adhocDescriptors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+71
-43
@@ -17,53 +17,11 @@
|
||||
package org.jetbrains.kotlin.container.tests
|
||||
|
||||
import org.jetbrains.kotlin.container.*
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.Closeable
|
||||
import javax.inject.Inject
|
||||
import kotlin.test.*
|
||||
|
||||
interface TestComponentInterface {
|
||||
public val disposed: Boolean
|
||||
fun foo()
|
||||
}
|
||||
|
||||
interface TestClientComponentInterface
|
||||
|
||||
class TestComponent : TestComponentInterface, Closeable {
|
||||
public override var disposed: Boolean = false
|
||||
override fun close() {
|
||||
disposed = true
|
||||
}
|
||||
|
||||
override fun foo() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
class ManualTestComponent(val name: String) : TestComponentInterface, Closeable {
|
||||
public override var disposed: Boolean = false
|
||||
override fun close() {
|
||||
disposed = true
|
||||
}
|
||||
|
||||
override fun foo() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
class TestClientComponent(val dep: TestComponentInterface) : TestClientComponentInterface, Closeable {
|
||||
override fun close() {
|
||||
if (dep.disposed)
|
||||
throw Exception("Dependency shouldn't be disposed before dependee")
|
||||
disposed = true
|
||||
}
|
||||
|
||||
var disposed: Boolean = false
|
||||
}
|
||||
|
||||
class TestClientComponent2() : TestClientComponentInterface {
|
||||
}
|
||||
|
||||
class ComponentContainerTest {
|
||||
Test fun should_throw_when_not_composed() {
|
||||
val container = StorageComponentContainer("test")
|
||||
@@ -155,6 +113,76 @@ class ComponentContainerTest {
|
||||
}
|
||||
}
|
||||
|
||||
Test fun should_resolve_adhoc_types_to_same_instances() {
|
||||
createContainer("test") {
|
||||
useImpl<TestAdhocComponent1>()
|
||||
useImpl<TestAdhocComponent2>()
|
||||
}.use {
|
||||
val descriptor1 = it.resolve<TestAdhocComponent1>()
|
||||
assertNotNull(descriptor1)
|
||||
val descriptor2 = it.resolve<TestAdhocComponent2>()
|
||||
assertNotNull(descriptor2)
|
||||
val component1 = descriptor1!!.getValue() as TestAdhocComponent1
|
||||
val component2 = descriptor2!!.getValue() as TestAdhocComponent2
|
||||
assertTrue(component1.service === component2.service)
|
||||
}
|
||||
}
|
||||
|
||||
Test fun should_resolve_iterable() {
|
||||
createContainer("test") {
|
||||
useImpl<TestComponent>()
|
||||
useImpl<TestClientComponent>()
|
||||
useImpl<TestClientComponent2>()
|
||||
useImpl<TestIterableComponent>()
|
||||
}.use {
|
||||
val descriptor = it.resolve<TestIterableComponent>()
|
||||
assertNotNull(descriptor)
|
||||
val iterableComponent = descriptor!!.getValue() as TestIterableComponent
|
||||
assertEquals(2, iterableComponent.components.count())
|
||||
assertTrue(iterableComponent.components.any { it is TestClientComponent })
|
||||
assertTrue(iterableComponent.components.any { it is TestClientComponent2 })
|
||||
}
|
||||
}
|
||||
|
||||
Test fun should_distinguish_generic() {
|
||||
createContainer("test") {
|
||||
useImpl<TestGenericClient>()
|
||||
useImpl<TestStringComponent>()
|
||||
useImpl<TestIntComponent>()
|
||||
}.use {
|
||||
val descriptor = it.resolve<TestGenericClient>()
|
||||
assertNotNull(descriptor)
|
||||
val genericClient = descriptor!!.getValue() as TestGenericClient
|
||||
assertTrue(genericClient.component1 is TestStringComponent)
|
||||
assertTrue(genericClient.component2 is TestIntComponent)
|
||||
}
|
||||
}
|
||||
|
||||
Ignore("Need generic type substitution")
|
||||
Test fun should_resolve_generic_adhoc() {
|
||||
createContainer("test") {
|
||||
useImpl<TestImplicitGenericClient>()
|
||||
}.use {
|
||||
val descriptor = it.resolve<TestImplicitGenericClient>()
|
||||
assertNotNull(descriptor)
|
||||
val genericClient = descriptor!!.getValue() as TestImplicitGenericClient
|
||||
assertTrue(genericClient.component is TestImplicitGeneric)
|
||||
}
|
||||
}
|
||||
|
||||
Test fun should_fail_with_invalid_cardinality() {
|
||||
createContainer("test") {
|
||||
useImpl<TestComponent>()
|
||||
useInstance(TestComponent())
|
||||
}.use {
|
||||
assertTrue {
|
||||
fails {
|
||||
it.resolve<TestComponent>()
|
||||
} is InvalidCardinalityException
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WithSetters {
|
||||
var isSetterCalled = false
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* 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 org.jetbrains.kotlin.container.tests
|
||||
|
||||
import java.io.*
|
||||
|
||||
interface TestComponentInterface {
|
||||
public val disposed: Boolean
|
||||
fun foo()
|
||||
}
|
||||
|
||||
interface TestClientComponentInterface {
|
||||
}
|
||||
|
||||
class TestComponent : TestComponentInterface, Closeable {
|
||||
public override var disposed: Boolean = false
|
||||
override fun close() {
|
||||
disposed = true
|
||||
}
|
||||
|
||||
override fun foo() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
class ManualTestComponent(val name: String) : TestComponentInterface, Closeable {
|
||||
public override var disposed: Boolean = false
|
||||
override fun close() {
|
||||
disposed = true
|
||||
}
|
||||
|
||||
override fun foo() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
class TestClientComponent(val dep: TestComponentInterface) : TestClientComponentInterface, Closeable {
|
||||
override fun close() {
|
||||
if (dep.disposed)
|
||||
throw Exception("Dependency shouldn't be disposed before dependee")
|
||||
disposed = true
|
||||
}
|
||||
|
||||
var disposed: Boolean = false
|
||||
}
|
||||
|
||||
class TestClientComponent2() : TestClientComponentInterface {
|
||||
}
|
||||
|
||||
class TestAdhocComponentService
|
||||
class TestAdhocComponent1(val service: TestAdhocComponentService) {
|
||||
|
||||
}
|
||||
|
||||
class TestAdhocComponent2(val service: TestAdhocComponentService) {
|
||||
|
||||
}
|
||||
|
||||
class TestIterableComponent(val components: Iterable<TestClientComponentInterface>)
|
||||
|
||||
interface TestGenericComponent<T>
|
||||
|
||||
class TestGenericClient(val component1 : TestGenericComponent<String>, val component2: TestGenericComponent<Int>)
|
||||
class TestStringComponent : TestGenericComponent<String>
|
||||
class TestIntComponent : TestGenericComponent<Int>
|
||||
|
||||
class TestImplicitGeneric<T>()
|
||||
class TestImplicitGenericClient(val component: TestImplicitGeneric<String>)
|
||||
Reference in New Issue
Block a user