aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/plugin-kotlin-as-java
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-subprojects/plugin-kotlin-as-java')
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/README.md15
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/api/plugin-kotlin-as-java.api103
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/build.gradle.kts33
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/CollectionExtensions.kt16
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/KotlinAsJavaPlugin.kt55
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinCompanion.kt65
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverter.kt508
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmField.kt16
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmName.kt23
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmOverloads.kt18
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmStatic.kt16
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmSynthetic.kt17
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureProvider.kt227
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureUtils.kt44
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformToJava.kt42
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameDocumentableTransformer.kt116
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider.kt37
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/KotlinAsJavaDocumentableTransformer.kt19
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/withCallableName.kt9
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaDocumentableToPageTranslator.kt35
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaPageCreator.kt33
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin5
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt548
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt129
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt170
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt190
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt60
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt71
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt618
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt137
30 files changed, 3375 insertions, 0 deletions
diff --git a/dokka-subprojects/plugin-kotlin-as-java/README.md b/dokka-subprojects/plugin-kotlin-as-java/README.md
new file mode 100644
index 00000000..e33bd1bb
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/README.md
@@ -0,0 +1,15 @@
+# Kotlin as Java plugin
+
+With Kotlin as Java plugin applied, all Kotlin signatures will be rendered as Java signatures.
+
+For instance, `fun foo(bar: Bar): Baz` will be rendered as `public final Baz foo(Bar bar)`.
+
+The Kotlin as Java plugin is published to maven central as a
+[separate artifact](https://mvnrepository.com/artifact/org.jetbrains.dokka/kotlin-as-java-plugin):
+
+```text
+org.jetbrains.dokka:kotlin-as-java-plugin:1.9.10
+```
+
+**This plugin is at its early stages**, so you may experience issues and encounter bugs. Feel free to
+[report](https://github.com/Kotlin/dokka/issues/new/choose) any errors you see.
diff --git a/dokka-subprojects/plugin-kotlin-as-java/api/plugin-kotlin-as-java.api b/dokka-subprojects/plugin-kotlin-as-java/api/plugin-kotlin-as-java.api
new file mode 100644
index 00000000..e7766e36
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/api/plugin-kotlin-as-java.api
@@ -0,0 +1,103 @@
+public final class org/jetbrains/dokka/kotlinAsJava/KotlinAsJavaPlugin : org/jetbrains/dokka/plugability/DokkaPlugin {
+ public fun <init> ()V
+ public final fun getJavaSignatureProvider ()Lorg/jetbrains/dokka/plugability/Extension;
+ public final fun getJvmNameTransformer ()Lorg/jetbrains/dokka/plugability/Extension;
+ public final fun getKotlinAsJavaDocumentableToPageTranslator ()Lorg/jetbrains/dokka/plugability/Extension;
+ public final fun getKotlinAsJavaDocumentableTransformer ()Lorg/jetbrains/dokka/plugability/Extension;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/TransformToJavaKt {
+ public static final fun transformToJava (Lorg/jetbrains/dokka/model/DClasslike;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DClasslike;
+ public static final fun transformToJava (Lorg/jetbrains/dokka/model/DFunction;Lorg/jetbrains/dokka/plugability/DokkaContext;Ljava/lang/String;Z)Ljava/util/List;
+ public static final fun transformToJava (Lorg/jetbrains/dokka/model/DPackage;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DPackage;
+ public static final fun transformToJava (Lorg/jetbrains/dokka/model/DProperty;Lorg/jetbrains/dokka/plugability/DokkaContext;ZLjava/lang/String;)Lorg/jetbrains/dokka/model/DProperty;
+ public static synthetic fun transformToJava$default (Lorg/jetbrains/dokka/model/DFunction;Lorg/jetbrains/dokka/plugability/DokkaContext;Ljava/lang/String;ZILjava/lang/Object;)Ljava/util/List;
+ public static synthetic fun transformToJava$default (Lorg/jetbrains/dokka/model/DProperty;Lorg/jetbrains/dokka/plugability/DokkaContext;ZLjava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/dokka/model/DProperty;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverter {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverterKt {
+ public static final fun getJvmNameProvider ()Lorg/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureProvider : org/jetbrains/dokka/base/signatures/JvmSignatureUtils, org/jetbrains/dokka/base/signatures/SignatureProvider {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun annotations (Lorg/jetbrains/dokka/model/DProperty;)Ljava/util/Map;
+ public fun annotations (Lorg/jetbrains/dokka/model/Documentable;)Ljava/util/Map;
+ public fun annotations (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;)Ljava/util/Map;
+ public fun annotationsBlock (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;)V
+ public fun annotationsBlockWithIgnored (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;Ljava/util/Set;Lorg/jetbrains/dokka/base/signatures/AtStrategy;Lkotlin/Pair;Ljava/lang/String;)V
+ public fun annotationsInline (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;)V
+ public fun annotationsInlineWithIgnored (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;Ljava/util/Set;Lorg/jetbrains/dokka/base/signatures/AtStrategy;Lkotlin/Pair;Ljava/lang/String;)V
+ public fun modifiers (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;)Ljava/util/Map;
+ public fun parametersBlock (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/DFunction;Lkotlin/jvm/functions/Function2;)V
+ public fun plus (Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
+ public fun signature (Lorg/jetbrains/dokka/model/Documentable;)Ljava/util/List;
+ public fun stylesIfDeprecated (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Ljava/util/Set;
+ public fun toSignatureString (Ljava/util/Collection;)Ljava/lang/String;
+ public fun toSignatureString (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/Annotations$Annotation;Lorg/jetbrains/dokka/base/signatures/AtStrategy;Lkotlin/Pair;Ljava/lang/String;)V
+ public fun uses (Lorg/jetbrains/dokka/model/DFunction;Lorg/jetbrains/dokka/model/DTypeParameter;)Z
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureUtils : org/jetbrains/dokka/base/signatures/JvmSignatureUtils {
+ public static final field INSTANCE Lorg/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureUtils;
+ public fun annotations (Lorg/jetbrains/dokka/model/DProperty;)Ljava/util/Map;
+ public fun annotations (Lorg/jetbrains/dokka/model/Documentable;)Ljava/util/Map;
+ public fun annotations (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;)Ljava/util/Map;
+ public fun annotationsBlock (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;)V
+ public fun annotationsBlockWithIgnored (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;Ljava/util/Set;Lorg/jetbrains/dokka/base/signatures/AtStrategy;Lkotlin/Pair;Ljava/lang/String;)V
+ public fun annotationsInline (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;)V
+ public fun annotationsInlineWithIgnored (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/AnnotationTarget;Ljava/util/Set;Lorg/jetbrains/dokka/base/signatures/AtStrategy;Lkotlin/Pair;Ljava/lang/String;)V
+ public fun modifiers (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;)Ljava/util/Map;
+ public fun parametersBlock (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/DFunction;Lkotlin/jvm/functions/Function2;)V
+ public fun plus (Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
+ public fun stylesIfDeprecated (Lorg/jetbrains/dokka/model/properties/WithExtraProperties;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Ljava/util/Set;
+ public fun toSignatureString (Ljava/util/Collection;)Ljava/lang/String;
+ public fun toSignatureString (Lorg/jetbrains/dokka/base/translators/documentables/PageContentBuilder$DocumentableContentBuilder;Lorg/jetbrains/dokka/model/Annotations$Annotation;Lorg/jetbrains/dokka/base/signatures/AtStrategy;Lkotlin/Pair;Ljava/lang/String;)V
+ public fun uses (Lorg/jetbrains/dokka/model/DFunction;Lorg/jetbrains/dokka/model/DTypeParameter;)Z
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameDocumentableTransformer : org/jetbrains/dokka/transformers/documentation/DocumentableTransformer {
+ public fun <init> ()V
+ public fun invoke (Lorg/jetbrains/dokka/model/DModule;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DModule;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider {
+ public fun <init> ()V
+ public final fun nameFor (Lorg/jetbrains/dokka/model/Documentable;)Ljava/lang/String;
+ public final fun nameForGetter (Lorg/jetbrains/dokka/model/DProperty;)Ljava/lang/String;
+ public final fun nameForSetter (Lorg/jetbrains/dokka/model/DProperty;)Ljava/lang/String;
+ public final fun nameForSyntheticClass (Lorg/jetbrains/dokka/model/Documentable;)Lorg/jetbrains/dokka/kotlinAsJava/transformers/Name;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/transformers/KotlinAsJavaDocumentableTransformer : org/jetbrains/dokka/transformers/documentation/DocumentableTransformer {
+ public fun <init> ()V
+ public fun invoke (Lorg/jetbrains/dokka/model/DModule;Lorg/jetbrains/dokka/plugability/DokkaContext;)Lorg/jetbrains/dokka/model/DModule;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/transformers/Name {
+ public fun <init> (Ljava/lang/String;)V
+ public final fun component1 ()Ljava/lang/String;
+ public final fun copy (Ljava/lang/String;)Lorg/jetbrains/dokka/kotlinAsJava/transformers/Name;
+ public static synthetic fun copy$default (Lorg/jetbrains/dokka/kotlinAsJava/transformers/Name;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/dokka/kotlinAsJava/transformers/Name;
+ public fun equals (Ljava/lang/Object;)Z
+ public final fun getFqName ()Ljava/lang/String;
+ public final fun getName ()Ljava/lang/String;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaDocumentableToPageTranslator : org/jetbrains/dokka/transformers/documentation/DocumentableToPageTranslator {
+ public fun <init> (Lorg/jetbrains/dokka/plugability/DokkaContext;)V
+ public fun invoke (Lorg/jetbrains/dokka/model/DModule;)Lorg/jetbrains/dokka/pages/ModulePageNode;
+ public synthetic fun invoke (Lorg/jetbrains/dokka/model/DModule;)Lorg/jetbrains/dokka/pages/RootPageNode;
+}
+
+public final class org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaPageCreator : org/jetbrains/dokka/base/translators/documentables/DefaultPageCreator {
+ public fun <init> (Lorg/jetbrains/dokka/base/DokkaBaseConfiguration;Lorg/jetbrains/dokka/base/transformers/pages/comments/CommentsToContentConverter;Lorg/jetbrains/dokka/base/signatures/SignatureProvider;Lorg/jetbrains/dokka/utilities/DokkaLogger;Ljava/util/List;Lorg/jetbrains/dokka/analysis/kotlin/internal/DocumentableSourceLanguageParser;)V
+ public fun pageForProperty (Lorg/jetbrains/dokka/model/DProperty;)Lorg/jetbrains/dokka/pages/MemberPageNode;
+}
+
diff --git a/dokka-subprojects/plugin-kotlin-as-java/build.gradle.kts b/dokka-subprojects/plugin-kotlin-as-java/build.gradle.kts
new file mode 100644
index 00000000..4fdd5c12
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/build.gradle.kts
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import dokkabuild.overridePublicationArtifactId
+
+plugins {
+ id("dokkabuild.kotlin-jvm")
+ id("dokkabuild.publish-jvm")
+ id("dokkabuild.test-k2")
+}
+
+overridePublicationArtifactId("kotlin-as-java-plugin")
+
+dependencies {
+ compileOnly(projects.dokkaSubprojects.dokkaCore)
+ compileOnly(projects.dokkaSubprojects.analysisKotlinApi)
+
+ implementation(projects.dokkaSubprojects.pluginBase)
+
+ implementation(kotlin("reflect"))
+
+ testImplementation(kotlin("test"))
+ testImplementation(libs.jsoup)
+ testImplementation(projects.dokkaSubprojects.pluginBase)
+ symbolsTestConfiguration(project(path = ":dokka-subprojects:analysis-kotlin-symbols", configuration = "shadow"))
+ descriptorsTestConfiguration(project(path = ":dokka-subprojects:analysis-kotlin-descriptors", configuration = "shadow"))
+ testImplementation(projects.dokkaSubprojects.pluginBaseTestUtils) {
+ exclude(module = "analysis-kotlin-descriptors")
+ }
+ testImplementation(projects.dokkaSubprojects.coreContentMatcherTestUtils)
+ testImplementation(projects.dokkaSubprojects.coreTestApi)
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/CollectionExtensions.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/CollectionExtensions.kt
new file mode 100644
index 00000000..3eab0aeb
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/CollectionExtensions.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+// TODO [beresnev] remove this copy-paste and use the same method from stdlib instead after updating to 1.5
+internal inline fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R? {
+ for (element in this) {
+ val result = transform(element)
+ if (result != null) {
+ return result
+ }
+ }
+ return null
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/KotlinAsJavaPlugin.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/KotlinAsJavaPlugin.kt
new file mode 100644
index 00000000..36da34dc
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/KotlinAsJavaPlugin.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.signatures.SignatureProvider
+import org.jetbrains.dokka.kotlinAsJava.signatures.JavaSignatureProvider
+import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameDocumentableTransformer
+import org.jetbrains.dokka.kotlinAsJava.transformers.KotlinAsJavaDocumentableTransformer
+import org.jetbrains.dokka.kotlinAsJava.translators.KotlinAsJavaDocumentableToPageTranslator
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
+import org.jetbrains.dokka.plugability.Extension
+import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
+import org.jetbrains.dokka.renderers.PostAction
+import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator
+import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer
+
+public class KotlinAsJavaPlugin : DokkaPlugin() {
+ public val kotlinAsJavaDocumentableTransformer: Extension<DocumentableTransformer, *, *> by extending {
+ CoreExtensions.documentableTransformer with KotlinAsJavaDocumentableTransformer()
+ }
+
+ public val jvmNameTransformer: Extension<DocumentableTransformer, *, *> by extending {
+ CoreExtensions.documentableTransformer with JvmNameDocumentableTransformer() order {
+ after(kotlinAsJavaDocumentableTransformer)
+ }
+ }
+
+ public val javaSignatureProvider: Extension<SignatureProvider, *, *> by extending {
+ with(plugin<DokkaBase>()) {
+ signatureProvider providing ::JavaSignatureProvider override kotlinSignatureProvider
+ }
+ }
+
+ public val kotlinAsJavaDocumentableToPageTranslator: Extension<DocumentableToPageTranslator, *, *> by extending {
+ CoreExtensions.documentableToPageTranslator providing ::KotlinAsJavaDocumentableToPageTranslator override
+ plugin<DokkaBase>().documentableToPageTranslator
+ }
+
+ internal val alphaVersionNotifier by extending {
+ CoreExtensions.postActions providing { ctx ->
+ PostAction {
+ ctx.logger.info("KotlinAsJava plugin is in Alpha version, use at your own risk, expect bugs and migration issues")
+ }
+ }
+ }
+
+ @OptIn(DokkaPluginApiPreview::class)
+ override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement =
+ PluginApiPreviewAcknowledgement
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinCompanion.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinCompanion.kt
new file mode 100644
index 00000000..260fc25d
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinCompanion.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.converters
+
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.PropertyContainer
+
+private const val DEFAULT_COMPANION_NAME = "Companion"
+
+internal fun DObject?.staticFunctionsForJava(): List<DFunction> {
+ if (this == null) return emptyList()
+ return functions.filter { it.isJvmStatic }
+}
+
+/**
+ * @return properties that will be visible as static for java.
+ * See [Static fields](https://kotlinlang.org/docs/java-to-kotlin-interop.html#static-fields)
+ */
+internal fun DObject?.staticPropertiesForJava(): List<DProperty> {
+ if (this == null) return emptyList()
+ return properties.filter { it.isJvmField || it.isConst || it.isLateInit }
+}
+
+internal fun DObject.companionInstancePropertyForJava(): DProperty? {
+ if (hasNothingToRender()) return null // do not show if companion not rendered
+
+ return DProperty(
+ name = name ?: DEFAULT_COMPANION_NAME,
+ modifier = sourceSets.associateWith { JavaModifier.Final },
+ dri = dri.copy(callable = Callable(name ?: DEFAULT_COMPANION_NAME, null, emptyList())),
+ documentation = emptyMap(),
+ sources = emptyMap(),
+ visibility = sourceSets.associateWith {
+ JavaVisibility.Public
+ },
+ type = GenericTypeConstructor(dri, emptyList()),
+ setter = null,
+ getter = null,
+ sourceSets = sourceSets,
+ receiver = null,
+ generics = emptyList(),
+ expectPresentInSet = expectPresentInSet,
+ isExpectActual = false,
+ extra = PropertyContainer.withAll(sourceSets.map {
+ mapOf(it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)).toAdditionalModifiers()
+ })
+ )
+}
+
+/**
+ * Hide companion object if there isn't members of parents.
+ * Properties and functions that are moved to outer class are not counted as members.
+ */
+internal fun DObject.hasNothingToRender(): Boolean {
+ val nonStaticPropsCount = properties.size - staticPropertiesForJava().size
+ val nonStaticFunctionsCount = functions.size - staticFunctionsForJava().size
+ val classLikesCount = classlikes.size
+ val superTypesCount = supertypes.values.firstOrNull()?.size ?: 0
+
+ return nonStaticFunctionsCount + nonStaticPropsCount +
+ classLikesCount + superTypesCount == 0
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverter.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverter.kt
new file mode 100644
index 00000000..a8b3a86c
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/converters/KotlinToJavaConverter.kt
@@ -0,0 +1,508 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.converters
+
+import org.jetbrains.dokka.kotlinAsJava.*
+import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameProvider
+import org.jetbrains.dokka.kotlinAsJava.transformers.withCallableName
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.withClass
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
+
+public val jvmNameProvider: JvmNameProvider = JvmNameProvider()
+internal const val OBJECT_INSTANCE_NAME = "INSTANCE"
+
+internal val DProperty.isConst: Boolean
+ get() = hasModifier(ExtraModifiers.KotlinOnlyModifiers.Const)
+
+internal val DProperty.isLateInit: Boolean
+ get() = hasModifier(ExtraModifiers.KotlinOnlyModifiers.LateInit)
+
+internal val DProperty.isJvmField: Boolean
+ get() = jvmField() != null
+
+internal val DFunction.isJvmStatic: Boolean
+ get() = jvmStatic() != null
+
+private fun DProperty.hasModifier(modifier: ExtraModifiers.KotlinOnlyModifiers): Boolean =
+ extra[AdditionalModifiers]
+ ?.content
+ ?.any { (_, modifiers) -> modifier in modifiers } == true
+
+public class KotlinToJavaConverter(
+ private val context: DokkaContext
+) {
+ private val kotlinToJavaMapper by lazy {
+ context.plugin<InternalKotlinAnalysisPlugin>().querySingle { kotlinToJavaService }
+ }
+
+ internal fun DPackage.asJava(): DPackage {
+ val syntheticClasses =
+ (properties.map { jvmNameProvider.nameForSyntheticClass(it) to it }
+ + functions.map { jvmNameProvider.nameForSyntheticClass(it) to it })
+ .groupBy({ it.first }) { it.second }
+ .map { (syntheticClassName, nodes) ->
+ DClass(
+ dri = dri.withClass(syntheticClassName.name),
+ name = syntheticClassName.name,
+ properties = nodes
+ .filterIsInstance<DProperty>()
+ .filterNot { it.hasJvmSynthetic() }
+ .map { it.asJava(true) },
+ constructors = emptyList(),
+ functions = (
+ nodes
+ .filterIsInstance<DProperty>()
+ .filterNot { it.isConst || it.isJvmField || it.hasJvmSynthetic() }
+ .flatMap { it.javaAccessors(relocateToClass = syntheticClassName.name) } +
+ nodes
+ .filterIsInstance<DFunction>()
+ .flatMap { it.asJava(syntheticClassName.name, true) })
+ .filterNot { it.hasJvmSynthetic() },
+ classlikes = emptyList(),
+ sources = emptyMap(),
+ expectPresentInSet = null,
+ visibility = sourceSets.associateWith {
+ JavaVisibility.Public
+ },
+ companion = null,
+ generics = emptyList(),
+ supertypes = emptyMap(),
+ documentation = emptyMap(),
+ modifier = sourceSets.associateWith { JavaModifier.Final },
+ sourceSets = sourceSets,
+ isExpectActual = false,
+ extra = PropertyContainer.empty()
+ )
+ }
+
+ return copy(
+ functions = emptyList(),
+ properties = emptyList(),
+ classlikes = classlikes.map { it.asJava() } + syntheticClasses,
+ typealiases = emptyList()
+ )
+ }
+
+ internal fun DProperty.asJava(
+ isTopLevel: Boolean = false,
+ relocateToClass: String? = null,
+ isFromObjectOrCompanion: Boolean = false
+ ) =
+ copy(
+ dri = if (relocateToClass.isNullOrBlank()) {
+ dri
+ } else {
+ dri.withClass(relocateToClass)
+ },
+ modifier = javaModifierFromSetter(),
+ visibility = visibility.mapValues {
+ if (isConst || isJvmField || (getter == null && setter == null) || (isFromObjectOrCompanion && isLateInit)) {
+ it.value.asJava()
+ } else {
+ it.value.propertyVisibilityAsJava()
+ }
+ },
+ type = type.asJava(), // TODO: check
+ setter = null,
+ getter = null, // Removing getters and setters as they will be available as functions
+ extra = if (isTopLevel || isConst || (isFromObjectOrCompanion && isJvmField) || (isFromObjectOrCompanion && isLateInit))
+ extra + extra.mergeAdditionalModifiers(
+ sourceSets.associateWith {
+ setOf(ExtraModifiers.JavaOnlyModifiers.Static)
+ }
+ )
+ else extra
+ )
+
+ internal fun Visibility.asJava() =
+ when (this) {
+ is JavaVisibility -> this
+ is KotlinVisibility.Public, KotlinVisibility.Internal -> JavaVisibility.Public
+ is KotlinVisibility.Private -> JavaVisibility.Private
+ is KotlinVisibility.Protected -> JavaVisibility.Protected
+ }
+
+ internal fun DProperty.javaModifierFromSetter() =
+ modifier.mapValues {
+ when {
+ it.value is JavaModifier -> it.value
+ setter == null -> JavaModifier.Final
+ else -> JavaModifier.Empty
+ }
+ }
+
+ internal fun DProperty.javaAccessors(
+ isTopLevel: Boolean = false,
+ relocateToClass: String? = null
+ ): List<DFunction> =
+ listOfNotNull(
+ getter?.let { getter ->
+ val name = "get" + name.capitalize()
+ getter.copy(
+ dri = if (relocateToClass.isNullOrBlank()) {
+ getter.dri
+ } else {
+ getter.dri.withClass(relocateToClass)
+ }.withCallableName(name),
+ name = name,
+ modifier = javaModifierFromSetter(),
+ visibility = visibility.mapValues { JavaVisibility.Public },
+ type = getter.type.asJava(),
+ extra = if (isTopLevel) getter.extra +
+ getter.extra.mergeAdditionalModifiers(
+ sourceSets.associateWith {
+ setOf(ExtraModifiers.JavaOnlyModifiers.Static)
+ }
+ )
+ else getter.extra
+ )
+ },
+ setter?.let { setter ->
+ val name = "set" + name.capitalize()
+ val baseDRI = (if (relocateToClass.isNullOrBlank()) {
+ setter.dri
+ } else {
+ setter.dri.withClass(relocateToClass)
+ }).withCallableName(name)
+ setter.copy(
+ dri = baseDRI,
+ name = name,
+ parameters = setter.parameters.map {
+ it.copy(
+ dri = baseDRI.copy(
+ target = it.dri.target,
+ extra = it.dri.extra
+ ), type = it.type.asJava()
+ )
+ },
+ modifier = javaModifierFromSetter(),
+ visibility = visibility.mapValues { JavaVisibility.Public },
+ type = Void,
+ extra = if (isTopLevel) setter.extra + setter.extra.mergeAdditionalModifiers(
+ sourceSets.associateWith {
+ setOf(ExtraModifiers.JavaOnlyModifiers.Static)
+ }
+ )
+ else setter.extra
+ )
+ }
+ )
+
+ private fun DFunction.asJava(
+ containingClassName: String,
+ newName: String,
+ parameters: List<DParameter>,
+ isTopLevel: Boolean = false
+ ): DFunction {
+ return copy(
+ dri = dri.copy(classNames = containingClassName, callable = dri.callable?.copy(name = newName)),
+ name = newName,
+ type = type.asJava(),
+ modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Final } && isConstructor)
+ sourceSets.associateWith { JavaModifier.Empty }
+ else sourceSets.associateWith { modifier.values.first() },
+ parameters = listOfNotNull(receiver?.asJava()) + parameters.map { it.asJava() },
+ visibility = visibility.map { (sourceSet, visibility) -> Pair(sourceSet, visibility.asJava()) }.toMap(),
+ receiver = null,
+ extra = if (isTopLevel || isJvmStatic) {
+ extra + extra.mergeAdditionalModifiers(
+ sourceSets.associateWith {
+ setOf(ExtraModifiers.JavaOnlyModifiers.Static)
+ }
+ )
+ } else {
+ extra
+ }
+ )
+ }
+
+ private fun DFunction.withJvmOverloads(
+ containingClassName: String,
+ newName: String,
+ isTopLevel: Boolean = false
+ ): List<DFunction>? {
+ val (paramsWithDefaults, paramsWithoutDefaults) = parameters
+ .withIndex()
+ .partition { (_, p) -> p.extra[DefaultValue] != null }
+ return paramsWithDefaults
+ .runningFold(paramsWithoutDefaults) { acc, param -> (acc + param) }
+ .map { params ->
+ asJava(
+ containingClassName,
+ newName,
+ params
+ .sortedBy(IndexedValue<DParameter>::index)
+ .map { it.value },
+ isTopLevel
+ )
+ }
+ .reversed()
+ .takeIf { it.isNotEmpty() }
+ }
+
+ internal fun DFunction.asJava(containingClassName: String, isTopLevel: Boolean = false): List<DFunction> {
+ val newName = when {
+ isConstructor -> containingClassName
+ else -> name
+ }
+ val baseFunction = asJava(containingClassName, newName, parameters, isTopLevel)
+ return if (hasJvmOverloads()) {
+ withJvmOverloads(containingClassName, newName, isTopLevel) ?: listOf(baseFunction)
+ } else {
+ listOf(baseFunction)
+ }
+ }
+
+ internal fun DClasslike.asJava(): DClasslike = when (this) {
+ is DClass -> asJava()
+ is DEnum -> asJava()
+ is DAnnotation -> asJava()
+ is DObject -> asJava()
+ is DInterface -> asJava()
+ else -> throw IllegalArgumentException("$this shouldn't be here")
+ }
+
+ internal fun DClass.asJava(): DClass = copy(
+ constructors = constructors
+ .filterNot { it.hasJvmSynthetic() }
+ .flatMap {
+ it.asJava(
+ dri.classNames ?: name
+ )
+ }, // name may not always be valid here, however classNames should always be not null
+ functions = functionsInJava(),
+ properties = propertiesInJava(),
+ classlikes = classlikesInJava(),
+ generics = generics.map { it.asJava() },
+ companion = companion?.companionAsJava(),
+ supertypes = supertypes.mapValues { it.value.map { it.asJava() } },
+ modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Empty }) sourceSets.associateWith { JavaModifier.Final }
+ else sourceSets.associateWith { modifier.values.first() }
+ )
+
+ /**
+ * Companion objects requires some custom logic for rendering as Java.
+ * They are excluded from usual classlikes rendering and added after.
+ */
+ internal fun DClass.classlikesInJava(): List<DClasslike> {
+ val classlikes = classlikes
+ .filter { it.name != companion?.name }
+ .map { it.asJava() }
+
+ val companionAsJava = companion?.companionAsJava()
+ return if (companionAsJava != null) classlikes.plus(companionAsJava) else classlikes
+ }
+
+
+ internal fun DClass.functionsInJava(): List<DFunction> =
+ properties
+ .filter { !it.isJvmField && !it.hasJvmSynthetic() }
+ .flatMap { property -> listOfNotNull(property.getter, property.setter) }
+ .plus(functions)
+ .plus(companion.staticFunctionsForJava())
+ .filterNot { it.hasJvmSynthetic() }
+ .flatMap { it.asJava(it.dri.classNames ?: it.name) }
+
+ internal fun DClass.propertiesInJava(): List<DProperty> {
+ val propertiesFromCompanion = companion
+ .staticPropertiesForJava()
+ .filterNot { it.hasJvmSynthetic() }
+ .map { it.asJava(isFromObjectOrCompanion = true) }
+ val companionInstanceProperty = companion?.companionInstancePropertyForJava()
+ val ownProperties = properties
+ .filterNot { it.hasJvmSynthetic() }
+ .map { it.asJava() }
+
+ return propertiesFromCompanion + ownProperties + listOfNotNull(companionInstanceProperty)
+ }
+
+ private fun DTypeParameter.asJava(): DTypeParameter = copy(
+ variantTypeParameter = variantTypeParameter.withDri(dri.possiblyAsJava()),
+ bounds = bounds.map { it.asJava() }
+ )
+
+ private fun Projection.asJava(): Projection = when (this) {
+ is Star -> Star
+ is Covariance<*> -> copy(inner.asJava())
+ is Contravariance<*> -> copy(inner.asJava())
+ is Invariance<*> -> copy(inner.asJava())
+ is Bound -> asJava()
+ }
+
+ private fun Bound.asJava(): Bound = when (this) {
+ is TypeParameter -> copy(dri.possiblyAsJava())
+ is GenericTypeConstructor -> copy(
+ dri = dri.possiblyAsJava(),
+ projections = projections.map { it.asJava() }
+ )
+
+ is FunctionalTypeConstructor -> copy(
+ dri = dri.possiblyAsJava(),
+ projections = projections.map { it.asJava() }
+ )
+
+ is TypeAliased -> copy(
+ typeAlias = typeAlias.asJava(),
+ inner = inner.asJava()
+ )
+
+ is Nullable -> copy(inner.asJava())
+ is DefinitelyNonNullable -> copy(inner.asJava())
+ is PrimitiveJavaType -> this
+ is Void -> this
+ is JavaObject -> this
+ is Dynamic -> this
+ is UnresolvedBound -> this
+ }
+
+ internal fun DEnum.asJava(): DEnum = copy(
+ constructors = constructors.flatMap { it.asJava(dri.classNames ?: name) },
+ functions = functions
+ .plus(
+ properties
+ .filter { !it.isJvmField && !it.hasJvmSynthetic() }
+ .flatMap { listOf(it.getter, it.setter) }
+ )
+ .filterNotNull()
+ .filterNot { it.hasJvmSynthetic() }
+ .flatMap { it.asJava(dri.classNames ?: name) },
+ properties = properties
+ .filterNot { it.hasJvmSynthetic() }
+ .map { it.asJava() },
+ classlikes = classlikes.map { it.asJava() },
+ supertypes = supertypes.mapValues { it.value.map { it.asJava() } }
+// , entries = entries.map { it.asJava() }
+ )
+
+ /**
+ * Parameters [excludedProps] and [excludedFunctions] used for rendering companion objects
+ * where some members (that lifted to outer class) are not rendered
+ */
+ internal fun DObject.asJava(
+ excludedProps: List<DProperty> = emptyList(),
+ excludedFunctions: List<DFunction> = emptyList()
+ ): DObject = copy(
+ functions = functions
+ .plus(
+ properties
+ .filterNot { it in excludedProps }
+ .filter { !it.isJvmField && !it.isConst && !it.isLateInit && !it.hasJvmSynthetic() }
+ .flatMap { listOf(it.getter, it.setter) }
+ )
+ .filterNotNull()
+ .filterNot { it in excludedFunctions }
+ .filterNot { it.hasJvmSynthetic() }
+ .flatMap { it.asJava(dri.classNames ?: name.orEmpty()) },
+ properties = properties
+ .filterNot { it.hasJvmSynthetic() }
+ .filterNot { it in excludedProps }
+ .map { it.asJava(isFromObjectOrCompanion = true) } +
+ DProperty(
+ name = OBJECT_INSTANCE_NAME,
+ modifier = sourceSets.associateWith { JavaModifier.Final },
+ dri = dri.copy(callable = Callable(OBJECT_INSTANCE_NAME, null, emptyList())),
+ documentation = emptyMap(),
+ sources = emptyMap(),
+ visibility = sourceSets.associateWith {
+ JavaVisibility.Public
+ },
+ type = GenericTypeConstructor(dri, emptyList()),
+ setter = null,
+ getter = null,
+ sourceSets = sourceSets,
+ receiver = null,
+ generics = emptyList(),
+ expectPresentInSet = expectPresentInSet,
+ isExpectActual = false,
+ extra = PropertyContainer.withAll(sourceSets.map {
+ mapOf(it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)).toAdditionalModifiers()
+ })
+ ),
+ classlikes = classlikes.map { it.asJava() },
+ supertypes = supertypes.mapValues { it.value.map { it.asJava() } }
+ )
+
+ internal fun DInterface.asJava(): DInterface = copy(
+ functions = functions
+ .plus(
+ properties
+ .filter { it.jvmField() == null && !it.hasJvmSynthetic() }
+ .flatMap { listOf(it.getter, it.setter) }
+ )
+ .filterNotNull()
+ .filterNot { it.hasJvmSynthetic() }
+ .flatMap { it.asJava(dri.classNames ?: name) },
+ properties = emptyList(),
+ classlikes = classlikes.map { it.asJava() }, // TODO: public static final class DefaultImpls with impls for methods
+ generics = generics.map { it.asJava() },
+ supertypes = supertypes.mapValues { it.value.map { it.asJava() } }
+ )
+
+ internal fun DAnnotation.asJava(): DAnnotation = copy(
+ properties = properties.map { it.asJava() },
+ constructors = emptyList(),
+ classlikes = classlikes.map { it.asJava() }
+ ) // TODO investigate if annotation class can have methods and properties not from constructor
+
+ internal fun DParameter.asJava(): DParameter = copy(
+ type = type.asJava(),
+ name = if (name.isNullOrBlank()) "\$self" else name
+ )
+
+ internal fun Visibility.propertyVisibilityAsJava(): Visibility =
+ if (this is JavaVisibility) this
+ else JavaVisibility.Private
+
+ private fun TypeConstructor.possiblyAsJava(): TypeConstructor = when (this) {
+ is GenericTypeConstructor -> copy(dri = this.dri.possiblyAsJava())
+ is FunctionalTypeConstructor -> copy(dri = this.dri.possiblyAsJava())
+ }
+
+
+ internal fun TypeConstructorWithKind.asJava(): TypeConstructorWithKind =
+ TypeConstructorWithKind(
+ typeConstructor = typeConstructor.possiblyAsJava(),
+ kind = kind.asJava()
+ )
+
+ internal fun ClassKind.asJava(): ClassKind {
+ return when (this) {
+ is JavaClassKindTypes -> this
+ KotlinClassKindTypes.CLASS -> JavaClassKindTypes.CLASS
+ KotlinClassKindTypes.INTERFACE -> JavaClassKindTypes.INTERFACE
+ KotlinClassKindTypes.ENUM_CLASS -> JavaClassKindTypes.ENUM_CLASS
+ KotlinClassKindTypes.ENUM_ENTRY -> JavaClassKindTypes.ENUM_ENTRY
+ KotlinClassKindTypes.ANNOTATION_CLASS -> JavaClassKindTypes.ANNOTATION_CLASS
+ KotlinClassKindTypes.OBJECT -> JavaClassKindTypes.CLASS
+ else -> throw IllegalStateException("Non exchaustive match while trying to convert $this to Java")
+ }
+ }
+
+ private fun <T : Documentable> PropertyContainer<T>.mergeAdditionalModifiers(second: SourceSetDependent<Set<ExtraModifiers>>) =
+ this[AdditionalModifiers]?.squash(AdditionalModifiers(second)) ?: AdditionalModifiers(second)
+
+ private fun AdditionalModifiers.squash(second: AdditionalModifiers) =
+ AdditionalModifiers(content + second.content)
+
+ internal fun DObject.companionAsJava(): DObject? {
+ if (hasNothingToRender()) return null
+
+ return asJava(
+ excludedProps = staticPropertiesForJava(),
+ excludedFunctions = staticFunctionsForJava()
+ )
+ }
+
+ private fun DRI.possiblyAsJava(): DRI {
+ return kotlinToJavaMapper.findAsJava(this) ?: this
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmField.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmField.kt
new file mode 100644
index 00000000..3cf9b8fa
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmField.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+internal fun <T : Documentable> WithExtraProperties<T>.jvmField(): Annotations.Annotation? =
+ extra[Annotations]?.directAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations) -> annotations.jvmFieldAnnotation() }
+
+internal fun List<Annotations.Annotation>.jvmFieldAnnotation(): Annotations.Annotation? =
+ firstOrNull { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmField" }
+
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmName.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmName.kt
new file mode 100644
index 00000000..6561f079
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmName.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.StringValue
+import org.jetbrains.dokka.model.isJvmName
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+internal fun <T : Documentable> WithExtraProperties<T>.directlyAnnotatedJvmName(): Annotations.Annotation? =
+ extra[Annotations]?.directAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations)-> annotations.jvmNameAnnotation() }
+
+internal fun <T : Documentable> WithExtraProperties<T>.fileLevelJvmName(): Annotations.Annotation? =
+ extra[Annotations]?.fileLevelAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations) -> annotations.jvmNameAnnotation() }
+
+internal fun List<Annotations.Annotation>.jvmNameAnnotation(): Annotations.Annotation? =
+ firstOrNull { it.isJvmName() }
+
+internal fun Annotations.Annotation.jvmNameAsString(): String? = (params["name"] as? StringValue)?.value
+
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmOverloads.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmOverloads.kt
new file mode 100644
index 00000000..08fbca07
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmOverloads.kt
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+internal fun <T : Documentable> WithExtraProperties<T>.hasJvmOverloads(): Boolean {
+ return extra[Annotations]
+ ?.directAnnotations
+ ?.entries
+ ?.any { (_, annotations) ->
+ annotations.any { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmOverloads" }
+ } == true
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmStatic.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmStatic.kt
new file mode 100644
index 00000000..a253dc83
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmStatic.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+internal fun <T : Documentable> WithExtraProperties<T>.jvmStatic(): Annotations.Annotation? =
+ extra[Annotations]?.directAnnotations?.entries?.firstNotNullOfOrNull { (_, annotations) -> annotations.jvmStaticAnnotation() }
+
+internal fun List<Annotations.Annotation>.jvmStaticAnnotation(): Annotations.Annotation? =
+ firstOrNull { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmStatic" }
+
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmSynthetic.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmSynthetic.kt
new file mode 100644
index 00000000..bf2a9ad1
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/jvmSynthetic.kt
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+internal fun <T : org.jetbrains.dokka.model.AnnotationTarget> WithExtraProperties<T>.hasJvmSynthetic(): Boolean {
+ return extra[Annotations]
+ ?.directAnnotations
+ ?.entries
+ ?.any { (_, annotations) ->
+ annotations.any { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmSynthetic" }
+ } == true
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureProvider.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureProvider.kt
new file mode 100644
index 00000000..e4c9d5dd
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureProvider.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.signatures
+
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.signatures.JvmSignatureUtils
+import org.jetbrains.dokka.base.signatures.SignatureProvider
+import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.pages.ContentKind
+import org.jetbrains.dokka.pages.ContentNode
+import org.jetbrains.dokka.pages.TextStyle
+import org.jetbrains.dokka.pages.TokenStyle
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.plugability.querySingle
+import org.jetbrains.dokka.utilities.DokkaLogger
+import kotlin.text.Typography.nbsp
+
+public class JavaSignatureProvider internal constructor(
+ ctcc: CommentsToContentConverter,
+ logger: DokkaLogger
+) : SignatureProvider, JvmSignatureUtils by JavaSignatureUtils {
+
+ public constructor(context: DokkaContext) : this(
+ context.plugin<DokkaBase>().querySingle { commentsToContentConverter },
+ context.logger
+ )
+
+ private val contentBuilder = PageContentBuilder(ctcc, this, logger)
+
+ private val ignoredVisibilities = setOf(JavaVisibility.Default)
+
+ private val ignoredModifiers =
+ setOf(KotlinModifier.Open, JavaModifier.Empty, KotlinModifier.Empty, KotlinModifier.Sealed)
+
+ override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) {
+ is DFunction -> signature(documentable)
+ is DProperty -> signature(documentable)
+ is DClasslike -> signature(documentable)
+ is DEnumEntry -> signature(documentable)
+ is DTypeParameter -> signature(documentable)
+ else -> throw NotImplementedError(
+ "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}"
+ )
+ }
+
+ private fun signature(e: DEnumEntry) =
+ e.sourceSets.map {
+ contentBuilder.contentFor(
+ e,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace),
+ sourceSets = setOf(it)
+ ) {
+ link(e.name, e.dri, styles = mainStyles + e.stylesIfDeprecated(it))
+ }
+ }
+
+ private fun signature(c: DClasslike) =
+ c.sourceSets.map { sourceSet ->
+ @Suppress("UNCHECKED_CAST")
+ val deprecationStyles = (c as? WithExtraProperties<out Documentable>)
+ ?.stylesIfDeprecated(sourceSet)
+ ?: emptySet()
+
+ contentBuilder.contentFor(
+ c,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace),
+ sourceSets = setOf(sourceSet)
+ ) {
+ annotationsBlock(c)
+ c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ")?.let { keyword(it) }
+
+ if (c is DClass) {
+ c.modifier[sourceSet]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ")?.let { keyword(it) }
+ c.modifiers()[sourceSet]?.toSignatureString()?.let { keyword(it) }
+ }
+
+ when (c) {
+ is DClass -> keyword("class ")
+ is DInterface -> keyword("interface ")
+ is DEnum -> keyword("enum ")
+ is DObject -> keyword("class ")
+ is DAnnotation -> keyword("@interface ")
+ }
+ link(c.name!!, c.dri, styles = mainStyles + deprecationStyles)
+ if (c is WithGenerics) {
+ list(c.generics, prefix = "<", suffix = ">",
+ separatorStyles = mainStyles + TokenStyle.Punctuation,
+ surroundingCharactersStyle = mainStyles + TokenStyle.Operator) {
+ +buildSignature(it)
+ }
+ }
+ if (c is WithSupertypes) {
+ c.supertypes.map { (p, dris) ->
+ val (classes, interfaces) = dris.partition { it.kind == JavaClassKindTypes.CLASS }
+ list(classes, prefix = " extends ", sourceSets = setOf(p),
+ separatorStyles = mainStyles + TokenStyle.Punctuation,
+ surroundingCharactersStyle = mainStyles + TokenStyle.Keyword) {
+ signatureForProjection(it.typeConstructor)
+ }
+ list(interfaces, prefix = " implements ", sourceSets = setOf(p),
+ separatorStyles = mainStyles + TokenStyle.Punctuation,
+ surroundingCharactersStyle = mainStyles + TokenStyle.Keyword) {
+ signatureForProjection(it.typeConstructor)
+ }
+ }
+ }
+ }
+ }
+
+ private fun signature(p: DProperty) =
+ p.sourceSets.map {
+ contentBuilder.contentFor(
+ p,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace, TextStyle.Block),
+ sourceSets = setOf(it)
+ ) {
+ annotationsBlock(p)
+ p.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") }
+ p.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.let { keyword("$it ") }
+ p.modifiers()[it]?.toSignatureString()?.let { keyword(it) }
+ signatureForProjection(p.type)
+ text(nbsp.toString())
+ link(p.name, p.dri, styles = mainStyles + p.stylesIfDeprecated(it))
+ }
+ }
+
+ private fun signature(f: DFunction) =
+ f.sourceSets.map { sourceSet ->
+ contentBuilder.contentFor(
+ f,
+ ContentKind.Symbol,
+ setOf(TextStyle.Monospace, TextStyle.Block),
+ sourceSets = setOf(sourceSet)
+ ) {
+ annotationsBlock(f)
+ f.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { keyword("$it ") }
+ f.modifier[sourceSet]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ")?.let { keyword(it) }
+ f.modifiers()[sourceSet]?.toSignatureString()?.let { keyword(it) }
+ val returnType = f.type
+ signatureForProjection(returnType)
+ text(nbsp.toString())
+ link(f.name, f.dri, styles = mainStyles + TokenStyle.Function + f.stylesIfDeprecated(sourceSet))
+ val usedGenerics = if (f.isConstructor) f.generics.filter { f uses it } else f.generics
+ list(usedGenerics, prefix = "<", suffix = ">",
+ separatorStyles = mainStyles + TokenStyle.Punctuation,
+ surroundingCharactersStyle = mainStyles + TokenStyle.Operator) {
+ +buildSignature(it)
+ }
+ punctuation("(")
+ if (f.parameters.isNotEmpty()) {
+ parametersBlock(f) {
+ annotationsInline(it)
+ text(it.modifiers()[sourceSet]?.toSignatureString() ?: "", styles = mainStyles + TokenStyle.Keyword)
+ signatureForProjection(it.type)
+ text(nbsp.toString())
+ text(it.name!!)
+ }
+ }
+ punctuation(")")
+ }
+ }
+
+ private fun signature(t: DTypeParameter) =
+ t.sourceSets.map {
+ contentBuilder.contentFor(t, sourceSets = setOf(it)) {
+ annotationsInline(t)
+ text(t.name.substringAfterLast("."), styles = mainStyles + t.stylesIfDeprecated(it))
+ list(
+ elements = t.bounds,
+ prefix = " extends ",
+ separatorStyles = mainStyles + TokenStyle.Punctuation,
+ surroundingCharactersStyle = mainStyles + TokenStyle.Keyword
+ ) {
+ signatureForProjection(it)
+ }
+ }
+
+ }
+
+ private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(p: Projection): Unit = when (p) {
+ is TypeParameter -> {
+ annotationsInline(p)
+ link(p.name, p.dri)
+ }
+
+ is TypeConstructor -> group(styles = emptySet()) {
+ annotationsInline(p)
+ link(p.dri.classNames.orEmpty(), p.dri)
+ list(p.projections, prefix = "<", suffix = ">",
+ separatorStyles = mainStyles + TokenStyle.Punctuation,
+ surroundingCharactersStyle = mainStyles + TokenStyle.Operator) {
+ signatureForProjection(it)
+ }
+ }
+
+ is Variance<*> -> group(styles = emptySet()) {
+ val variance = when(p) {
+ is Covariance<*> -> "? extends "
+ is Contravariance<*> -> "? super "
+ is Invariance<*> -> ""
+ }
+ keyword(variance)
+ signatureForProjection(p.inner)
+ }
+
+ is Star -> operator("?")
+
+ is Nullable -> signatureForProjection(p.inner)
+ is DefinitelyNonNullable -> signatureForProjection(p.inner)
+
+ is JavaObject, is Dynamic -> link("Object", DRI("java.lang", "Object"))
+ is Void -> text("void")
+ is PrimitiveJavaType -> text(p.name)
+ is UnresolvedBound -> text(p.name)
+ is TypeAliased -> signatureForProjection(p.inner)
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureUtils.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureUtils.kt
new file mode 100644
index 00000000..1738d40d
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/signatures/JavaSignatureUtils.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.signatures
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.signatures.All
+import org.jetbrains.dokka.base.signatures.JvmSignatureUtils
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.AnnotationTarget
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+public object JavaSignatureUtils : JvmSignatureUtils {
+
+ private val ignoredAnnotations = setOf(
+ Annotations.Annotation(DRI("kotlin.jvm", "Transient"), emptyMap()),
+ Annotations.Annotation(DRI("kotlin.jvm", "Volatile"), emptyMap()),
+ Annotations.Annotation(DRI("kotlin.jvm", "Transitive"), emptyMap()),
+ Annotations.Annotation(DRI("kotlin.jvm", "Strictfp"), emptyMap()),
+ Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap())
+ )
+
+ private val strategy = All
+ private val listBrackets = Pair('{', '}')
+ private val classExtension = ".class"
+
+ override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: AnnotationTarget) {
+ annotationsBlockWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension)
+ }
+
+ override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: AnnotationTarget) {
+ annotationsInlineWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension)
+ }
+
+ override fun <T : Documentable> WithExtraProperties<T>.modifiers(): Map<DokkaConfiguration.DokkaSourceSet, Set<ExtraModifiers.JavaOnlyModifiers>> {
+ return extra[AdditionalModifiers]?.content?.entries?.associate {
+ it.key to it.value.filterIsInstance<ExtraModifiers.JavaOnlyModifiers>().toSet()
+ } ?: emptyMap()
+ }
+
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformToJava.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformToJava.kt
new file mode 100644
index 00000000..32344dd9
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformToJava.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.kotlinAsJava.converters.KotlinToJavaConverter
+import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameDocumentableTransformer
+import org.jetbrains.dokka.model.DClasslike
+import org.jetbrains.dokka.model.DFunction
+import org.jetbrains.dokka.model.DPackage
+import org.jetbrains.dokka.model.DProperty
+import org.jetbrains.dokka.plugability.DokkaContext
+
+private val JVM_NAME_DOCUMENTABLE_TRANSFORMER by lazy {
+ JvmNameDocumentableTransformer()
+}
+
+public fun DPackage.transformToJava(context: DokkaContext): DPackage {
+ with(KotlinToJavaConverter(context)) {
+ return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this@transformToJava.asJava(), context)
+ }
+}
+
+public fun DClasslike.transformToJava(context: DokkaContext): DClasslike {
+ with(KotlinToJavaConverter(context)) {
+ return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this@transformToJava.asJava(), context)
+ }
+}
+
+public fun DFunction.transformToJava(context: DokkaContext, containingClassName: String, isTopLevel: Boolean = false): List<DFunction> {
+ with(KotlinToJavaConverter(context)) {
+ return this@transformToJava.asJava(containingClassName, isTopLevel)
+ .map { JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(it, context) }
+ }
+}
+
+public fun DProperty.transformToJava(context: DokkaContext, isTopLevel: Boolean = false, relocateToClass: String? = null): DProperty {
+ with(KotlinToJavaConverter(context)) {
+ return JVM_NAME_DOCUMENTABLE_TRANSFORMER.transform(this@transformToJava.asJava(isTopLevel, relocateToClass), context)
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameDocumentableTransformer.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameDocumentableTransformer.kt
new file mode 100644
index 00000000..fe625e1c
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameDocumentableTransformer.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.transformers
+
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.PropertyContainer
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer
+
+public class JvmNameDocumentableTransformer : DocumentableTransformer {
+ private val jvmNameProvider = JvmNameProvider()
+
+ override fun invoke(original: DModule, context: DokkaContext): DModule {
+ return original.copy(packages = original.packages.map { transform(it, context) })
+ }
+
+ internal fun <T : Documentable> transform(documentable: T, context: DokkaContext): T {
+ val transformResult = with(documentable) {
+ when (this) {
+ is DPackage -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ is DFunction -> {
+ val name = jvmNameProvider.nameFor(this)
+ copy(
+ dri = documentable.dri.withCallableName(name),
+ name = name,
+ extra = extra.withoutJvmName()
+ )
+ }
+ is DProperty -> transformGetterAndSetter(this)
+ is DClasslike -> transformClassLike(this, context)
+ is DEnumEntry -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ else -> {
+ context.logger.warn("Failed to translate a JvmName for ${this.javaClass.canonicalName}")
+ this
+ }
+ }
+ }
+ @Suppress("UNCHECKED_CAST")
+ return transformResult as T
+ }
+
+ private fun PropertyContainer<DFunction>.withoutJvmName(): PropertyContainer<DFunction> {
+ val annotationsWithoutJvmName = get(Annotations)?.let { annotations ->
+ annotations.copy((annotations.directAnnotations).map { (sourceset, annotations) ->
+ sourceset to annotations.filterNot { it.isJvmName() }
+ }.toMap() + annotations.fileLevelAnnotations)
+ }
+ val extraWithoutAnnotations: PropertyContainer<DFunction> = minus(Annotations)
+
+ return extraWithoutAnnotations.addAll(listOfNotNull(annotationsWithoutJvmName))
+ }
+
+ private fun transformClassLike(documentable: DClasslike, context: DokkaContext): DClasslike =
+ with(documentable) {
+ when (this) {
+ is DClass -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ is DAnnotation -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ is DObject -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ is DEnum -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ is DInterface -> copy(
+ functions = functions.map { transform(it, context) },
+ properties = properties.map { transform(it, context) },
+ classlikes = classlikes.map { transform(it, context) },
+ )
+ }
+ }
+
+ private fun transformGetterAndSetter(entry: DProperty): DProperty =
+ with(entry) {
+ copy(
+ setter = jvmNameProvider.nameForSetter(this)?.let { setterName ->
+ setter?.let { setter ->
+ setter.copy(
+ dri = setter.dri.withCallableName(setterName),
+ name = setterName,
+ extra = setter.extra.withoutJvmName()
+ )
+ }
+ },
+ getter = jvmNameProvider.nameForGetter(this)?.let { getterName ->
+ getter?.let { getter ->
+ getter.copy(
+ dri = getter.dri.withCallableName(getterName),
+ name = getterName,
+ extra = getter.extra.withoutJvmName()
+ )
+ }
+ })
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider.kt
new file mode 100644
index 00000000..caf76b68
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/JvmNameProvider.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.transformers
+
+import org.jetbrains.dokka.kotlinAsJava.directlyAnnotatedJvmName
+import org.jetbrains.dokka.kotlinAsJava.fileLevelJvmName
+import org.jetbrains.dokka.kotlinAsJava.jvmNameAsString
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+
+public data class Name(val fqName: String) {
+ val name: String = fqName.substringAfterLast(".")
+}
+
+public class JvmNameProvider {
+ public fun <T> nameFor(entry: T): String where T : Documentable, T : WithExtraProperties<T> =
+ entry.directlyAnnotatedJvmName()?.jvmNameAsString()
+ ?: entry.name
+ ?: throw IllegalStateException("Failed to provide a name for ${entry.javaClass.canonicalName}")
+
+ public fun <T> nameForSyntheticClass(entry: T): Name where T : WithSources, T : WithExtraProperties<T>, T : Documentable {
+ val name: String = (entry.fileLevelJvmName()?.params?.get("name") as? StringValue)?.value
+ ?: (entry.sources.entries.first().value.path.split("/").last().split(".").first().capitalize() + "Kt")
+ return Name("${entry.dri.packageName}.$name")
+ }
+
+ public fun nameForGetter(entry: DProperty): String? =
+ entry.getter?.directlyAnnotatedJvmName()?.jvmNameAsString()
+
+ public fun nameForSetter(entry: DProperty): String? =
+ entry.setter?.directlyAnnotatedJvmName()?.jvmNameAsString()
+
+ private fun List<Annotations.Annotation>.jvmNameAnnotation(): Annotations.Annotation? =
+ firstOrNull { it.isJvmName() }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/KotlinAsJavaDocumentableTransformer.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/KotlinAsJavaDocumentableTransformer.kt
new file mode 100644
index 00000000..45682ea4
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/KotlinAsJavaDocumentableTransformer.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.transformers
+
+import org.jetbrains.dokka.kotlinAsJava.converters.KotlinToJavaConverter
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer
+
+public class KotlinAsJavaDocumentableTransformer : DocumentableTransformer {
+ override fun invoke(original: DModule, context: DokkaContext): DModule =
+ original.copy(packages = original.packages.map {
+ with(KotlinToJavaConverter(context)) {
+ it.asJava()
+ }
+ })
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/withCallableName.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/withCallableName.kt
new file mode 100644
index 00000000..d2f5a9cf
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/transformers/withCallableName.kt
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.transformers
+
+import org.jetbrains.dokka.links.DRI
+
+internal fun DRI.withCallableName(newName: String): DRI = copy(callable = callable?.copy(name = newName))
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaDocumentableToPageTranslator.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaDocumentableToPageTranslator.kt
new file mode 100644
index 00000000..a0ed24d4
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaDocumentableToPageTranslator.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.translators
+
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.DokkaBaseConfiguration
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.pages.ModulePageNode
+import org.jetbrains.dokka.plugability.*
+import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
+
+public class KotlinAsJavaDocumentableToPageTranslator(
+ context: DokkaContext
+) : DocumentableToPageTranslator {
+ private val configuration = configuration<DokkaBase, DokkaBaseConfiguration>(context)
+ private val commentsToContentConverter = context.plugin<DokkaBase>().querySingle { commentsToContentConverter }
+ private val signatureProvider = context.plugin<DokkaBase>().querySingle { signatureProvider }
+ private val customTagContentProviders = context.plugin<DokkaBase>().query { customTagContentProvider }
+ private val documentableSourceLanguageParser = context.plugin<InternalKotlinAnalysisPlugin>().querySingle { documentableSourceLanguageParser }
+ private val logger: DokkaLogger = context.logger
+
+ override fun invoke(module: DModule): ModulePageNode =
+ KotlinAsJavaPageCreator(
+ configuration,
+ commentsToContentConverter,
+ signatureProvider,
+ logger,
+ customTagContentProviders,
+ documentableSourceLanguageParser
+ ).pageForModule(module)
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaPageCreator.kt b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaPageCreator.kt
new file mode 100644
index 00000000..fcdc1d83
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/kotlin/org/jetbrains/dokka/kotlinAsJava/translators/KotlinAsJavaPageCreator.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka.kotlinAsJava.translators
+
+import org.jetbrains.dokka.base.DokkaBaseConfiguration
+import org.jetbrains.dokka.base.signatures.SignatureProvider
+import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter
+import org.jetbrains.dokka.base.transformers.pages.tags.CustomTagContentProvider
+import org.jetbrains.dokka.base.translators.documentables.DefaultPageCreator
+import org.jetbrains.dokka.model.DProperty
+import org.jetbrains.dokka.pages.MemberPageNode
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.dokka.analysis.kotlin.internal.DocumentableSourceLanguageParser
+
+public class KotlinAsJavaPageCreator(
+ configuration: DokkaBaseConfiguration?,
+ commentsToContentConverter: CommentsToContentConverter,
+ signatureProvider: SignatureProvider,
+ logger: DokkaLogger,
+ customTagContentProviders: List<CustomTagContentProvider>,
+ documentableAnalyzer: DocumentableSourceLanguageParser
+) : DefaultPageCreator(
+ configuration,
+ commentsToContentConverter,
+ signatureProvider,
+ logger,
+ customTagContentProviders = customTagContentProviders,
+ documentableAnalyzer = documentableAnalyzer
+) {
+ override fun pageForProperty(p: DProperty): MemberPageNode? = null
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/dokka-subprojects/plugin-kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
new file mode 100644
index 00000000..4b6619b1
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin
@@ -0,0 +1,5 @@
+#
+# Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+#
+
+org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt
new file mode 100644
index 00000000..cba5e9ef
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt
@@ -0,0 +1,548 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.*
+import kotlin.test.*
+
+private const val COMPANION_NAME = "C"
+
+class CompanionAsJavaTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `empty companion object should not be rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {}
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only jvmField should not be rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmField val jvmFieldProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion property with jvmField should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmField val jvmFieldProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassProperty = parentClass.properties.firstOrNull { it.name == "jvmFieldProp" }
+ assertNotNull(parentClassProperty, "Parent class should contain the companion jvmField property")
+ assertIsStatic(parentClassProperty)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only const should not be rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val constProp: Int = 0
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion property with const should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val constProp: Int = 0
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassProperty = parentClass.properties.firstOrNull { it.name == "constProp" }
+ assertNotNull(parentClassProperty, "Parent class should contain the companion const property")
+ assertIsStatic(parentClassProperty)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only lateinit not rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | lateinit var lateInitProp: String
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion property with lateinit should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | lateinit var lateInitProp: String
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassProperty = parentClass.properties.firstOrNull { it.name == "lateInitProp" }
+ assertNotNull(parentClassProperty, "Parent class should contain the companion lateinit property")
+ assertIsStatic(parentClassProperty)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only jvmStatic fun not rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic fun staticFun(): String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion function with JvmStatic should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic fun staticFun(): String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassFunction = parentClass.functions.firstOrNull { it.name == "staticFun" }
+ assertNotNull(parentClassFunction, "Parent class should contains the companion jvmStatic function")
+ assertIsStatic(parentClassFunction)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with nested classes is rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic
+ | fun staticFun1(): String = ""
+ |
+ | const val CONST_VAL: Int = 100
+ |
+ | class NestedClass
+ | object NestedObject
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+
+ val classLikes = parentClass.companion?.classlikes
+ assertNotNull(classLikes)
+ assertEquals(2, classLikes.size,
+ "Classlike list should contains nested class and object")
+ assertTrue(classLikes.any { it.name == "NestedClass" })
+ assertTrue(classLikes.any { it.name == "NestedObject" })
+
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with supertype is rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Parent
+ |interface IParent
+ |class MyClass {
+ | companion object $COMPANION_NAME : Parent(), IParent {
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object rendered for own properties`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmField
+ | val jvmField: String = ""
+ | const val contVal: Int = 0
+ | lateinit var lateInit: String
+ |
+ | val rendered: Int = TODO()
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+
+ val properties = parentClass.companion?.properties
+
+ assertNotNull(properties)
+ assertEquals(2, properties.size) // including INSTANCE
+ assertTrue(properties.any { it.name == "rendered" })
+ assertTrue(properties.none { it.name == "jvmField1" })
+ assertTrue(properties.none { it.name == "contVal" })
+ assertTrue(properties.none { it.name == "lateInit" })
+ }
+ }
+ }
+
+ @Test
+ fun `companion object rendered for own functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic
+ | fun staticFun(): String = ""
+ |
+ | fun renderedFun(): String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+
+ val functions = parentClass.companion?.functions
+
+ assertNotNull(functions)
+ assertEquals(1, functions.size)
+ assertTrue(functions.any { it.name == "renderedFun" })
+ assertTrue(functions.none { it.name == "staticFun" })
+ }
+ }
+ }
+
+ @Test
+ fun `companion const value should be rendered as public by default`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val constProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertEquals(
+ JavaVisibility.Public,
+ parentClass.properties.firstOrNull()?.visibility?.values?.first()
+ )
+ assertNull(parentClass.findFunction("constProp"), "There is no getter for the cont field")
+ }
+ }
+ }
+
+ @Test
+ fun `companion const value should preserve Java modifier`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | protected const val constProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ documentedVisibilities = setOf(
+ org.jetbrains.dokka.DokkaConfiguration.Visibility.PUBLIC,
+ org.jetbrains.dokka.DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ },
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertEquals(
+ JavaVisibility.Protected,
+ parentClass.properties.firstOrNull()?.visibility?.values?.first()
+ )
+ assertNull(parentClass.findFunction("constProp"), "There is no getter for the cont field")
+ }
+ }
+ }
+
+ @Test
+ fun `companion lateinit value should be rendered as public by default`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | lateinit var lateInitProp: String
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertEquals(
+ JavaVisibility.Public,
+ parentClass.properties.firstOrNull()?.visibility?.values?.first()
+ )
+ assertNull(parentClass.findFunction("lateInitProp"), "There is no getter for the cont field")
+ }
+ }
+ }
+
+ @Test
+ fun `named companion instance property should be rendered if companion rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | var property: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertNotNull(parentClass.properties.any { it.name == COMPANION_NAME })
+ }
+ }
+ }
+
+ @Test
+ fun `default named companion instance property should be rendered if companion rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object {
+ | var property: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertTrue(parentClass.properties.any { it.name == "Companion" })
+ }
+ }
+ }
+
+ @Test
+ fun `companion instance property should be hidden if companion not rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val property: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertTrue(parentClass.properties.none { it.name == COMPANION_NAME })
+ }
+ }
+ }
+}
+
+private fun DModule.findClass(name: String) = packages.flatMap { it.classlikes }
+ .firstOrNull { it.name == name } as DClass
+
+private fun DClass.findFunction(name: String) = functions.firstOrNull { it.name.contains(name, ignoreCase = true) }
+
+private fun assertCompanionRendered(parentClass: DClass) {
+ assertNotNull(parentClass.companion, "Companion should not be null")
+ assertTrue(
+ parentClass.classlikes.any { it.name == COMPANION_NAME },
+ "Companion should be in classlikes list"
+ )
+}
+
+private fun assertCompanionNotRendered(parentClass: DClass) {
+ assertNull(parentClass.companion, "Companion should be null")
+ assertTrue(
+ parentClass.classlikes.none { it.name == COMPANION_NAME },
+ "Companion should not be in classlikes list"
+ )
+}
+
+private fun assertIsStatic(property: DProperty) {
+ val extra = property.extra[AdditionalModifiers]
+ assertNotNull(extra, "extra for property is present")
+ assertTrue(
+ extra.content.values.contains(setOf(ExtraModifiers.JavaOnlyModifiers.Static)),
+ "Property contains extra modifier static"
+ )
+}
+
+private fun assertIsStatic(function: DFunction) {
+ val extra = function.extra[AdditionalModifiers]
+ assertNotNull(extra, "extra for property is present")
+ assertTrue(
+ extra.content.values.contains(setOf(ExtraModifiers.JavaOnlyModifiers.Static)),
+ "Function contains extra modifier static"
+ )
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt
new file mode 100644
index 00000000..bdea1cb4
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.DEnum
+import kotlin.test.Test
+import kotlin.test.assertTrue
+
+class DRITranslationTest : BaseAbstractTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle nested classes`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class A {
+ | class B(val x: String)
+ |}
+ |class C {
+ | class B(val x: String)
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedClasslikesDRIs = module.packages.flatMap { it.classlikes }.flatMap { it.classlikes }.map { it.dri }
+ val driRegex = "[AC]\\.B".toRegex()
+
+ nestedClasslikesDRIs.forEach { dri ->
+ assertTrue(driRegex.matches(dri.classNames.toString()))
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle interface methods`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |interface A {
+ | fun b()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedFunctionDRI = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.filter { it.name == "b" }.map { it.dri }.single()
+
+ assertTrue(nestedFunctionDRI.classNames == "A")
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle object methods`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |object A {
+ | fun b() {}
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedFunctionDRI = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.filter { it.name == "b" }.map { it.dri }.single()
+
+ assertTrue(nestedFunctionDRI.classNames == "A")
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle enum functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |enum class A(private val x: Int) {
+ | X(0);
+ | fun b() = x
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedFunctionDRI = (module.packages.single().classlikes.single() as DEnum).functions.filter { it.name == "b" }.map { it.dri }.single()
+
+ assertTrue(nestedFunctionDRI.classNames == "A")
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle nested classes' constructors`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class A {
+ | class B(val x: String)
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val constructorDRI = (module.packages.flatMap { it.classlikes }.flatMap { it.classlikes }.single() as DClass).constructors.single().dri
+ assertTrue(constructorDRI.classNames == "A.B")
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt
new file mode 100644
index 00000000..6167e13a
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.AdditionalModifiers
+import org.jetbrains.dokka.model.ExtraModifiers
+import org.jetbrains.dokka.model.JavaVisibility
+import kotlin.test.*
+
+class JvmFieldTest : BaseAbstractTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should keep properties annotated with JvmField as properties`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class SampleClass(@JvmField val property: String, val otherProperty: String)
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertNotNull(classLike.properties.firstOrNull { it.name == "property" })
+ assertEquals(
+ listOf("getOtherProperty"),
+ classLike.functions.map { it.name })
+ }
+ }
+ }
+
+ @Test
+ fun `should work for top-level property`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmField
+ |val property: String = TODO()
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertNotNull(classLike.properties.firstOrNull { it.name == "property" })
+ assertEquals(
+ emptyList(),
+ classLike.functions.map { it.name })
+ }
+ }
+ }
+
+ @Test
+ fun `properties without JvmName should be kept private`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class SampleClass(val property: String)
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertEquals(JavaVisibility.Private, classLike.properties.firstOrNull()?.visibility?.values?.first())
+ assertNotNull(classLike.functions.firstOrNull { it.name.startsWith("get") })
+ assertEquals(
+ JavaVisibility.Public,
+ classLike.functions.first { it.name.startsWith("get") }.visibility.values.first()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `object jvmfield property should have no getters`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |object MyObject {
+ | @JvmField
+ | val property: String = TODO()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ val property = classLike.properties.singleOrNull { it.name == "property" }
+ assertNotNull(property)
+ assertEquals(
+ emptyList(),
+ classLike.functions.map { it.name }
+ )
+ assertNull(property.getter)
+ assertNull(property.setter)
+ }
+ }
+ }
+
+ @Test
+ fun `enum jvmfield property should have no getters`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |enum class MyEnum {
+ | ITEM;
+ |
+ | @JvmField
+ | val property: String = TODO()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ val property = classLike.properties.singleOrNull { it.name == "property" }
+ assertNotNull(property)
+ assertEquals(
+ emptyList(),
+ classLike.functions
+ .filter{ it.name.contains("property", ignoreCase = true) }
+ .map { it.name }
+ )
+ assertNull(property.getter)
+ assertNull(property.setter)
+ }
+ }
+ }
+
+
+ @Test
+ fun `object jvmfield property should be static`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |object MyObject {
+ | @JvmField
+ | val property: String = TODO()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ val property = classLike.properties.singleOrNull { it.name == "property" }
+ assertNotNull(property)
+
+ val extra = property.extra[AdditionalModifiers]
+ assertNotNull(extra, "Additional modifiers for property are exist")
+ assertTrue(extra.content.values.contains(setOf(ExtraModifiers.JavaOnlyModifiers.Static)),
+ "Extra modifiers contains static")
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt
new file mode 100644
index 00000000..7a087fb7
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.TypeConstructor
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.isJvmName
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+class JvmNameTest : BaseAbstractTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should change name for class containing top level function`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedClassLikeDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ )
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertEquals(expectedClassLikeDri, classLike.dri)
+ assertEquals("CustomJvmName", classLike.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should change name for top level function`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |@JvmName("jvmSample")
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedFunctionDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ callable = Callable(
+ "jvmSample",
+ receiver = null,
+ params = emptyList()
+ )
+ )
+ val function = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first()
+ assertEquals(expectedFunctionDri, function.dri)
+ assertEquals("jvmSample", function.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should change name of a setter for top level property`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |@get:JvmName("xd")
+ |@set:JvmName("asd")
+ |var property: String
+ | get() = ""
+ | set(value) {}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedSetterDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ callable = Callable(
+ "asd",
+ receiver = null,
+ //Todo this is bad, this should be a type in java, look at the bytecode
+ params = listOf(TypeConstructor("kotlin.String", emptyList()))
+ )
+ )
+ val function =
+ module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first { it.name == "asd" }
+ assertEquals(expectedSetterDri, function.dri)
+ assertEquals("asd", function.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should change name of a getter for top level property`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |@get:JvmName("xd")
+ |@set:JvmName("asd")
+ |var property: String
+ | get() = ""
+ | set(value) {}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedGetterDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ callable = Callable(
+ "xd",
+ receiver = null,
+ params = emptyList()
+ )
+ )
+ val function =
+ module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first { it.name == "xd" }
+ assertEquals(expectedGetterDri, function.dri)
+ assertEquals("xd", function.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should leave the name as default if annotation is not provided`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedClassLikeDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "SampleKt",
+ )
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertEquals(expectedClassLikeDri, classLike.dri)
+ assertEquals("SampleKt", classLike.name)
+ }
+ }
+ }
+
+ @Test
+ fun `jvmName extra should be removed after the name swap`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmName("CustomJvmName")
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first() as DClass
+ assertNull(
+ classLike.extra[Annotations]?.directAnnotations?.flatMap { it.value }
+ ?.map { it.dri }
+ ?.firstOrNull { it.isJvmName() }
+ )
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt
new file mode 100644
index 00000000..1d6f4698
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class JvmOverloadsTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should generate multiple functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmOverloads
+ |fun sample(a: Int = 0, b: String, c: Int = 0): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(3, functions.size)
+ assertEquals(3, functions[0].parameters.size)
+ assertEquals(2, functions[1].parameters.size)
+ assertEquals(1, functions[2].parameters.size)
+ }
+ }
+ }
+
+ @Test
+ fun `should do nothing if there is no default values`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmOverloads
+ |fun sample(a: Int, b: String, c: Int): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(1, functions.size)
+ assertEquals(3, functions[0].parameters.size)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt
new file mode 100644
index 00000000..3de48da7
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class JvmSyntheticTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should not include synthetic functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmSynthetic
+ |fun synthetic(): String = ""
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(1, functions.size)
+ assertEquals("sample", functions[0].name)
+ }
+ }
+ }
+
+ @Test
+ fun `should check synthetic on method fields, getters and setters`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@get:JvmSynthetic
+ |var synthetic: String = ""
+ |
+ |@set:JvmSynthetic
+ |var synthetic2: String = ""
+ |
+ |@JvmSynthetic
+ |var synthetic3: String = ""
+ |
+ |var sample: String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(4, functions.size)
+ assertEquals("setSynthetic", functions[0].name)
+ assertEquals("getSynthetic2", functions[1].name)
+ assertEquals("getSample", functions[2].name)
+ assertEquals("setSample", functions[3].name)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt
new file mode 100644
index 00000000..93d5c1b5
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt
@@ -0,0 +1,618 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import matchers.content.*
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.jdk
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.GenericTypeConstructor
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.*
+import signatures.Parameter
+import signatures.Parameters
+import signatures.firstSignature
+import signatures.renderedContent
+import utils.A
+import utils.TestOutputWriterPlugin
+import utils.match
+import kotlin.test.*
+
+class KotlinAsJavaPluginTest : BaseAbstractTest() {
+
+ @Test
+ fun `top-level functions should be generated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |object TestObj {}
+ |
+ |fun testFL(l: List<String>) = l
+ |fun testF() {}
+ |fun testF2(i: Int) = i
+ |fun testF3(to: TestObj) = to
+ |fun <T : Char> testF4(t: T) = listOf(t)
+ |val testV = 1
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val content = (root.children.single().children.first { it.name == "TestKt" } as ContentPage).content
+
+ val functionRows = content.findTableWithKind(kind = ContentKind.Functions).children
+ functionRows.assertCount(6)
+
+ val propRows = content.findTableWithKind(kind = ContentKind.Properties).children
+ propRows.assertCount(1)
+ }
+ }
+ }
+
+ private fun ContentNode.findTableWithKind(kind: ContentKind): ContentNode = dfs { node ->
+ node is ContentTable && node.dci.kind === kind
+ }.let { assertNotNull(it, "the table with kind $kind") }
+
+ @Test
+ fun topLevelWithClassTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Test {
+ | fun testFC() {}
+ | val testVC = 1
+ |}
+ |
+ |fun testF(i: Int) = i
+ |val testV = 1
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val contentList = root.children
+ .flatMap { it.children<ContentPage>() }
+
+ contentList.find {it.name == "Test"}.apply {
+ assertNotNull(this)
+ content.findTableWithKind(ContentKind.Functions).children.assertCount(2)
+ content.findTableWithKind(ContentKind.Properties).children.assertCount(1)
+ }
+ contentList.find {it.name == "TestKt"}.apply {
+ assertNotNull(this)
+ content.findTableWithKind(ContentKind.Functions).children.assertCount(2)
+ content.findTableWithKind(ContentKind.Properties).children.assertCount(1)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun kotlinAndJavaTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |fun testF(i: Int) = i
+ |/src/main/kotlin/kotlinAsJavaPlugin/TestJ.java
+ |package kotlinAsJavaPlugin;
+ |
+ |public class TestJ {
+ | public int testF(int i) { return i; }
+ |}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val classes = root.children.first().children.associateBy { it.name }
+ classes.values.assertCount(2, "Class count: ")
+
+ classes["TestKt"].let {
+ it?.children.orEmpty().assertCount(1, "(Kotlin) TestKt members: ")
+ it!!.children.first()
+ .let { assertEquals("testF", it.name, "(Kotlin) Expected method name: testF, got: ${it.name}") }
+ }
+
+ classes["TestJ"].let {
+ it?.children.orEmpty().assertCount(2, "(Java) TestJ members: ") // constructor + method
+ it!!.children.map { it.name }
+ .let {
+ assertTrue(
+ it.containsAll(
+ setOf(
+ "testF",
+ "TestJ"
+ )
+ ),
+ "(Java) Expected method name: testF, got: $it"
+ )
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `public kotlin properties should have a getter with same visibilities`(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Test {
+ | public val publicProperty: String = ""
+ |}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesTransformationStage = { rootPageNode ->
+ val propertyGetter = rootPageNode.dfs { it is MemberPageNode && it.name == "getPublicProperty" } as? MemberPageNode
+ assertNotNull(propertyGetter)
+ propertyGetter.content.assertNode {
+ group {
+ header(1) {
+ +"getPublicProperty"
+ }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ group {
+ +"public final "
+ group {
+ link {
+ +"String"
+ }
+ }
+ link {
+ +"getPublicProperty"
+ }
+ +"()"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `java properties should keep its modifiers`(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/TestJ.java
+ |package kotlinAsJavaPlugin;
+ |
+ |public class TestJ {
+ | public Int publicProperty = 1;
+ |}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val testClass = root.dfs { it.name == "TestJ" } as? ClasslikePageNode
+ assertNotNull(testClass)
+ (testClass.content as ContentGroup).children.last().children.last().assertNode {
+ group {
+ header(2){
+ +"Properties"
+ }
+ table {
+ group {
+ link {
+ +"publicProperty"
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ group {
+ group {
+ +"public Int"
+ link {
+ +"publicProperty"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `koltin interfaces and classes should be split to extends and implements`(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |open class A { }
+ |interface B
+ |class C : A(), B
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val testClass = root.dfs { it.name == "C" } as? ClasslikePageNode
+ assertNotNull(testClass)
+ testClass.content.assertNode {
+ group {
+ header(expectedLevel = 1) {
+ +"C"
+ }
+ platformHinted {
+ group {
+ +"public final class "
+ link {
+ +"C"
+ }
+ +" extends "
+ group {
+ link {
+ +"A"
+ }
+ }
+ +" implements "
+ group {
+ link {
+ +"B"
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ private fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") =
+ assertEquals(n, count(), "${prefix}Expected $n, got ${count()}")
+
+ @Test
+ fun `typealias`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(DokkaConfiguration.ExternalDocumentationLink.jdk(8))
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |typealias XD = Int
+ |class ABC {
+ | fun someFun(xd: XD): Int = 1
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-a-b-c/some-fun.html").firstSignature().match(
+ "public final ", A("Integer"), A("someFun"), "(", Parameters(
+ Parameter(A("Integer"), "xd")
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `typealias with generic`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |typealias XD<B, A> = Map<A, B>
+ |
+ |class ABC {
+ | fun someFun(xd: XD<Int, String>) = 1
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-a-b-c/some-fun.html").firstSignature().match(
+ "public final ", A("Integer"), A("someFun"), "(", Parameters(
+ Parameter(A("Map"), "<", A("String"), ", ", A("Integer"), "> xd"),
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `const in top level`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |const val FIRST = "String"
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ assertNull(writerPlugin.writer.contents["root/kotlinAsJavaPlugin/-test-kt/get-f-i-r-s-t.html"])
+ }
+ }
+ }
+
+ @Test
+ fun `function in top level`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |fun sample(a: Int) = ""
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-test-kt/sample.html").firstSignature().match(
+ "public final static ", A("String"), A("sample"), "(", Parameters(
+ Parameter(A("Integer"), "a"),
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should render primary kotlin constructor as a java constructor`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Test(val xd: Int)
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val content = root.children
+ .flatMap { it.children<ContentPage>() }
+ .map { it.content }.single().mainContents
+
+ val text = content.single { it is ContentHeader }.children
+ .single() as ContentText
+
+ assertEquals("Constructors", text.text)
+ }
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-test/-test.html").firstSignature().match(
+ A("Test"), A("Test"), "(", Parameters(
+ Parameter(A("Integer"), "xd")
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ /**
+ * Kotlin Int becomes java int. Java int cannot be annotated in source, but Kotlin Int can be.
+ * This is paired with DefaultDescriptorToDocumentableTranslatorTest.`Java primitive annotations work`()
+ *
+ * This test currently does not do anything because Kotlin.Int currently becomes java.lang.Integer not primitive int
+ */
+ @Test
+ fun `Java primitive annotations work`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |@MustBeDocumented
+ |@Target(AnnotationTarget.TYPE)
+ |annotation class Hello()
+ |fun bar(): @Hello() Int
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ documentablesTransformationStage = { module ->
+ val type = module.packages.single()
+ .classlikes.first { it.name == "TestKt" }
+ .functions.single()
+ .type as GenericTypeConstructor
+ assertEquals(
+ Annotations.Annotation(DRI("kotlinAsJavaPlugin", "Hello"), emptyMap()),
+ type.extra[Annotations]?.directAnnotations?.values?.single()?.single()
+ )
+ // A bug; the GenericTypeConstructor cast should fail and this should be a PrimitiveJavaType
+ assertEquals("java.lang/Integer///PointingToDeclaration/", type.dri.toString())
+ }
+ }
+ }
+
+ @Test
+ fun `Java function should keep its access modifier`(){
+ val className = "Test"
+ val accessModifier = "public"
+ val methodName = "method"
+
+ val testClassQuery = """
+ |/src/main/kotlin/kotlinAsJavaPlugin/${className}.java
+ |package kotlinAsJavaPlugin;
+ |
+ |public class $className {
+ | $accessModifier void ${methodName}() {
+ |
+ | }
+ |}
+ """.trimMargin()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ testClassQuery,
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ val methodDocumentation = "root/kotlinAsJavaPlugin/-${className.toLowerCase()}/${methodName}.html"
+
+ writerPlugin.writer.renderedContent(methodDocumentation)
+ .firstSignature()
+ .match(
+ "$accessModifier void ", A(methodName), "()",
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+}
+
+private val ContentNode.mainContents: List<ContentNode>
+ get() = (this as ContentGroup).children
+ .filterIsInstance<ContentGroup>()
+ .single { it.dci.kind == ContentKind.Main }.children[0].let { it.children[0] }.children
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt
new file mode 100644
index 00000000..25312810
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.jdk
+import signatures.firstSignature
+import signatures.renderedContent
+import signatures.signature
+import utils.*
+import kotlin.test.Test
+
+class KotlinAsJavaSignatureTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+
+ @Suppress("SameParameterValue")
+ private fun source(signature: String) =
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ | $signature
+ """.trimIndent()
+
+ @Test
+ fun `fun with definitely non-nullable types as java`() {
+ val source = source("fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val signature = writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-test-kt/elvis-like.html").firstSignature()
+ signature.match(
+ "public final static ", Span("T"), A("elvisLike"),
+ "<T extends ", A("Any"), ">(",
+ Span(
+ Span(Span(), " x, "),
+ Span(Span(), " y")
+ ),
+ ")",
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should display annotations`() {
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |@MustBeDocumented
+ |annotation class OnClass
+ |
+ |@MustBeDocumented
+ |annotation class OnMethod
+ |
+ |@MustBeDocumented
+ |annotation class OnParameter
+ |
+ |@Target(AnnotationTarget.TYPE)
+ |@MustBeDocumented
+ |annotation class OnType
+ |
+ |@Target(AnnotationTarget.TYPE_PARAMETER)
+ |@MustBeDocumented
+ |annotation class OnTypeParameter
+ |
+ |@OnClass
+ |class Clazz<@OnTypeParameter T : @OnType Any> {
+ | @OnMethod
+ | fun <@OnTypeParameter T : @OnType Any> withParams(@OnParameter str1: String, str2: String): Boolean {
+ | return str1 == str2
+ | }
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val signatures = writerPlugin.writer
+ .renderedContent("root/kotlinAsJavaPlugin/-clazz/index.html")
+ .signature()
+
+ val classSignature = signatures[0]
+ classSignature.match(
+ Div(Div("@", A("OnClass"), "()")),
+ "public final class ", A("Clazz"),
+ // <@OnTypeParameter() T extends @OnType() Object>
+ "<", Span("@", A("OnTypeParameter"), "() "), "T extends ", Span("@", A("OnType"), "() "), A("Object"), ">",
+ ignoreSpanWithTokenStyle = true
+ )
+
+ val functionSignature = signatures[2]
+ functionSignature.match(
+ Div(Div("@", A("OnMethod"), "()")),
+ "public final ", A("Boolean"), A("withParams"),
+ // <@OnTypeParameter() T extends @OnType() Object>
+ "<", Span("@", A("OnTypeParameter"), "() "), "T extends ", Span("@", A("OnType"), "() "), A("Any"), ">(",
+ Span(
+ Span(
+ Span("@", A("OnParameter"), "() "),
+ A("String"), "str1, "
+ ),
+ Span(
+ A("String"), "str2"
+ )
+ ), ")",
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+}