org.jetbrains.dokka.javadoc.location.JavadocLocationProvider.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javadoc-plugin Show documentation
Show all versions of javadoc-plugin Show documentation
Dokka is an API documentation engine for Kotlin
The newest version!
/*
* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package org.jetbrains.dokka.javadoc.location
import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProvider
import org.jetbrains.dokka.javadoc.pages.*
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.Nullable
import org.jetbrains.dokka.links.PointingToDeclaration
import org.jetbrains.dokka.model.DisplaySourceSet
import org.jetbrains.dokka.pages.ContentPage
import org.jetbrains.dokka.pages.PageNode
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import java.util.*
public class JavadocLocationProvider(
pageRoot: RootPageNode,
dokkaContext: DokkaContext
) : DefaultLocationProvider(pageRoot, dokkaContext) {
private val pathIndex = IdentityHashMap>().apply {
fun registerPath(page: PageNode, prefix: List = emptyList()) {
val packagePath = page.takeIf { it is JavadocPackagePageNode }?.name.orEmpty()
.replace(".", "/")
val newPathPrefix = prefix + packagePath
val path = (prefix + when (page) {
is AllClassesPage -> listOf("allclasses")
is TreeViewPage -> if (page.classes == null)
listOf("overview-tree")
else
listOf("package-tree")
is ContentPage -> if (page.dri.isNotEmpty() && page.dri.first().classNames != null)
listOfNotNull(page.dri.first().classNames)
else if (page is JavadocPackagePageNode)
listOf(packagePath, "package-summary")
else if (page is IndexPage)
listOf("index-files", page.name)
else if (page is DeprecatedPage)
listOf("deprecated")
else
listOf("index")
else -> emptyList()
}).filterNot { it.isEmpty() }
put(page, path)
page.children.forEach { registerPath(it, newPathPrefix) }
}
put(pageRoot, listOf("index"))
pageRoot.children.forEach { registerPath(it) }
}
private val parentPageIndex = HashMap()
private val nodeIndex = HashMap().apply {
fun registerNode(node: PageNode) {
if (node is ContentPage) put(node.dri.first(), node)
(node as? JavadocClasslikePageNode)?.getAnchorables()?.forEach { navigableNode ->
parentPageIndex[navigableNode.getDRI()] = node
}
node.children.forEach(::registerNode)
}
registerNode(pageRoot)
}
private operator fun IdentityHashMap>.get(dri: DRI) = this[nodeIndex[dri]]
private fun List.relativeTo(context: List): String {
val contextPath = context.dropLast(1).flatMap { it.split("/") }
val commonPathElements = flatMap { it.split("/") }.zip(contextPath).takeWhile { (a, b) -> a == b }.count()
return (List(contextPath.size - commonPathElements) { ".." } + this.flatMap { it.split("/") }.drop(commonPathElements)).joinToString("/")
}
private fun JavadocClasslikePageNode.findAnchorableByDRI(dri: DRI): AnchorableJavadocNode? =
(constructors + methods + entries + properties).firstOrNull { it.dri == dri }
override fun resolve(dri: DRI, sourceSets: Set, context: PageNode?): String? =
getLocalLocation(dri, context)
?: getLocalLocation(dri.copy(target = PointingToDeclaration), context)
// Not found in PageGraph, that means it's an external link
?: getExternalLocation(dri, sourceSets)
?: getExternalLocation(dri.copy(target = PointingToDeclaration), sourceSets)
private fun getLocalLocation(dri: DRI, context: PageNode?): String? =
nodeIndex[dri]?.let { resolve(it, context) }
?: parentPageIndex[dri]?.let {
val anchor = when (val anchorElement = (it as? JavadocClasslikePageNode)?.findAnchorableByDRI(dri)) {
is JavadocFunctionNode -> anchorElement.getAnchor()
is JavadocEntryNode -> anchorElement.name
is JavadocPropertyNode -> anchorElement.name
else -> anchorForDri(dri)
}
"${resolve(it, context, skipExtension = true)}.html#$anchor"
}
private fun anchorForDri(dri: DRI): String =
dri.callable?.let { callable ->
"${callable.name}(${
callable.params.joinToString(",") {
((it as? Nullable)?.wrapped ?: it).toString()
}
})"
} ?: dri.classNames.orEmpty()
override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String {
return pathIndex[node]?.relativeTo(pathIndex[context].orEmpty())?.let {
if (skipExtension) it.removeSuffix(".html") else it
} ?: run {
throw IllegalStateException("Path for ${node::class.java.canonicalName}:${node.name} not found")
}
}
public fun resolve(link: LinkJavadocListEntry, contextRoot: PageNode? = null, skipExtension: Boolean = true): String {
return pathIndex[link.dri.first()]?.let {
when (link.kind) {
JavadocContentKind.Class -> it
JavadocContentKind.OverviewSummary -> it.dropLast(1) + "index"
JavadocContentKind.PackageSummary -> it.dropLast(1) + "package-summary"
JavadocContentKind.AllClasses -> it.dropLast(1) + "allclasses"
JavadocContentKind.OverviewTree -> it.dropLast(1) + "overview-tree"
JavadocContentKind.PackageTree -> it.dropLast(1) + "package-tree"
JavadocContentKind.IndexPage -> it.dropLast(1) + "index-1"
else -> it
}
}?.relativeTo(pathIndex[contextRoot].orEmpty())?.let { if (skipExtension) "$it.html" else it }.orEmpty()
}
override fun pathToRoot(from: PageNode): String {
TODO("Not yet implemented")
}
override fun ancestors(node: PageNode): List {
TODO("Not yet implemented")
}
override fun expectedLocationForDri(dri: DRI): String {
if (dri.packageName?.isNotEmpty() == true && dri.classNames == null)
return (dri.packageName?.split(".").orEmpty() + "package-summary").joinToString("/")
return (dri.packageName?.split(".").orEmpty() +
dri.classNames?.split(".").orEmpty() // Top-level methods will always be relocated which is fine
).joinToString("/")
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy