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

net.bjoernpetersen.musicbot.api.plugin.management.DefaultDependencyManager.kt Maven / Gradle / Ivy

There is a newer version: 0.25.0
Show newest version
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)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy