Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

map & set multibinding #1951

Open
wants to merge 1 commit into
base: 4.1.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 104 additions & 2 deletions projects/core/koin-core/src/commonMain/kotlin/org/koin/core/Koin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import org.koin.core.logger.EmptyLogger
import org.koin.core.logger.Logger
import org.koin.core.module.Module
import org.koin.core.module.flatten
import org.koin.core.module.mapMultibindingQualifier
import org.koin.core.module.setMultibindingQualifier
import org.koin.core.parameter.ParametersDefinition
import org.koin.core.qualifier.Qualifier
import org.koin.core.qualifier.TypeQualifier
Expand Down Expand Up @@ -151,6 +153,106 @@ class Koin {
parameters: ParametersDefinition? = null,
): T? = scopeRegistry.rootScope.getOrNull(clazz, qualifier, parameters)

/**
* Lazy inject a Koin Map Multibinding instance
* @param qualifier
* @param parameters
*
* @return Lazy instance of type Map<K, V>
*/
inline fun <reified K, reified V> injectMapMultibinding(
qualifier: Qualifier = mapMultibindingQualifier<K, V>(),
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null,
): Lazy<Map<K, V>> = scopeRegistry.rootScope.inject(qualifier, mode, parameters)

/**
* Lazy inject a Koin Set Multibinding instance
* @param qualifier
* @param parameters
*
* @return Lazy instance of type Set<E>
*/
inline fun <reified E> injectSetMultibinding(
qualifier: Qualifier = setMultibindingQualifier<E>(),
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null,
): Lazy<Set<E>> = scopeRegistry.rootScope.inject(qualifier, mode, parameters)

/**
* Lazy inject a Koin Map Multibinding instance if available
* @param qualifier
* @param parameters
*
* @return Lazy instance of type Map<K, V> or null
*/
inline fun <reified K, reified V> injectMapMultibindingOrNull(
qualifier: Qualifier = mapMultibindingQualifier<K, V>(),
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null,
): Lazy<Map<K, V>?> = scopeRegistry.rootScope.injectOrNull(qualifier, mode, parameters)

/**
* Lazy inject a Koin Set Multibinding instance if available
* @param qualifier
* @param parameters
*
* @return Lazy instance of type Set<E> or null
*/
inline fun <reified E> injectSetMultibindingOrNull(
qualifier: Qualifier = setMultibindingQualifier<E>(),
mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(),
noinline parameters: ParametersDefinition? = null,
): Lazy<Set<E>?> = scopeRegistry.rootScope.injectOrNull(qualifier, mode, parameters)

/**
* Get a Koin Map Multibinding instance
* @param qualifier
* @param parameters
*
* @return instance of type Map<K, V>
*/
inline fun <reified K, reified V> getMapMultibinding(
qualifier: Qualifier = mapMultibindingQualifier<K, V>(),
noinline parameters: ParametersDefinition? = null,
): Map<K, V> = scopeRegistry.rootScope.get(qualifier, parameters)

/**
* Get a Koin Set Multibinding instance
* @param qualifier
* @param parameters
*
* @return instance of type Set<E>
*/
inline fun <reified E> getSetMultibinding(
qualifier: Qualifier = setMultibindingQualifier<E>(),
noinline parameters: ParametersDefinition? = null,
): Set<E> = scopeRegistry.rootScope.get(qualifier, parameters)

/**
* Get a Koin Map Multibinding instance if available
* @param qualifier
* @param parameters
*
* @return instance of type Map<K, V> or null
*/
inline fun <reified K, reified V> getMapMultibindingOrNull(
qualifier: Qualifier = mapMultibindingQualifier<K, V>(),
noinline parameters: ParametersDefinition? = null,
): Map<K, V>? = scopeRegistry.rootScope.getOrNull(qualifier, parameters)

/**
* Get a Koin Set Multibinding instance if available
* @param qualifier
* @param parameters
*
* @return instance of type Set<E> or null
*/
inline fun <reified E> getSetMultibindingOrNull(
qualifier: Qualifier = setMultibindingQualifier<E>(),
noinline parameters: ParametersDefinition? = null,
): Set<E>? = scopeRegistry.rootScope.getOrNull(qualifier, parameters)

/**
* Declare a component definition from the given instance
* This result of declaring a single definition of type T, returning the given instance
Expand Down Expand Up @@ -307,12 +409,12 @@ class Koin {
* @param allowOverride - allow to override definitions
* @param createEagerInstances - run instance creation for eager single definitions
*/
fun loadModules(modules: List<Module>, allowOverride: Boolean = true, createEagerInstances : Boolean = false) {
fun loadModules(modules: List<Module>, allowOverride: Boolean = true, createEagerInstances: Boolean = false) {
val flattedModules = flatten(modules)
instanceRegistry.loadModules(flattedModules, allowOverride)
scopeRegistry.loadScopes(flattedModules)

if (createEagerInstances){
if (createEagerInstances) {
createEagerInstances()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
package org.koin.core.module

import org.koin.core.annotation.KoinInternalApi
import org.koin.core.definition.*
import org.koin.core.definition.Definition
import org.koin.core.definition.IndexKey
import org.koin.core.definition.Kind
import org.koin.core.definition.KoinDefinition
import org.koin.core.definition._createDefinition
import org.koin.core.definition.indexKey
import org.koin.core.error.DefinitionOverrideException
import org.koin.core.instance.FactoryInstanceFactory
import org.koin.core.instance.InstanceFactory
Expand Down Expand Up @@ -114,6 +119,69 @@ class Module(
return KoinDefinition(this, factory)
}

/**
* Declare a Single Map<K, V> and inject an element to it with a given key
* @param key can't be null
* @param definition - the element definition function
*/
inline fun <reified K : Any, reified V> intoMap(
key: K,
qualifier: Qualifier = mapMultibindingQualifier<K, V>(),
noinline definition: Definition<V>,
): KoinDefinition<Map<K, V>> {
single(multibindingValueQualifier(qualifier, key), definition = definition)
single(multibindingIterateKeyQualifier(qualifier, key)) {
MultibindingIterateKey(key, multibindingValueQualifier(qualifier, key))
}
return declareMapMultibinding(qualifier)
}

/**
* Declare a Single Map<K, V> definition, the key type [K] can't be null
* @param qualifier can't be null
* @param createdAtStart
*/
inline fun <reified K : Any, reified V> declareMapMultibinding(
qualifier: Qualifier = mapMultibindingQualifier<K, V>(),
createdAtStart: Boolean = false,
): KoinDefinition<Map<K, V>> {
val isCreatedAtStart = createdAtStart || this._createdAtStart
return single<Map<K, V>>(qualifier) { parametersHolder ->
MapMultibinding(isCreatedAtStart, this, qualifier, V::class, parametersHolder)
}
}

/**
* Declare a Single Set<E> and inject an element to it
* @param definition - the element definition function
*/
inline fun <reified E> intoSet(
qualifier: Qualifier = setMultibindingQualifier<E>(),
noinline definition: Definition<E>,
): KoinDefinition<Set<E>> {
val key = SetMultibinding.getDistinctKey()
single(multibindingValueQualifier(qualifier, key), definition = definition)
single(multibindingIterateKeyQualifier(qualifier, key)) {
MultibindingIterateKey(key, multibindingValueQualifier(qualifier, key))
}
return declareSetMultibinding(qualifier)
}

/**
* Declare a Single Set<E> definition
* @param qualifier can't be null
* @param createdAtStart
*/
inline fun <reified E> declareSetMultibinding(
qualifier: Qualifier = setMultibindingQualifier<E>(),
createdAtStart: Boolean = false,
): KoinDefinition<Set<E>> {
val isCreatedAtStart = createdAtStart || this._createdAtStart
return single(qualifier) { parametersHolder ->
SetMultibinding(isCreatedAtStart, this, qualifier, E::class, parametersHolder)
}
}

@KoinInternalApi
fun indexPrimaryType(instanceFactory: InstanceFactory<*>) {
val def = instanceFactory.beanDefinition
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
* Copyright 2017-Present the original author or authors.
*
* 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.koin.core.module

import co.touchlab.stately.concurrency.AtomicInt
import org.koin.core.parameter.ParametersHolder
import org.koin.core.qualifier.Qualifier
import org.koin.core.qualifier.StringQualifier
import org.koin.core.scope.Scope
import org.koin.ext.getFullName
import kotlin.reflect.KClass

/**
* Multibinding of Map & Set
*
* @author - luozejiaqun
*/
inline fun <reified K, reified V> mapMultibindingQualifier(): Qualifier =
StringQualifier("map_multibinding_${K::class.getFullName()}_${V::class.getFullName()}")

inline fun <reified E> setMultibindingQualifier(): Qualifier =
StringQualifier("set_multibinding_${E::class.getFullName()}")

@PublishedApi
internal fun <K> multibindingValueQualifier(multibindingQualifier: Qualifier, key: K): Qualifier =
StringQualifier("${multibindingQualifier.value}_of_$key")

@PublishedApi
internal fun <K> multibindingIterateKeyQualifier(multibindingQualifier: Qualifier, key: K): Qualifier =
StringQualifier("${multibindingQualifier.value}_iterate_$key")

@PublishedApi
internal class MultibindingIterateKey<T>(val key: T, val valueQualifier: Qualifier)

@PublishedApi
internal class MapMultibinding<K : Any, V>(
createdAtStart: Boolean,
private val scope: Scope,
private val qualifier: Qualifier,
private val valueClass: KClass<*>,
private val parametersHolder: ParametersHolder,
) : Map<K, V> {

init {
if (createdAtStart) {
values
}
}

override val keys: Set<K>
get() {
val multibindingKeys = mutableSetOf<K>()
scope.getAll<MultibindingIterateKey<*>>()
.mapNotNullTo(multibindingKeys) { multibindingIterateKey ->
if (multibindingIterateKey.valueQualifier.value.startsWith(qualifier.value)) {
multibindingIterateKey.key as? K
} else {
null
}
}
return multibindingKeys
}

override val size: Int
get() = keys.size

override val values: Collection<V>
get() = keys.map { get(it) }

override val entries: Set<Map.Entry<K, V>>
get() {
val filed = LinkedHashSet<Map.Entry<K, V>>()
keys.mapTo(filed) { Entry(it, get(it)) }
return filed
}

override fun containsKey(key: K): Boolean =
keys.contains(key)

override fun containsValue(value: V): Boolean =
values.contains(value)

override fun get(key: K): V {
return scope.get(valueClass, multibindingValueQualifier(qualifier, key)) { parametersHolder }
}

override fun isEmpty(): Boolean = keys.isEmpty()

private class Entry<K, V>(override val key: K, override val value: V) : Map.Entry<K, V>
}

@PublishedApi
internal class SetMultibinding<E>(
createdAtStart: Boolean,
private val scope: Scope,
private val qualifier: Qualifier,
private val valueClass: KClass<*>,
private val parametersHolder: ParametersHolder,
) : Set<E> {
init {
if (createdAtStart) {
getAll()
}
}

override val size: Int
get() = getAllKeys().size

override fun contains(element: E): Boolean {
return getAll().contains(element)
}

override fun containsAll(elements: Collection<E>): Boolean {
return getAll().containsAll(elements)
}

override fun isEmpty(): Boolean = size == 0

override fun iterator(): Iterator<E> {
return getAll().iterator()
}

private fun getAllKeys(): Set<Key> {
val multibindingKeys = mutableSetOf<Key>()
scope.getAll<MultibindingIterateKey<*>>()
.mapNotNullTo(multibindingKeys) { multibindingIterateKey ->
if (multibindingIterateKey.valueQualifier.value.startsWith(qualifier.value)) {
multibindingIterateKey.key as? Key
} else {
null
}
}
return multibindingKeys
}

private fun getAll(): Set<E> {
val result = LinkedHashSet<E>()
getAllKeys().mapTo(result) { get(it) }
return result
}

private fun get(key: Key): E {
return scope.get(valueClass, multibindingValueQualifier(qualifier, key)) { parametersHolder }
}

class Key(private val placeholder: Int) {
override fun toString(): String = "placeholder_$placeholder"
}

companion object {
private val accumulatingKey = AtomicInt(0)

fun getDistinctKey() = Key(accumulatingKey.incrementAndGet())
}
}
Loading