By Guest

Unnamed Paste

Auto Detect 2.99 KiB 32 one month ago
class Injector { // List of pre-constructed dependencies to inject private val dependencies = IdentityHashMap<KClass<*>, Either<Any, MutableList<Any>>>() private fun addDependency(type: KClass<*>, classOrInstance: Either<Any, MutableList<Any>>) { if (dependencies.containsKey(type)) throw IllegalArgumentException("Dependency of class '${type.simpleName}' already exists") dependencies[type] = classOrInstance } /** * Add an instance as a dependency, this will not be injected into but it can be depended by others */ fun addInstance(instance: Any): Injector { addDependency(instance::class, Either.Left(instance)) return this } fun add(type: KClass<*>, vararg arguments: Any): Injector { addDependency(type, Either.Right(mutableListOf(*arguments))) return this } inline fun <reified T> add(vararg arguments: Any): Injector { add(T::class, *arguments) return this } fun build() { // Use a foreach instead of a filter because things will be moved dependencies.forEach { if (it.value.isRight()) construct(it.key) } } private fun construct(type: KClass<*>): Any { checkCyclic(type, type) // Check cyclic constructors recursively val constructValues = mutableListOf<Any>() type.primaryConstructor!!.parameters .forEach { it -> val paramType = it.type.classifier as KClass<*> if (paramType.hasAnnotation<Dependency>()) { dependencies[paramType]!!.fold( { constructValues.add(it) }, { constructValues.add(construct(paramType)) } // Construct the argument ) } else { val passedValue = dependencies[paramType]!!.orNull()!!.remove(0) if (passedValue::class != paramType) throw IllegalArgumentException("Construct error on '${type::class.simpleName}', " + "argument type '${paramType.simpleName}' did not match '${passedValue::class.simpleName}'") } } val newValue = type.primaryConstructor!!.call(*constructValues.toTypedArray()) dependencies[type] = Either.Left(newValue) return newValue } /** * Check for cyclic dependency */ private fun checkCyclic(dontFind: KClass<*>, type: KClass<*>) { type.primaryConstructor!!.parameters .filter { it.hasAnnotation<Dependency>() } .forEach { val paramType = it.type.classifier as KClass<*> if (paramType == dontFind) throw IllegalStateException("Cyclic dependency detected between '${dontFind.simpleName}' and '${type.simpleName}'") checkCyclic(dontFind, it.type.classifier as KClass<*>) } } }