diff options
author | Paweł Marks <pmarks@virtuslab.com> | 2019-11-25 15:34:47 +0100 |
---|---|---|
committer | Paweł Marks <pmarks@virtuslab.com> | 2019-11-26 15:27:04 +0100 |
commit | a945617725e8df084270aacd5af76da2b911111e (patch) | |
tree | f752ceb1427108d052575440c339b61bcd6bb5a9 /core/src | |
parent | 4b1a3a2cbe62f98c9f1b472e70d754645d7f8641 (diff) | |
download | dokka-a945617725e8df084270aacd5af76da2b911111e.tar.gz dokka-a945617725e8df084270aacd5af76da2b911111e.tar.bz2 dokka-a945617725e8df084270aacd5af76da2b911111e.zip |
Interface segregation in plugins api
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/main/kotlin/DokkaGenerator.kt | 4 | ||||
-rw-r--r-- | core/src/main/kotlin/plugability/DokkaContext.kt | 82 | ||||
-rw-r--r-- | core/src/main/kotlin/plugability/DokkaPlugin.kt | 14 |
3 files changed, 54 insertions, 46 deletions
diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt index b29a0da2..05eb3054 100644 --- a/core/src/main/kotlin/DokkaGenerator.kt +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -25,9 +25,7 @@ class DokkaGenerator( fun generate(): Unit { logger.debug("Initializing plugins") - val context = DokkaContext.from(configuration.pluginsClasspath) - logger.progress("Loaded plugins: ${context.pluginNames}") - logger.progress("Loaded: ${context.loadedListForDebug}") + val context = DokkaContext.create(configuration.pluginsClasspath, logger) configuration.passesConfigurations.map { pass -> AnalysisEnvironment(DokkaMessageCollector(logger), pass.analysisPlatform).run { diff --git a/core/src/main/kotlin/plugability/DokkaContext.kt b/core/src/main/kotlin/plugability/DokkaContext.kt index dcd3398e..7727329c 100644 --- a/core/src/main/kotlin/plugability/DokkaContext.kt +++ b/core/src/main/kotlin/plugability/DokkaContext.kt @@ -1,55 +1,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>> -class DokkaContext private constructor() { + 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") - operator fun <T: Any, E: ExtensionPoint<T>> get(point: E) = extensions[point] as List<Extension<T>> - - @PublishedApi - internal fun plugin(kclass: KClass<*>) = plugins[kclass] - - val pluginNames: List<String> - get() = plugins.values.map { it::class.qualifiedName.toString() } + override operator fun <T : Any, E : ExtensionPoint<T>> get(point: E) = extensions[point] as List<Extension<T>> - val loadedListForDebug - get() = extensions.run { keys + values.flatten() }.toList() - .joinToString(prefix = "[\n", separator = ",\n", postfix = "\n]") { "\t$it" } + @Suppress("UNCHECKED_CAST") + override fun <T : DokkaPlugin> plugin(kclass: KClass<T>) = plugins[kclass] as T - private fun install(plugin: DokkaPlugin) { + fun install(plugin: DokkaPlugin) { plugins[plugin::class] = plugin + plugin.context = this plugin.internalInstall(this) } - companion object { - fun from(pluginsClasspath: Iterable<File>) = DokkaContext().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) } - } + override fun addExtension(extension: Extension<*>) { + extensions.getOrPut(extension.extensionPoint, ::mutableListOf) += extension } - private fun checkClasspath(classLoader: URLClassLoader) { - classLoader.findResource(javaClass.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" - ) - } - } + 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}") - internal fun addExtension(it: Extension<*>) { - extensions.getOrPut(it.extensionPoint, ::mutableListOf) += it } } + +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" + ) + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/plugability/DokkaPlugin.kt b/core/src/main/kotlin/plugability/DokkaPlugin.kt index 60ca245c..663cf88e 100644 --- a/core/src/main/kotlin/plugability/DokkaPlugin.kt +++ b/core/src/main/kotlin/plugability/DokkaPlugin.kt @@ -3,12 +3,7 @@ package org.jetbrains.dokka.plugability import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty import kotlin.reflect.KProperty1 -import kotlin.reflect.KTypeProjection import kotlin.reflect.full.createInstance -import kotlin.reflect.full.createType -import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.jvmErasure private typealias ExtensionDelegate<T> = ReadOnlyProperty<DokkaPlugin, Extension<T>> @@ -18,10 +13,8 @@ abstract class DokkaPlugin { @PublishedApi internal var context: DokkaContext? = null - protected open fun install(context: DokkaContext) {} - protected inline fun <reified T : DokkaPlugin> plugin(): T = - context?.plugin(T::class) as? T ?: T::class.createInstance().also { it.context = this.context } + context?.plugin(T::class) ?: T::class.createInstance().also { it.context = this.context } protected fun <T : Any> extensionPoint() = object : ReadOnlyProperty<DokkaPlugin, ExtensionPoint<T>> { @@ -44,10 +37,7 @@ abstract class DokkaPlugin { }.also { thisRef.extensionDelegates += property } } - internal fun internalInstall(ctx: DokkaContext) { - context = ctx - install(ctx) - + internal fun internalInstall(ctx: DokkaContextConfiguration) { extensionDelegates.asSequence() .filterIsInstance<KProperty1<DokkaPlugin, Extension<*>>>() // always true .map { it.get(this) } |