From e1dad14904ca0e3b727cf0d7239db35f5402e775 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Thu, 19 Dec 2019 15:02:03 +0100 Subject: Resolved problems with plugins loading ordering and added type safe ordering function --- core/src/main/kotlin/plugability/DokkaContext.kt | 52 +++++++++++++++++++++--- core/src/main/kotlin/plugability/DokkaPlugin.kt | 4 +- core/src/main/kotlin/plugability/extensions.kt | 41 +++++++++++++++---- 3 files changed, 82 insertions(+), 15 deletions(-) (limited to 'core/src/main/kotlin/plugability') diff --git a/core/src/main/kotlin/plugability/DokkaContext.kt b/core/src/main/kotlin/plugability/DokkaContext.kt index 7b1c1306..8709b74f 100644 --- a/core/src/main/kotlin/plugability/DokkaContext.kt +++ b/core/src/main/kotlin/plugability/DokkaContext.kt @@ -36,6 +36,7 @@ interface DokkaContext : DokkaExtensionHandler { .also { checkClasspath(it) } .let { ServiceLoader.load(DokkaPlugin::class.java, it) } .forEach { install(it) } + applyExtensions() }.also { it.logInitialisationInfo() } } } @@ -54,7 +55,7 @@ fun DokkaContext.single(point: E): T where T : Any, E : ExtensionPoint } interface DokkaContextConfiguration { - fun addExtension(extension: Extension<*>) + fun addExtensionDependencies(extension: Extension<*>) } private class DokkaContextConfigurationImpl( @@ -63,11 +64,43 @@ private class DokkaContextConfigurationImpl( override val platforms: Map ) : DokkaContext, DokkaContextConfiguration { private val plugins = mutableMapOf, DokkaPlugin>() - private val pluginStubs = mutableMapOf, DokkaPlugin>() - internal val extensions = mutableMapOf, MutableList>>() + private enum class State { + UNVISITED, + VISITING, + VISITED; + } + + internal val verticesWithState = mutableMapOf, State>() + internal val adjacencyList: MutableMap, MutableList>> = mutableMapOf() + + private fun topologicalSort() { + + val result: MutableList> = mutableListOf() + + fun visit(n: Extension<*>) { + val state = verticesWithState[n] + if(state == State.VISITED) + return + if(state == State.VISITING) + throw Error("Detected cycle in plugins graph") + verticesWithState[n] = State.VISITING + adjacencyList[n]?.forEach { visit(it) } + verticesWithState[n] = State.VISITED + result += n + } + + for((vertex, state) in verticesWithState) { + if(state == State.UNVISITED) + visit(vertex) + } + result.asReversed().forEach { + extensions.getOrPut(it.extensionPoint, ::mutableListOf) += it + } + } + @Suppress("UNCHECKED_CAST") override operator fun get(point: E, askDefault: AskDefault) where T : Any, E : ExtensionPoint = when (askDefault) { @@ -91,8 +124,13 @@ private class DokkaContextConfigurationImpl( plugin.internalInstall(this) } - override fun addExtension(extension: Extension<*>) { - extensions.getOrPut(extension.extensionPoint, ::mutableListOf) += extension + override fun addExtensionDependencies(extension: Extension<*>) { + val orderDsl = OrderDsl() + extension.ordering?.invoke(orderDsl) + + verticesWithState += extension to State.UNVISITED + adjacencyList.getOrPut(extension, ::mutableListOf) += orderDsl.following.toList() + orderDsl.previous.forEach { adjacencyList.getOrPut(it, ::mutableListOf) += extension } } fun logInitialisationInfo() { @@ -105,6 +143,10 @@ private class DokkaContextConfigurationImpl( logger.progress("Loaded: $loadedListForDebug") } + + fun applyExtensions() { + topologicalSort() + } } private fun checkClasspath(classLoader: URLClassLoader) { diff --git a/core/src/main/kotlin/plugability/DokkaPlugin.kt b/core/src/main/kotlin/plugability/DokkaPlugin.kt index 7a968b8b..3f82000d 100644 --- a/core/src/main/kotlin/plugability/DokkaPlugin.kt +++ b/core/src/main/kotlin/plugability/DokkaPlugin.kt @@ -40,8 +40,6 @@ abstract class DokkaPlugin { extensionDelegates.asSequence() .filterIsInstance>>() // should be always true .map { it.get(this) } - .forEach { ctx.addExtension(it) } + .forEach { ctx.addExtensionDependencies(it) } } - - } \ No newline at end of file diff --git a/core/src/main/kotlin/plugability/extensions.kt b/core/src/main/kotlin/plugability/extensions.kt index 3039cb5a..8d1a7ba0 100644 --- a/core/src/main/kotlin/plugability/extensions.kt +++ b/core/src/main/kotlin/plugability/extensions.kt @@ -7,7 +7,7 @@ data class ExtensionPoint internal constructor( override fun toString() = "ExtensionPoint: $pluginClass/$pointName" } -class Extension internal constructor( +abstract class Extension internal constructor( internal val extensionPoint: ExtensionPoint, internal val pluginClass: String, internal val extensionName: String, @@ -17,12 +17,39 @@ class Extension internal constructor( override fun toString() = "Extension: $pluginClass/$extensionName" override fun equals(other: Any?) = - if (other is Extension<*>) this.pluginClass == other.extensionName && this.extensionName == other.extensionName + if (other is Extension<*>) this.pluginClass == other.pluginClass && this.extensionName == other.extensionName else false override fun hashCode() = listOf(pluginClass, extensionName).hashCode() } +class ExtensionOrdered( + extensionPoint: ExtensionPoint, + pluginClass: String, + extensionName: String, + action: T, + ordering: (OrderDsl.() -> Unit) +) : Extension( + extensionPoint, + pluginClass, + extensionName, + action, + ordering +) + +class ExtensionUnordered( + extensionPoint: ExtensionPoint, + pluginClass: String, + extensionName: String, + action: T +) : Extension( + extensionPoint, + pluginClass, + extensionName, + action, + null +) + internal data class Ordering(val previous: Set>, val following: Set>) @DslMarker @@ -31,16 +58,16 @@ annotation class ExtensionsDsl @ExtensionsDsl class ExtendingDSL(private val pluginClass: String, private val extensionName: String) { infix fun ExtensionPoint.with(action: T) = - Extension(this, this@ExtendingDSL.pluginClass, extensionName, action) + ExtensionUnordered(this, this@ExtendingDSL.pluginClass, extensionName, action) - infix fun Extension.order(block: OrderDsl.() -> Unit) = - Extension(extensionPoint, pluginClass, extensionName, action, block) + infix fun ExtensionUnordered.order(block: OrderDsl.() -> Unit) = + ExtensionOrdered(extensionPoint, pluginClass, extensionName, action, block) } @ExtensionsDsl class OrderDsl { - private val previous = mutableSetOf>() - private val following = mutableSetOf>() + internal val previous = mutableSetOf>() + internal val following = mutableSetOf>() fun after(vararg extensions: Extension<*>) { previous += extensions -- cgit