aboutsummaryrefslogtreecommitdiff
path: root/plugins/kotlin-as-java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/kotlin-as-java')
-rw-r--r--plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt27
-rw-r--r--plugins/kotlin-as-java/src/main/kotlin/jvmField.kt12
-rw-r--r--plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt13
-rw-r--r--plugins/kotlin-as-java/src/test/kotlin/JvmFieldTest.kt81
4 files changed, 121 insertions, 12 deletions
diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt
index 4ecc84a7..8b21e44d 100644
--- a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt
+++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt
@@ -1,5 +1,6 @@
package org.jetbrains.dokka.kotlinAsJava.converters
+import org.jetbrains.dokka.kotlinAsJava.jvmField
import org.jetbrains.dokka.kotlinAsJava.transformers.JvmNameProvider
import org.jetbrains.dokka.kotlinAsJava.transformers.withCallableName
import org.jetbrains.dokka.links.Callable
@@ -22,6 +23,9 @@ private val DProperty.isConst: Boolean
ExtraModifiers.KotlinOnlyModifiers.Const in modifiers
} == true
+private val DProperty.isJvmField: Boolean
+ get() = jvmField() != null
+
internal fun DPackage.asJava(): DPackage {
val syntheticClasses =
(properties.map { jvmNameProvider.nameForSyntheticClass(it) to it }
@@ -36,7 +40,7 @@ internal fun DPackage.asJava(): DPackage {
functions = (
nodes
.filterIsInstance<DProperty>()
- .filterNot { it.isConst }
+ .filterNot { it.isConst || it.isJvmField }
.flatMap { it.javaAccessors(relocateToClass = syntheticClassName.name) } +
nodes.filterIsInstance<DFunction>()
.map { it.asJava(syntheticClassName.name) }), // TODO: methods are static and receiver is a param
@@ -76,6 +80,8 @@ internal fun DProperty.asJava(isTopLevel: Boolean = false, relocateToClass: Stri
visibility = visibility.mapValues {
if (isTopLevel && isConst) {
JavaVisibility.Public
+ } else if (jvmField() != null) {
+ it.value.asJava()
} else {
it.value.propertyVisibilityAsJava()
}
@@ -92,6 +98,14 @@ internal fun DProperty.asJava(isTopLevel: Boolean = false, relocateToClass: Stri
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 {
@@ -169,6 +183,7 @@ internal fun DFunction.asJava(containingClassName: String): DFunction {
sourceSets.map { it to JavaModifier.Empty }.toMap()
else sourceSets.map { it to modifier.values.first() }.toMap(),
parameters = listOfNotNull(receiver?.asJava()) + parameters.map { it.asJava() },
+ visibility = visibility.map { (sourceSet, visibility) -> Pair(sourceSet, visibility.asJava()) }.toMap(),
receiver = null
) // TODO static if toplevel
}
@@ -184,9 +199,7 @@ internal fun DClasslike.asJava(): DClasslike = when (this) {
internal fun DClass.asJava(): DClass = copy(
constructors = constructors.map { it.asJava(name) },
- functions = (functions + properties.map { it.getter } + properties.map { it.setter }).filterNotNull().map {
- it.asJava(name)
- },
+ functions = functionsInJava(),
properties = properties.map { it.asJava() },
classlikes = classlikes.map { it.asJava() },
generics = generics.map { it.asJava() },
@@ -196,6 +209,12 @@ internal fun DClass.asJava(): DClass = copy(
else sourceSets.map { it to modifier.values.first() }.toMap()
)
+internal fun DClass.functionsInJava(): List<DFunction> =
+ (properties.filter { it.jvmField() == null }
+ .flatMap { property -> listOfNotNull(property.getter, property.setter) } + functions).map {
+ it.asJava(name)
+ }
+
private fun DTypeParameter.asJava(): DTypeParameter = copy(
variantTypeParameter = variantTypeParameter.withDri(dri.possiblyAsJava()),
bounds = bounds.map { it.asJava() }
diff --git a/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt b/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt
new file mode 100644
index 00000000..9a66eb27
--- /dev/null
+++ b/plugins/kotlin-as-java/src/main/kotlin/jvmField.kt
@@ -0,0 +1,12 @@
+package org.jetbrains.dokka.kotlinAsJava
+
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
+
+internal fun WithExtraProperties<out Documentable>.jvmField(): Annotations.Annotation? =
+ extra[Annotations]?.directAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmFieldAnnotation() }
+
+internal fun List<Annotations.Annotation>.jvmFieldAnnotation(): Annotations.Annotation? =
+ firstOrNull { it.dri.packageName == "kotlin.jvm" && it.dri.classNames == "JvmField" } \ No newline at end of file
diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt
index 4e0ff7d7..31252ae0 100644
--- a/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt
+++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt
@@ -1,12 +1,12 @@
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
-import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
-data class Name(val fqName: String){
+data class Name(val fqName: String) {
val name = fqName.substringAfterLast(".")
}
@@ -16,12 +16,9 @@ class JvmNameProvider {
?: entry.name
?: throw IllegalStateException("Failed to provide a name for ${entry.javaClass.canonicalName}")
- fun <T> nameForSyntheticClass(entry: T): Name where T : WithSources, T : WithExtraProperties<out Documentable>, T: Documentable {
- val name = entry.extra[Annotations]?.let {
- it.fileLevelAnnotations.entries.firstNotNullResult { (_, annotations) ->
- annotations.jvmNameAnnotation()?.jvmNameAsString()
- }
- } ?: entry.sources.entries.first().value.path.split("/").last().split(".").first().capitalize() + "Kt"
+ fun <T> nameForSyntheticClass(entry: T): Name where T : WithSources, T : WithExtraProperties<out Documentable>, 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")
}
diff --git a/plugins/kotlin-as-java/src/test/kotlin/JvmFieldTest.kt b/plugins/kotlin-as-java/src/test/kotlin/JvmFieldTest.kt
new file mode 100644
index 00000000..154083d7
--- /dev/null
+++ b/plugins/kotlin-as-java/src/test/kotlin/JvmFieldTest.kt
@@ -0,0 +1,81 @@
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.JavaVisibility
+import org.junit.jupiter.api.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+
+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", "equals", "hashCode", "toString"),
+ 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()
+ )
+ }
+ }
+ }
+} \ No newline at end of file