Skip to content

Commit

Permalink
Fix link to javadoc enum entry (#2131)
Browse files Browse the repository at this point in the history
  • Loading branch information
vmishenev authored and MarcinAman committed Sep 30, 2021
1 parent 3dae816 commit c420522
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 20 deletions.
18 changes: 18 additions & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,20 @@ public final class org/jetbrains/dokka/links/DRI$Companion {
public final fun getTopLevel ()Lorg/jetbrains/dokka/links/DRI;
}

public final class org/jetbrains/dokka/links/DRIExtraContainer {
public fun <init> ()V
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun encode ()Ljava/lang/String;
public final fun getData ()Ljava/lang/String;
public final fun getMap ()Ljava/util/Map;
}

public abstract class org/jetbrains/dokka/links/DRIExtraProperty {
public fun <init> ()V
public final fun getKey ()Ljava/lang/String;
}

public final class org/jetbrains/dokka/links/DRIKt {
public static final fun getDriOfAny ()Lorg/jetbrains/dokka/links/DRI;
public static final fun getDriOfUnit ()Lorg/jetbrains/dokka/links/DRI;
Expand All @@ -477,6 +491,10 @@ public abstract class org/jetbrains/dokka/links/DriTarget {
public final class org/jetbrains/dokka/links/DriTarget$Companion {
}

public final class org/jetbrains/dokka/links/EnumEntryDRIExtra : org/jetbrains/dokka/links/DRIExtraProperty {
public static final field INSTANCE Lorg/jetbrains/dokka/links/EnumEntryDRIExtra;
}

public final class org/jetbrains/dokka/links/JavaClassReference : org/jetbrains/dokka/links/TypeReference {
public fun <init> (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
Expand Down
21 changes: 21 additions & 0 deletions core/src/main/kotlin/links/DRI.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.jetbrains.dokka.links

import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue

/**
* [DRI] stands for DokkaResourceIdentifier
Expand All @@ -23,6 +26,24 @@ data class DRI(
}
}

object EnumEntryDRIExtra: DRIExtraProperty<EnumEntryDRIExtra>()

abstract class DRIExtraProperty<T> {
val key: String = this::class.qualifiedName
?: (this.javaClass.let { it.`package`.name + "." + it.simpleName.ifEmpty { "anonymous" } })
}

class DRIExtraContainer(val data: String? = null) {
val map: MutableMap<String, Any> = if (data != null) ObjectMapper().readValue(data) else mutableMapOf()
inline operator fun <reified T> get(prop: DRIExtraProperty<T>): T? =
map[prop.key]?.let { prop as? T }

inline operator fun <reified T> set(prop: DRIExtraProperty<T>, value: T) =
value.also { map[prop.key] = it as Any }

fun encode(): String = ObjectMapper().writeValueAsString(map)
}

val DriOfUnit = DRI("kotlin", "Unit")
val DriOfAny = DRI("kotlin", "Any")

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package org.jetbrains.dokka.analysis

import com.intellij.psi.*
import org.jetbrains.dokka.links.Callable
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.DriTarget
import org.jetbrains.dokka.links.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
Expand All @@ -14,13 +13,16 @@ fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWi
val callable = parameter?.containingDeclaration ?: firstIsInstanceOrNull<CallableDescriptor>()

DRI(
firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString() ?: "",
(filterIsInstance<ClassDescriptor>() + filterIsInstance<TypeAliasDescriptor>()).toList()
packageName = firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString() ?: "",
classNames = (filterIsInstance<ClassDescriptor>() + filterIsInstance<TypeAliasDescriptor>()).toList()
.takeIf { it.isNotEmpty() }
?.asReversed()
?.joinToString(separator = ".") { it.name.asString() },
callable?.let { Callable.from(it) },
DriTarget.from(parameter ?: descriptor)
callable = callable?.let { Callable.from(it) },
target = DriTarget.from(parameter ?: descriptor),
extra = if (descriptor is EnumEntrySyntheticClassDescriptor)
DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode()
else null
)
}

Expand All @@ -30,9 +32,13 @@ fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run {
val classes = filterIsInstance<PsiClass>().filterNot { it is PsiTypeParameter }
.toList() // We only want exact PsiClass types, not PsiTypeParameter subtype
DRI(
classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', "") ?: "",
classes.toList().takeIf { it.isNotEmpty() }?.asReversed()?.mapNotNull { it.name }?.joinToString("."),
psiMethod?.let { Callable.from(it) } ?: psiField?.let { Callable.from(it) },
DriTarget.from(psi)
packageName = classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', "") ?: "",
classNames = classes.toList().takeIf { it.isNotEmpty() }?.asReversed()?.mapNotNull { it.name }
?.joinToString("."),
callable = psiMethod?.let { Callable.from(it) } ?: psiField?.let { Callable.from(it) },
target = DriTarget.from(psi),
extra = if (psi is PsiEnumConstant)
DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode()
else null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package org.jetbrains.dokka.base.resolvers.external.javadoc

import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider
import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation
import org.jetbrains.dokka.links.Callable
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.utilities.htmlEscape

Expand All @@ -30,8 +29,21 @@ open class JavadocExternalLocationProvider(
if (classNames == null) {
return "$docWithModule$packageLink/package-summary$extension".htmlEscape()
}
val classLink =
if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension"

// in Kotlin DRI of enum entry is not callable
if (DRIExtraContainer(extra)[EnumEntryDRIExtra] != null) {
val (classSplit, enumEntityAnchor) = if (callable == null) {
val lastIndex = classNames?.lastIndexOf(".") ?: 0
classNames?.substring(0, lastIndex) to classNames?.substring(lastIndex + 1)
} else
classNames to callable?.name

val classLink =
if (packageLink == null) "${classSplit}$extension" else "$packageLink/${classSplit}$extension"
return "$docWithModule$classLink#$enumEntityAnchor".htmlEscape()
}

val classLink = if (packageLink == null) "${classNames}$extension" else "$packageLink/${classNames}$extension"
val callableChecked = callable ?: return "$docWithModule$classLink".htmlEscape()

return ("$docWithModule$classLink#" + anchorPart(callableChecked)).htmlEscape()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package locationProvider

import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProvider
import org.jetbrains.dokka.base.resolvers.external.javadoc.JavadocExternalLocationProvider
import org.jetbrains.dokka.base.resolvers.shared.ExternalDocumentation
import org.jetbrains.dokka.base.resolvers.shared.PackageList
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
import org.jetbrains.dokka.links.*
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.net.URL

class JavadocExternalLocationProviderTest : BaseAbstractTest() {
private val testDataDir =
getTestDataDir("locationProvider").toAbsolutePath().toString().removePrefix("/").let { "/$it" }

private val jdk = "https://docs.oracle.com/javase/8/docs/api/"
private val jdkPackageListURL = URL("file://$testDataDir/jdk8-package-list")

private val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/")
classpath += jvmStdlibPath!!
}
}
}

private fun getTestLocationProvider(context: DokkaContext? = null): DefaultExternalLocationProvider {
val dokkaContext = context ?: DokkaContext.create(configuration, logger, emptyList())
val packageList = PackageList.load(jdkPackageListURL, 8, true)!!
val externalDocumentation =
ExternalDocumentation(URL(jdk), packageList)
return JavadocExternalLocationProvider(externalDocumentation, "--", "-", dokkaContext)
}

@Test
fun `link to enum entity of javadoc`() {
val locationProvider = getTestLocationProvider()
val ktDri = DRI(
"java.nio.file",
"StandardOpenOption.CREATE",
extra = DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode()
)
val javaDri = DRI(
"java.nio.file",
"StandardOpenOption",
Callable("CREATE", null, emptyList()),
PointingToDeclaration,
DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode()
)

assertEquals(
"https://docs.oracle.com/javase/8/docs/api/java/nio/file/StandardOpenOption.html#CREATE",
locationProvider.resolve(ktDri)
)

assertEquals(
"https://docs.oracle.com/javase/8/docs/api/java/nio/file/StandardOpenOption.html#CREATE",
locationProvider.resolve(javaDri)
)
}

@Test
fun `link to nested class of javadoc`() {
val locationProvider = getTestLocationProvider()
val dri = DRI(
"java.rmi.activation",
"ActivationGroupDesc.CommandEnvironment"
)

assertEquals(
"https://docs.oracle.com/javase/8/docs/api/java/rmi/activation/ActivationGroupDesc.CommandEnvironment.html",
locationProvider.resolve(dri)
)
}
}
8 changes: 3 additions & 5 deletions plugins/base/src/test/kotlin/model/JavaTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package model

import org.jetbrains.dokka.Platform
import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo
import org.jetbrains.dokka.links.DRI
import org.jetbrains.dokka.links.JavaClassReference
import org.jetbrains.dokka.links.PointingToDeclaration
import org.jetbrains.dokka.links.sureClassNames
import org.jetbrains.dokka.links.*
import org.jetbrains.dokka.model.*
import org.jetbrains.dokka.model.doc.Param
import org.jetbrains.dokka.model.doc.Text
Expand Down Expand Up @@ -369,7 +366,8 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
"java.lang.annotation",
"RetentionPolicy",
DRICallable("RUNTIME", null, emptyList()),
PointingToDeclaration
PointingToDeclaration,
DRIExtraContainer().also { it[EnumEntryDRIExtra] = EnumEntryDRIExtra }.encode()
)
)

Expand Down
Loading

0 comments on commit c420522

Please sign in to comment.