All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.salomonbrys.kodein.Injector.kt Maven / Gradle / Ivy

package com.github.salomonbrys.kodein

import com.github.salomonbrys.kodein.internal.KodeinContainer
import java.io.Serializable
import java.lang.reflect.Type
import java.util.*
import kotlin.reflect.KProperty

private object UNINITIALIZED_VALUE

abstract class Injected internal constructor(val bind: Kodein.Bind) : Serializable {
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE

    val value: T
        @Suppress("UNCHECKED_CAST")
        get() {
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE)
                return _v1 as T

            return synchronized(this) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE)
                    _v2 as T
                else
                    throw KodeinInjector.UninjectedException()
            }
        }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value

    fun isInjected(): Boolean = _value !== UNINITIALIZED_VALUE

    override fun toString(): String = if (isInjected()) value.toString() else "Uninjected $_type: $bind."

    internal fun _inject(container: KodeinContainer) {
        _value = _getInjection(container)
    }

    protected abstract fun _getInjection(container: KodeinContainer): T
    protected abstract val _type: String
}

class InjectedFactory(private val _key: Kodein.Key) : Injected<(A) -> T>(_key.bind) {
    override fun _getInjection(container: KodeinContainer) = container.nonNullFactory(_key)
    override val _type = "factory"
}

class InjectedNullableFactory(private val _key: Kodein.Key) : Injected<((A) -> T)?>(_key.bind) {
    override fun _getInjection(container: KodeinContainer) = container.factoryOrNull(_key)
    override val _type = "factory"
}

class InjectedProvider(bind: Kodein.Bind) : Injected<() -> T>(bind) {
    override fun _getInjection(container: KodeinContainer) = container.nonNullProvider(bind)
    override val _type = "provider"
}

class InjectedNullableProvider(bind: Kodein.Bind) : Injected<(() -> T)?>(bind) {
    override fun _getInjection(container: KodeinContainer) = container.providerOrNull(bind)
    override val _type = "provider"
}

class InjectedInstance(bind: Kodein.Bind) : Injected(bind) {
    override fun _getInjection(container: KodeinContainer) = container.nonNullProvider(bind).invoke()
    override val _type = "instance"
}

class InjectedNullableInstance(bind: Kodein.Bind) : Injected(bind) {
    override fun _getInjection(container: KodeinContainer) = container.providerOrNull(bind)?.invoke()
    override val _type = "instance"
}


class KodeinInjector() {

    class UninjectedException : RuntimeException("Value has not been injected")

    private val _list = LinkedList>()

    private var _kodein: Kodein? = null

    private var _onInjected: (Kodein) -> Unit = {}

    fun onInjected(cb: (Kodein) -> Unit) { _onInjected = cb }

    fun  _register(injected: Injected) = injected.apply { _list.add(this) }

    inline fun  factory(tag: Any? = null): Injected<(A) -> T> = _register(InjectedFactory(Kodein.Key(Kodein.Bind(typeToken(), tag), typeToken())))
    inline fun  factoryOrNull(tag: Any? = null): Injected<((A) -> T)?> = _register(InjectedNullableFactory(Kodein.Key(Kodein.Bind(typeToken(), tag), typeToken())))

    inline fun  provider(tag: Any? = null): Injected<() -> T> = _register(InjectedProvider(Kodein.Bind(typeToken(), tag)))
    inline fun  providerOrNull(tag: Any? = null): Injected<(() -> T)?> = _register(InjectedNullableProvider(Kodein.Bind(typeToken(), tag)))

    inline fun  instance(tag: Any? = null): Injected = _register(InjectedInstance(Kodein.Bind(typeToken(), tag)))
    inline fun  instanceOrNull(tag: Any? = null): Injected = _register(InjectedNullableInstance(Kodein.Bind(typeToken(), tag)))

    inner class CurriedFactory(val arg: A, val argType: Type) {
        // https://youtrack.jetbrains.com/issue/KT-12126
        fun  _register(injected: Injected) = this@KodeinInjector._register(injected)

        inline fun  provider(tag: Any? = null): Lazy<() -> T> = _register(InjectedFactory(Kodein.Key(Kodein.Bind(typeToken(), tag), argType))).toProvider(arg)
        inline fun  providerOrNull(tag: Any? = null): Lazy<(() -> T)?> = _register(InjectedNullableFactory(Kodein.Key(Kodein.Bind(typeToken(), tag), argType))).toNullableProvider(arg)

        inline fun  instance(tag: Any? = null): Lazy = _register(InjectedFactory(Kodein.Key(Kodein.Bind(typeToken(), tag), argType))).toInstance(arg)
        inline fun  instanceOrNull(tag: Any? = null): Lazy = _register(InjectedNullableFactory(Kodein.Key(Kodein.Bind(typeToken(), tag), argType))).toNullableInstance(arg)
    }

    inline fun  with(arg: A) = CurriedFactory(arg, typeToken())

    inline fun  providerFromFactory(arg: A, tag: Any? = null): Lazy<() -> T> = factory(tag).toProvider(arg)
    inline fun  providerFromFactoryOrNull(arg: A, tag: Any? = null): Lazy<(() -> T)?> = factoryOrNull(tag).toNullableProvider(arg)

    inline fun  instanceFromFactory(arg: A, tag: Any? = null): Lazy = factory(tag).toInstance(arg)
    inline fun  instanceFromFactoryOrNull(arg: A, tag: Any? = null): Lazy = factoryOrNull(tag).toNullableInstance(arg)


    fun kodein(): Lazy = lazy { _kodein ?: throw KodeinInjector.UninjectedException() }

    fun inject(kodein: Kodein) {
        _list.forEach { it._inject(kodein._container) }
        _kodein = kodein
        _onInjected(kodein)
    }
}

fun  Injected<(A) -> T>.toProvider(arg: A): Lazy<() -> T> = lazy { { value(arg) } }
fun  Injected<((A) -> T)?>.toNullableProvider(arg: A): Lazy<(() -> T)?> = lazy {
    val v = value ?: return@lazy null
    return@lazy { v(arg) }
}
fun  Injected<(A) -> T>.toInstance(arg: A): Lazy = lazy { value(arg) }
fun  Injected<((A) -> T)?>.toNullableInstance(arg: A): Lazy = lazy {
    val v = value ?: return@lazy null
    return@lazy v(arg)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy