diff options
author | Marcin Aman <marcin.aman@gmail.com> | 2020-12-29 14:38:19 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-29 14:38:19 +0100 |
commit | f5e7cffbebb66b989c64bdda61e1f7809ddc6068 (patch) | |
tree | ea0ef4b550f9a55681a915d5cb4d9f1beca22041 /plugins/kotlin-as-java/src/main/kotlin | |
parent | e55f3b015faec3f62e829a2aa5984e4bd6d5037e (diff) | |
download | dokka-f5e7cffbebb66b989c64bdda61e1f7809ddc6068.tar.gz dokka-f5e7cffbebb66b989c64bdda61e1f7809ddc6068.tar.bz2 dokka-f5e7cffbebb66b989c64bdda61e1f7809ddc6068.zip |
Parsing of JvmName (#1675)
* Parsing of JvmName
* Make JvmName processor run after KaJ
Diffstat (limited to 'plugins/kotlin-as-java/src/main/kotlin')
8 files changed, 260 insertions, 88 deletions
diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt index 2a9b857a..b8e12553 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt @@ -4,6 +4,7 @@ import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration 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 @@ -13,13 +14,20 @@ class KotlinAsJavaPlugin : DokkaPlugin() { val kotlinAsJavaDocumentableTransformer by extending { CoreExtensions.documentableTransformer with KotlinAsJavaDocumentableTransformer() } + + val jvmNameTransformer by extending { + CoreExtensions.documentableTransformer with JvmNameDocumentableTransformer() order { + after(kotlinAsJavaDocumentableTransformer) + } + } + val javaSignatureProvider by extending { with(plugin<DokkaBase>()) { signatureProvider providing ::JavaSignatureProvider override kotlinSignatureProvider } } val kotlinAsJavaDocumentableToPageTranslator by extending { - CoreExtensions.documentableToPageTranslator providing ::KotlinAsJavaDocumentableToPageTranslator override - plugin<DokkaBase>().documentableToPageTranslator + CoreExtensions.documentableToPageTranslator providing ::KotlinAsJavaDocumentableToPageTranslator override + plugin<DokkaBase>().documentableToPageTranslator } }
\ No newline at end of file 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 0f7d5c7a..4ecc84a7 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt @@ -1,27 +1,19 @@ package org.jetbrains.dokka.kotlinAsJava.converters -import org.jetbrains.dokka.links.* +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.PointingToDeclaration +import org.jetbrains.dokka.links.withClass import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.DAnnotation -import org.jetbrains.dokka.model.DEnum -import org.jetbrains.dokka.model.DFunction -import org.jetbrains.dokka.model.Nullable -import org.jetbrains.dokka.model.TypeConstructor import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType -import java.lang.IllegalStateException -private fun <T : WithSources> List<T>.groupedByLocation() = - map { it.sources to it } - .groupBy({ (location, _) -> - location.let { - it.entries.first().value.path.split("/").last().split(".").first() + "Kt" - } // TODO: first() does not look reasonable - }) { it.second } +val jvmNameProvider = JvmNameProvider() private val DProperty.isConst: Boolean get() = extra[AdditionalModifiers] @@ -31,38 +23,39 @@ private val DProperty.isConst: Boolean } == true internal fun DPackage.asJava(): DPackage { - @Suppress("UNCHECKED_CAST") - val syntheticClasses = ((properties + functions) as List<WithSources>) - .groupedByLocation() - .map { (syntheticClassName, nodes) -> - DClass( - dri = dri.withClass(syntheticClassName), - name = syntheticClassName, - properties = nodes.filterIsInstance<DProperty>().map { it.asJava(true) }, - constructors = emptyList(), - functions = ( - nodes - .filterIsInstance<DProperty>() - .filterNot { it.isConst } - .flatMap { it.javaAccessors() } + - nodes.filterIsInstance<DFunction>() - .map { it.asJava(syntheticClassName) }), // TODO: methods are static and receiver is a param - classlikes = emptyList(), - sources = emptyMap(), - expectPresentInSet = null, - visibility = sourceSets.map { - it to JavaVisibility.Public - }.toMap(), - companion = null, - generics = emptyList(), - supertypes = emptyMap(), - documentation = emptyMap(), - modifier = sourceSets.map { it to JavaModifier.Final }.toMap(), - sourceSets = sourceSets, - isExpectActual = false, - extra = PropertyContainer.empty() - ) - } + 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>().map { it.asJava(true) }, + constructors = emptyList(), + functions = ( + nodes + .filterIsInstance<DProperty>() + .filterNot { it.isConst } + .flatMap { it.javaAccessors(relocateToClass = syntheticClassName.name) } + + nodes.filterIsInstance<DFunction>() + .map { it.asJava(syntheticClassName.name) }), // TODO: methods are static and receiver is a param + classlikes = emptyList(), + sources = emptyMap(), + expectPresentInSet = null, + visibility = sourceSets.map { + it to JavaVisibility.Public + }.toMap(), + companion = null, + generics = emptyList(), + supertypes = emptyMap(), + documentation = emptyMap(), + modifier = sourceSets.map { it to JavaModifier.Final }.toMap(), + sourceSets = sourceSets, + isExpectActual = false, + extra = PropertyContainer.empty() + ) + } return copy( functions = emptyList(), @@ -110,41 +103,56 @@ internal fun DProperty.javaModifierFromSetter() = internal fun DProperty.javaAccessors(isTopLevel: Boolean = false, relocateToClass: String? = null): List<DFunction> = listOfNotNull( - getter?.copy( - dri = if (relocateToClass.isNullOrBlank()) { - dri + 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.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else getter.extra + ) + }, + setter?.let { setter -> + val name = "set" + name.capitalize() + val baseDRI = (if (relocateToClass.isNullOrBlank()) { + setter.dri } else { - dri.withClass(relocateToClass) - }, - name = "get" + name.capitalize(), - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { JavaVisibility.Public }, - type = type.asJava(), // TODO: check - extra = if (isTopLevel) getter!!.extra + - getter!!.extra.mergeAdditionalModifiers( - sourceSets.map { - it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) - }.toMap() + 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() ) - else getter!!.extra - ), - setter?.copy( - dri = if (relocateToClass.isNullOrBlank()) { - dri - } else { - dri.withClass(relocateToClass) - }, - name = "set" + name.capitalize(), - modifier = javaModifierFromSetter(), - visibility = visibility.mapValues { JavaVisibility.Public }, - type = type.asJava(), // TODO: check - extra = if (isTopLevel) setter!!.extra + setter!!.extra.mergeAdditionalModifiers( - sourceSets.map { - it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) - }.toMap() + }, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = Void, + extra = if (isTopLevel) setter.extra + setter.extra.mergeAdditionalModifiers( + sourceSets.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else setter.extra ) - else setter!!.extra - ) + } ) @@ -154,7 +162,7 @@ internal fun DFunction.asJava(containingClassName: String): DFunction { else -> name } return copy( -// dri = dri.copy(callable = dri.callable?.asJava()), + 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) @@ -193,7 +201,7 @@ private fun DTypeParameter.asJava(): DTypeParameter = copy( bounds = bounds.map { it.asJava() } ) -private fun Projection.asJava(): Projection = when(this) { +private fun Projection.asJava(): Projection = when (this) { is Star -> Star is Covariance<*> -> copy(inner.asJava()) is Contravariance<*> -> copy(inner.asJava()) @@ -201,7 +209,7 @@ private fun Projection.asJava(): Projection = when(this) { is Bound -> asJava() } -private fun Bound.asJava(): Bound = when(this) { +private fun Bound.asJava(): Bound = when (this) { is TypeParameter -> copy(dri.possiblyAsJava()) is GenericTypeConstructor -> copy( dri = dri.possiblyAsJava(), @@ -286,7 +294,7 @@ internal fun DParameter.asJava(): DParameter = copy( ) internal fun Visibility.propertyVisibilityAsJava(): Visibility = - if(this is JavaVisibility) this + if (this is JavaVisibility) this else JavaVisibility.Private internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() @@ -295,7 +303,7 @@ internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.b private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames private fun DRI.possiblyAsJava() = this.partialFqName().mapToJava()?.toDRI(this) ?: this -private fun TypeConstructor.possiblyAsJava() = when(this) { +private fun TypeConstructor.possiblyAsJava() = when (this) { is GenericTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) is FunctionalTypeConstructor -> copy(dri = this.dri.possiblyAsJava()) } @@ -318,7 +326,7 @@ internal fun TypeConstructorWithKind.asJava(): TypeConstructorWithKind = ) internal fun ClassKind.asJava(): ClassKind { - return when(this){ + return when (this) { is JavaClassKindTypes -> this KotlinClassKindTypes.CLASS -> JavaClassKindTypes.CLASS KotlinClassKindTypes.INTERFACE -> JavaClassKindTypes.INTERFACE diff --git a/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt b/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt new file mode 100644 index 00000000..c81210d6 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/jvmName.kt @@ -0,0 +1,19 @@ +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 +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult + +internal fun WithExtraProperties<out Documentable>.directlyAnnotatedJvmName(): Annotations.Annotation? = + extra[Annotations]?.directAnnotations?.entries?.firstNotNullResult { (_, annotations) -> annotations.jvmNameAnnotation() } + +internal fun WithExtraProperties<out Documentable>.fileLevelJvmName(): Annotations.Annotation? = + extra[Annotations]?.fileLevelAnnotations?.entries?.firstNotNullResult { (_, 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
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt index 60c2e5cc..fd40366f 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt @@ -140,7 +140,7 @@ class JavaSignatureProvider internal constructor(ctcc: CommentsToContentConverte text(it.modifiers()[it]?.toSignatureString() ?: "") signatureForProjection(it.type) text(nbsp.toString()) - link(it.name!!, it.dri) + text(it.name!!) } text(")") } diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt new file mode 100644 index 00000000..3fb26034 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameDocumentableTransformer.kt @@ -0,0 +1,96 @@ +package org.jetbrains.dokka.kotlinAsJava.transformers + +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + +class JvmNameDocumentableTransformer : DocumentableTransformer { + private val jvmNameProvider = JvmNameProvider() + private lateinit var context: DokkaContext + + override fun invoke(original: DModule, context: DokkaContext): DModule { + this.context = context + return original.copy(packages = original.packages.map { transform(it) }) + } + + private fun <T : Documentable> transform(documentable: T): T = + with(documentable) { + when (this) { + is DPackage -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DFunction -> { + val name = jvmNameProvider.nameFor(this) + copy( + dri = documentable.dri.withCallableName(name), + name = name + ) + } + is DProperty -> transformGetterAndSetter(this) + is DClasslike -> transformClassLike(this) + is DEnumEntry -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + else -> { + context.logger.warn("Failed to translate a JvmName for ${this.javaClass.canonicalName}") + this + } + } + } as T + + private fun transformClassLike(documentable: DClasslike): DClasslike = + with(documentable) { + when (this) { + is DClass -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DAnnotation -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DObject -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DEnum -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + is DInterface -> copy( + functions = functions.map { transform(it) }, + properties = properties.map { transform(it) }, + classlikes = classlikes.map { transform(it) }, + ) + } + } + + 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 + ) + } + }, + getter = jvmNameProvider.nameForGetter(this)?.let { getterName -> + getter?.let { getter -> + getter.copy( + dri = getter.dri.withCallableName(getterName), + name = getterName + ) + } + }) + } +}
\ 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 new file mode 100644 index 00000000..4e0ff7d7 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/JvmNameProvider.kt @@ -0,0 +1,36 @@ +package org.jetbrains.dokka.kotlinAsJava.transformers + +import org.jetbrains.dokka.kotlinAsJava.directlyAnnotatedJvmName +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){ + val name = fqName.substringAfterLast(".") +} + +class JvmNameProvider { + fun <T> nameFor(entry: T): String where T : Documentable, T : WithExtraProperties<out Documentable> = + entry.directlyAnnotatedJvmName()?.jvmNameAsString() + ?: 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" + return Name("${entry.dri.packageName}.$name") + } + + fun nameForGetter(entry: DProperty): String? = + entry.getter?.directlyAnnotatedJvmName()?.jvmNameAsString() + + fun nameForSetter(entry: DProperty): String? = + entry.setter?.directlyAnnotatedJvmName()?.jvmNameAsString() + + private fun List<Annotations.Annotation>.jvmNameAnnotation(): Annotations.Annotation? = + firstOrNull { it.isJvmName() } +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt index 8b07670f..5916a11c 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt @@ -8,4 +8,4 @@ import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer class KotlinAsJavaDocumentableTransformer : DocumentableTransformer { override fun invoke(original: DModule, context: DokkaContext): DModule = original.copy(packages = original.packages.map { it.asJava() }) -} +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/withCallableName.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/withCallableName.kt new file mode 100644 index 00000000..30f6a217 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/withCallableName.kt @@ -0,0 +1,5 @@ +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))
\ No newline at end of file |