net.bjoernpetersen.musicbot.api.plugin.management.DefaultDependencyManager.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of musicbot Show documentation
Show all versions of musicbot Show documentation
Core library of JMusicBot, which plays music from various providers.
package net.bjoernpetersen.musicbot.api.plugin.management
import com.google.common.collect.MultimapBuilder
import mu.KotlinLogging
import net.bjoernpetersen.musicbot.api.config.ChoiceBox
import net.bjoernpetersen.musicbot.api.config.Config
import net.bjoernpetersen.musicbot.api.config.ConfigSerializer
import net.bjoernpetersen.musicbot.api.config.NonnullConfigChecker
import net.bjoernpetersen.musicbot.api.config.SerializationException
import net.bjoernpetersen.musicbot.api.config.UiNode
import net.bjoernpetersen.musicbot.api.plugin.PluginLoader
import net.bjoernpetersen.musicbot.spi.plugin.GenericPlugin
import net.bjoernpetersen.musicbot.spi.plugin.PlaybackFactory
import net.bjoernpetersen.musicbot.spi.plugin.Plugin
import net.bjoernpetersen.musicbot.spi.plugin.Provider
import net.bjoernpetersen.musicbot.spi.plugin.Suggester
import net.bjoernpetersen.musicbot.spi.plugin.bases
import net.bjoernpetersen.musicbot.spi.plugin.hasActiveBase
import net.bjoernpetersen.musicbot.spi.plugin.id
import net.bjoernpetersen.musicbot.spi.plugin.management.DependencyConfigurationException
import net.bjoernpetersen.musicbot.spi.plugin.management.DependencyManager
import kotlin.reflect.KClass
class DefaultDependencyManager(
state: Config,
override val genericPlugins: List,
override val playbackFactories: List,
override val providers: List,
override val suggesters: List) : DependencyManager {
private val logger = KotlinLogging.logger { }
private val basesByPlugin: Map>> = allPlugins
.associateWith { it.bases.toSet() }
private val allBases: Set> = basesByPlugin.values.flatten().toSet()
@Suppress("UnstableApiUsage")
private val pluginsByBase = MultimapBuilder.SetMultimapBuilder
.hashKeys()
.hashSetValues()
.build, Plugin>().apply {
for (entry in basesByPlugin) {
entry.value.forEach {
put(it, entry.key)
}
}
}
private val pluginSerializer = object : ConfigSerializer {
override fun serialize(obj: Plugin): String = obj::class.qualifiedName!!
override fun deserialize(string: String): Plugin = allPlugins
.firstOrNull { it::class.qualifiedName == string } ?: throw SerializationException()
}
private val defaultByBase: Map, Config.SerializedEntry> =
allBases.associateWith { state.defaultEntry(it) }
constructor(config: Config, loader: PluginLoader) : this(config, loadPlugins(loader))
private constructor(config: Config, plugins: Plugins) : this(
config,
plugins.generic,
plugins.playbackFactories,
plugins.providers,
plugins.suggesters)
override fun getDefaults(plugin: Plugin): Sequence> {
return basesByPlugin[plugin]?.asSequence()?.filter { defaultByBase[it]?.get() == plugin }
?: throw IllegalStateException()
}
override fun getDefault(base: KClass): B? {
@Suppress("UNCHECKED_CAST")
return defaultByBase[base]?.get() as B?
}
override fun isDefault(plugin: Plugin, base: KClass<*>): Boolean {
return defaultByBase[base]?.get() == plugin
}
override fun setDefault(plugin: Plugin?, base: KClass<*>) {
defaultByBase[base]?.set(plugin) ?: logger.warn { "Tried to set default on unknown base" }
}
override fun findAvailable(base: KClass<*>): List {
return pluginsByBase[base].toList()
}
@Throws(DependencyConfigurationException::class)
override fun finish(): PluginFinder {
val genericPlugins: List = findEnabledGeneric()
val playbackFactories: List = findEnabledPlaybackFactory()
val providers: List = findEnabledProvider()
val suggesters: List = findEnabledSuggester()
val defaultByBase = findEnabledDependencies()
.associateWithTo(HashMap()) { base ->
val plugin = try {
getDefault(base)
} catch (e: SerializationException) {
null
} ?: throw DependencyConfigurationException("No default: ${base.qualifiedName}")
plugin
}
sequenceOf(genericPlugins, playbackFactories, providers, suggesters)
.flatMap { it.asSequence() }
.filter { it::class.hasActiveBase }
.forEach {
defaultByBase[it.id] = it
}
return PluginFinder(defaultByBase, genericPlugins, playbackFactories, providers, suggesters)
}
private fun Config.defaultEntry(base: KClass): Config.SerializedEntry {
fun defaultEntryUi(): UiNode {
return ChoiceBox(
{ it.name },
{ pluginsByBase[base].toList() })
}
val key = "${base.qualifiedName!!}.default"
return SerializedEntry(
key,
"",
pluginSerializer,
NonnullConfigChecker,
defaultEntryUi())
}
private companion object {
fun loadPlugins(loader: PluginLoader): Plugins {
return Plugins(
generic = loader.load(GenericPlugin::class).toList(),
playbackFactories = loader.load(PlaybackFactory::class).toList(),
providers = loader.load(Provider::class).toList(),
suggesters = loader.load(Suggester::class).toList())
}
}
}
data class Plugins(
val generic: List,
val playbackFactories: List,
val providers: List,
val suggesters: List)