commonMain.org.kodein.di.conf.ConfigurableDI.kt Maven / Gradle / Ivy
package org.kodein.di.conf
import org.kodein.di.*
import org.kodein.di.internal.maySynchronized
import org.kodein.di.internal.synchronizedIfNull
/**
* A class that can be used to configure a DI object and as a DI object.
*
* If you want it to be mutable, the [mutable] property needs to be set **before** any dependency retrieval.
* The non-mutable configuration methods ([addImport], [addExtend] & [addConfig]) needs to happen **before** any dependency retrieval.
*/
public class ConfigurableDI : DI {
// override val container: KodeinContainer
// get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
/** @suppress */
override val di: DI get() = this
private val _lock = Any()
/**
* Whether this Configurabledi can be mutated.
*
* `null` = not set yet. `true` = can be mutated. `false` = cannot be mutated.
*
* Note that if not set, this field will be set to false on `first` DI retrieval.
*/
public var mutable: Boolean? = null
set(value) {
if (value == field)
return
if (field != null)
throw IllegalStateException("Mutable field has already been set. You must set the mutable field before first retrieval.")
field = value
}
/**
* Default constructor.
*/
public constructor()
/**
* Convenient constructor to directly set the mutability.
*
* @param mutable Whether this DI can be mutated.
*/
public constructor(mutable: Boolean) {
this.mutable = mutable
}
/**
* Configuration lambdas.
*
* When constructing the DI instance (upon first retrieval), all configuration lambdas will be applied.
*/
private var _configs: MutableList Unit>? = ArrayList()
/**
* DI instance. If it is not null, than it cannot be configured anymore.
*/
@Volatile
private var _instance: DI? = null
/**
* Get the DI instance if it has already been constructed, or construct it if not.
*
* The first time this function is called is the end of the configuration.
*/
public fun getOrConstruct(): DI {
return synchronizedIfNull(
lock = _lock,
predicate = { _instance },
ifNotNull = { it },
ifNull = {
if (mutable == null)
mutable = false
val configs = checkNotNull(_configs) { "recursive initialization detected" }
_configs = null
val (di, init) = DI.withDelayedCallbacks {
for (config in configs)
config()
}
_instance = di
init()
di
}
)
}
/**
* Clear all the bindings of the DI instance. Needs [mutable] to be true.
*
* @throws IllegalStateException if [mutable] is not `true`.
*/
public fun clear() {
if (mutable != true)
throw IllegalStateException("Configurabledi is not mutable, you cannot clear bindings.")
maySynchronized(_lock) {
val configs = _configs
if (configs != null) {
configs.clear()
}
else {
_instance = null
_configs = ArrayList()
}
}
}
/**
* Whether or not this DI can be configured (meaning that it has not been used for retrieval yet).
*/
public val canConfigure: Boolean get() = _instance == null
/**
* Adds a configuration to the bindings that will be applied when the DI is constructed.
*
* @param config The lambda to be applied when the DI instance is constructed.
* @exception IllegalStateException When calling this function after [getOrConstruct] or any `DI` retrieval function.
*/
public fun addConfig(config: DI.MainBuilder.() -> Unit) {
maySynchronized(_lock) {
val configs = _configs
if (configs == null) {
if (mutable != true)
throw IllegalStateException("The non-mutable Configurabledi has been accessed and therefore constructed, you cannot add bindings after first retrieval.")
val previous = _instance
_instance = null
_configs = ArrayList()
if (previous != null)
addExtend(previous)
}
_configs!!.add(config)
}
}
/**
* Adds a module to the bindings that will be applied when the DI is constructed.
*
* @param module The module to apply when the DI instance is constructed.
* @param allowOverride Whether this module is allowed to override existing bindings.
* @exception IllegalStateException When calling this function after [getOrConstruct] or any `DI` retrieval function.
*/
public fun addImport(module: DI.Module, allowOverride: Boolean = false): Unit = addConfig { import(module, allowOverride) }
/**
* Adds the bindings of an existing DI instance to the bindings that will be applied when the DI is constructed.
*
* @param di The existing DI instance whose bindings to be apply when the DI instance is constructed.
* @param allowOverride Whether these bindings are allowed to override existing bindings.
* @exception IllegalStateException When calling this function after [getOrConstruct] or any `DI` retrieval function.
*/
public fun addExtend(di: DI, allowOverride: Boolean = false): Unit = addConfig { extend(di, allowOverride) }
/** @suppress */
override val container: DIContainer get() = getOrConstruct().container
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy