aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/plugability/DokkaContext.kt
blob: 7727329c9f7de3eb5a195e9d3e8c2d6b8e05e162 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package org.jetbrains.dokka.plugability

import org.jetbrains.dokka.DokkaLogger
import java.io.File
import java.net.URLClassLoader
import java.util.*
import kotlin.reflect.KClass

interface DokkaContext {
    operator fun <T : Any, E : ExtensionPoint<T>> get(point: E): List<Extension<T>>

    fun <T : DokkaPlugin> plugin(kclass: KClass<T>): T?

    val logger: DokkaLogger

    companion object {
        fun create(pluginsClasspath: Iterable<File>, logger: DokkaLogger): DokkaContext =
            DokkaContextConfigurationImpl(logger).apply {
                pluginsClasspath.map { it.relativeTo(File(".").absoluteFile).toURI().toURL() }
                    .toTypedArray()
                    .let { URLClassLoader(it, this.javaClass.classLoader) }
                    .also { checkClasspath(it) }
                    .let { ServiceLoader.load(DokkaPlugin::class.java, it) }
                    .forEach { install(it) }
            }.also { it.logInitialisationInfo() }
    }
}

interface DokkaContextConfiguration {
    fun addExtension(extension: Extension<*>)
}

private class DokkaContextConfigurationImpl(
    override val logger: DokkaLogger
) : DokkaContext, DokkaContextConfiguration {
    private val plugins = mutableMapOf<KClass<*>, DokkaPlugin>()

    internal val extensions = mutableMapOf<ExtensionPoint<*>, MutableList<Extension<*>>>()

    @Suppress("UNCHECKED_CAST")
    override operator fun <T : Any, E : ExtensionPoint<T>> get(point: E) = extensions[point] as List<Extension<T>>

    @Suppress("UNCHECKED_CAST")
    override fun <T : DokkaPlugin> plugin(kclass: KClass<T>) = plugins[kclass] as T

    fun install(plugin: DokkaPlugin) {
        plugins[plugin::class] = plugin
        plugin.context = this
        plugin.internalInstall(this)
    }

    override fun addExtension(extension: Extension<*>) {
        extensions.getOrPut(extension.extensionPoint, ::mutableListOf) += extension
    }

    fun logInitialisationInfo() {
        val pluginNames: List<String> = plugins.values.map { it::class.qualifiedName.toString() }

        val loadedListForDebug = extensions.run { keys + values.flatten() }.toList()
            .joinToString(prefix = "[\n", separator = ",\n", postfix = "\n]") { "\t$it" }

        logger.progress("Loaded plugins: ${pluginNames}")
        logger.progress("Loaded: ${loadedListForDebug}")

    }
}

private fun checkClasspath(classLoader: URLClassLoader) {
    classLoader.findResource(DokkaContext::class.java.name.replace('.', '/') + ".class")?.also {
        throw AssertionError(
            "Dokka API found on plugins classpath. This will lead to subtle bugs. " +
                    "Please fix your plugins dependencies or exclude dokka api artifact from plugin classpath"
        )
    }
}