diff options
author | Kamil Doległo <kamilok1965@interia.pl> | 2020-03-01 21:26:13 +0100 |
---|---|---|
committer | Kamil Doległo <kamilok1965@users.noreply.github.com> | 2020-03-04 18:00:19 +0100 |
commit | e4044ec67ad90041b02bb84c4b966ffeac537617 (patch) | |
tree | a06e82ad218737771b7a6381537a98ba00c94eaf | |
parent | cf0c5043887b1dd38808b0fc12bd8700c9f3b6ba (diff) | |
download | dokka-e4044ec67ad90041b02bb84c4b966ffeac537617.tar.gz dokka-e4044ec67ad90041b02bb84c4b966ffeac537617.tar.bz2 dokka-e4044ec67ad90041b02bb84c4b966ffeac537617.zip |
Add initial version of Kotlin as Java plugin
10 files changed, 380 insertions, 420 deletions
diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index 117dd552..f30ffa00 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -58,7 +58,7 @@ data class DRIWithPlatformInfo( fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, PlatformDependent.empty()) -open class DokkaDescriptorVisitor( // TODO: close this class and make it private together with DRIWithPlatformInfo +private class DokkaDescriptorVisitor( // TODO: close this class and make it private together with DRIWithPlatformInfo private val platformData: PlatformData, private val resolutionFacade: DokkaResolutionFacade ) : DeclarationDescriptorVisitorEmptyBodies<Documentable, DRIWithPlatformInfo>() { diff --git a/plugins/kotlin-as-java/src/main/kotlin/JavaSignatureProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/JavaSignatureProvider.kt new file mode 100644 index 00000000..526546f2 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/JavaSignatureProvider.kt @@ -0,0 +1,112 @@ +package org.jetbrains.dokka.kotlinAsJava + +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.links.sureClassNames +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Annotation +import org.jetbrains.dokka.model.Enum +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.PlatformData +import org.jetbrains.dokka.utilities.DokkaLogger + +class JavaSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider { + private val contentBuilder = PageContentBuilder(ctcc, logger) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is Function -> signature(documentable) + is Classlike -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun signature(f: Function) = f.platformData.map { signature(f, it) }.distinct() + + private fun signature(c: Classlike) = c.platformData.map { signature(c, it) }.distinct() + + private fun signature(c: Classlike, platform: PlatformData) = contentBuilder.contentFor(c, ContentKind.Symbol) { + text(c.visibility[platform]?.externalDisplayName ?: "") + if (c is Class) { + text(c.modifier.toString()) + } + when (c) { + is Class -> text(" class ") + is Interface -> text(" interface ") + is Enum -> text(" enum ") + is Object -> text(" class ") + is Annotation -> text(" @interface ") + } + text(c.name!!) + if (c is WithSupertypes) { + list(c.supertypes.getValue(platform), prefix = " : ") { + link(it.sureClassNames, it) + } + } + } + + private fun signature(f: Function, platform: PlatformData) = contentBuilder.contentFor(f, ContentKind.Symbol) { + text(f.visibility[platform]?.externalDisplayName ?: "") + text(f.modifier.toString()) + val returnType = f.type + if (!f.isConstructor && returnType.constructorFqName != Unit::class.qualifiedName) { + text(": ") + type(returnType) + } + text(" ") + link(f.name, f.dri) + val generics = f.generics.filterOnPlatform(platform) + if (generics.isNotEmpty()) { + text("<") + generics.forEach { + signature(it) + } + text(">") + } + text("(") + list(listOfNotNull(f.receiver) + f.parameters.filterOnPlatform(platform)) { + type(it.type) + text(" ") + link(it.name!!, it.dri) + text(", ") + } + text(")") + } + + private fun signature(t: TypeParameter) = contentBuilder.contentFor(t, ContentKind.Symbol) { + link(t.name, t.dri) + if (t.bounds.isNotEmpty()) { + text("<") + t.bounds.forEach { + signature(it, t.dri, t.platformData) + } + text(">") + } + } + + private fun signature(p: Projection, dri: DRI, platforms: List<PlatformData>): List<ContentNode> = when (p) { + is OtherParameter -> contentBuilder.contentFor(dri, platforms.toSet()) { text(p.name) }.children + + is TypeConstructor -> contentBuilder.contentFor(dri, platforms.toSet()) { + link(p.dri.classNames.orEmpty(), p.dri) + }.children + p.projections.flatMap { signature(it, dri, platforms) } + + is Variance -> contentBuilder.contentFor(dri, platforms.toSet()) { + text(p.kind.toString() + " ") + }.children + signature(p.inner, dri, platforms) + + is Star -> contentBuilder.contentFor(dri, platforms.toSet()) { text("*") }.children + + is Nullable -> signature(p.inner, dri, platforms) + contentBuilder.contentFor( + dri, + platforms.toSet() + ) { text("?") }.children + } + + private fun <T : Documentable> Collection<T>.filterOnPlatform(platformData: PlatformData) = + this.filter { it.platformData.contains(platformData) } +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaDescriptorToDocumentableTranslator.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaDescriptorToDocumentableTranslator.kt deleted file mode 100644 index 3b615dcb..00000000 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaDescriptorToDocumentableTranslator.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.jetbrains.dokka.kotlinAsJava - -import org.jetbrains.dokka.analysis.DokkaResolutionFacade -import org.jetbrains.dokka.base.translators.descriptors.DRIWithPlatformInfo -import org.jetbrains.dokka.base.translators.descriptors.DokkaDescriptorVisitor -import org.jetbrains.dokka.base.translators.descriptors.withEmptyInfo -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.Function -import org.jetbrains.dokka.pages.PlatformData -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.dokka.transformers.descriptors.DescriptorToDocumentableTranslator -import org.jetbrains.kotlin.descriptors.* - -class KotlinAsJavaDescriptorToDocumentableTranslator( - private val context: DokkaContext -) : DescriptorToDocumentableTranslator { - override fun invoke( - moduleName: String, - packageFragments: Iterable<PackageFragmentDescriptor>, - platformData: PlatformData - ): Module = - KotlinAsJavaDokkaDescriptorVisitor(platformData, context.platforms[platformData]?.facade!!).run { - packageFragments.map { visitPackageFragmentDescriptor(it, DRI.topLevel.withEmptyInfo()) } - }.let { Module(moduleName, it) } -} - -class KotlinAsJavaDokkaDescriptorVisitor( - platformData: PlatformData, - resolutionFacade: DokkaResolutionFacade -) : DokkaDescriptorVisitor(platformData, resolutionFacade) { - override fun visitPackageFragmentDescriptor( - descriptor: PackageFragmentDescriptor, - parent: DRIWithPlatformInfo - ): Package { - val dri = DRI(packageName = descriptor.fqName.asString()) - DescriptorCache.add(dri, descriptor) - return super.visitPackageFragmentDescriptor(descriptor, parent) - } - - override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): Classlike { - val dri = parent.dri.withClass(descriptor.name.asString()) - DescriptorCache.add(dri, descriptor) - return super.visitClassDescriptor(descriptor, parent) - } - - override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): Property { - val dri = parent.dri.copy(callable = Callable.from(descriptor)) - DescriptorCache.add(dri, descriptor) - return super.visitPropertyDescriptor(descriptor, parent) - } - - override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): Function { - val dri = parent.dri.copy(callable = Callable.from(descriptor)) - DescriptorCache.add(dri, descriptor) - return super.visitFunctionDescriptor(descriptor, parent) - } - - override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): Function { - val dri = parent.dri.copy(callable = Callable.from(descriptor)) - DescriptorCache.add(dri, descriptor) - return super.visitConstructorDescriptor(descriptor, parent) - } - - override fun visitPropertyAccessorDescriptor( - descriptor: PropertyAccessorDescriptor, - propertyDescriptor: PropertyDescriptor, - parent: DRI - ): Function { - val dri = parent.copy(callable = Callable.from(descriptor)) - DescriptorCache.add(dri, descriptor) - return super.visitPropertyAccessorDescriptor(descriptor, propertyDescriptor, parent) - } -}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageBuilder.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageBuilder.kt deleted file mode 100644 index ef6f9c33..00000000 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageBuilder.kt +++ /dev/null @@ -1,81 +0,0 @@ -package org.jetbrains.dokka.kotlinAsJava - -import org.jetbrains.dokka.base.translators.documentables.DefaultPageBuilder -import org.jetbrains.dokka.base.translators.documentables.RootContentBuilder -import org.jetbrains.dokka.kotlinAsJava.conversions.asJava -import org.jetbrains.dokka.kotlinAsJava.conversions.asStatic -import org.jetbrains.dokka.kotlinAsJava.conversions.withClass -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.links.withClass -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.Enum -import org.jetbrains.dokka.model.Function -import org.jetbrains.dokka.pages.* -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi - -fun DeclarationDescriptor.sourceLocation(): String? = this.findPsi()?.containingFile?.virtualFile?.path -fun <T : Documentable> List<T>.groupedByLocation(): Map<String, List<T>> = - this.map { DescriptorCache[it.dri]?.sourceLocation() to it } - .filter { it.first != null }.groupBy({ (location, _) -> - location!!.let { it.split("/").last().split(".").first() + "Kt" } - }) { it.second } - -fun PlatformInfo.toClassPlatformInfo(inherited: List<DRI> = emptyList()) = - ClassPlatformInfo(this, emptyList()) - -class KotlinAsJavaPageBuilder(rootContentGroup: RootContentBuilder) : DefaultPageBuilder(rootContentGroup) { - - override fun pageForModule(m: Module): ModulePageNode = - ModulePageNode(m.name.ifEmpty { "root" }, contentForModule(m), m, m.packages.map { pageForPackage(it) }) - - data class FunsAndProps(val key: String, val funs: List<Function>, val props: List<Property>) - - override fun pageForPackage(p: Package): PackagePageNode { - - val funs = p.functions.groupedByLocation() - - val props = p.properties.groupedByLocation() - - val zipped = (funs.keys + props.keys) - .map { k -> FunsAndProps(k, funs[k].orEmpty(), props[k].orEmpty()) } - - val classes = (p.classlikes + zipped.map { (key, funs, props) -> - val dri = p.dri.withClass(key) - val actual = - (funs.flatMap { it.actual } + props.flatMap { it.actual }).distinct().map { it.toClassPlatformInfo() } - Class( - dri = dri, - name = key, - kind = KotlinClassKindTypes.CLASS, - constructors = emptyList(), - functions = funs.map { it.withClass(key, dri).asStatic() }, - properties = props.map { it.withClass(key, dri) }, - classlikes = emptyList(), - actual = actual, - expected = null, - visibility = p.platformData.map { it to Visibilities.PUBLIC }.toMap() - ) - }).map { it.asJava() } - - return PackagePageNode( - p.name, contentForPackage(p, classes), setOf(p.dri), p, - classes.map(::pageForClasslike) - ) - } - - private fun contentForPackage(p: Package, nClasses: List<Classlike>) = group(p) { - header(1) { text("Package ${p.name}") } - block("Types", 2, ContentKind.Properties, nClasses, p.platformData) { - link(it.name, it.dri) - text(it.briefDocTagString) - } - } - - override fun contentForClasslike(c: Classlike): ContentGroup = when (c) { - is Class -> contentForClass(c) - is Enum -> contentForEnum(c) - else -> throw IllegalStateException("$c should not be present here") - } -}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageContentBuilder.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageContentBuilder.kt deleted file mode 100644 index a8ec7126..00000000 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPageContentBuilder.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.jetbrains.dokka.kotlinAsJava - -import org.jetbrains.dokka.base.translators.documentables.DefaultPageContentBuilder -import org.jetbrains.dokka.base.translators.documentables.PageContentBuilderFunction -import org.jetbrains.dokka.base.translators.documentables.type -import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.Function -import org.jetbrains.dokka.model.JavaTypeWrapper -import org.jetbrains.dokka.pages.* -import org.jetbrains.dokka.utilities.DokkaLogger - -class KotlinAsJavaPageContentBuilder( - dri: Set<DRI>, - platformData: Set<PlatformData>, - kind: Kind, - commentsConverter: CommentsToContentConverter, - logger: DokkaLogger, - styles: Set<Style> = emptySet(), - extras: Set<Extra> = emptySet() -) : DefaultPageContentBuilder(dri, platformData, kind, commentsConverter, logger, styles, extras) { - - override fun signature(f: Function) = signature(f) { - - // DokkaConsoleLogger.info("KotlinAsJavaSignature") - val returnType = f.returnType - if (!f.isConstructor) { - if (returnType != null && - returnType.constructorFqName != Unit::class.qualifiedName - ) { - if ((returnType as? JavaTypeWrapper)?.isPrimitive == true) - text(returnType.constructorFqName ?: "") - else - type(returnType) - text(" ") - } else text("void ") - - } - - link(f.name, f.dri) - text("(") - val params = listOfNotNull(f.receiver) + f.parameters - list(params) { - if ((it.type as? JavaTypeWrapper)?.isPrimitive == true) - text(it.type.constructorFqName ?: "") - else - type(it.type) - - text(" ") - link(it.name ?: "receiver", it.dri) - } - text(")") - } - - override fun group( - dri: Set<DRI>, - platformData: Set<PlatformData>, - kind: Kind, - block: PageContentBuilderFunction - ): ContentGroup = group(dri, platformData, kind, commentsConverter, logger, block) - - companion object { - fun group( - dri: Set<DRI>, - platformData: Set<PlatformData>, - kind: Kind, - commentsConverter: CommentsToContentConverter, - logger: DokkaLogger, - block: PageContentBuilderFunction - ): ContentGroup = - KotlinAsJavaPageContentBuilder(dri, platformData, kind, commentsConverter, logger).apply(block).build() - } -}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt index 1a6bc0db..0ca278ce 100644 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt @@ -1,47 +1,18 @@ -package org.jetbrains.dokka.kotlinAsJava - +package org.jetbrains.dokka.kotlinAsJava import org.jetbrains.dokka.CoreExtensions import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.links.DRI -import org.jetbrains.dokka.model.Module -import org.jetbrains.dokka.pages.ModulePageNode -import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.kotlinAsJava.transformers.KotlinAsJavaDocumentableTransformer import org.jetbrains.dokka.plugability.DokkaPlugin -import org.jetbrains.dokka.plugability.plugin -import org.jetbrains.dokka.plugability.querySingle -import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor class KotlinAsJavaPlugin : DokkaPlugin() { - val kotlinAsJavaDescriptorToDocumentableTranslator by extending { - CoreExtensions.descriptorToDocumentableTranslator providing ::KotlinAsJavaDescriptorToDocumentableTranslator - } val kotlinAsJavaDocumentableToPageTranslator by extending { - CoreExtensions.documentableToPageTranslator providing ::KotlinAsJavaDocumentationToPageTranslator + CoreExtensions.documentableTransformer with KotlinAsJavaDocumentableTransformer() + } + val javaSignatureProvider by extending { + val dokkaBasePlugin = plugin<DokkaBase>() + dokkaBasePlugin.signatureProvider providing { ctx -> + JavaSignatureProvider(ctx.single(dokkaBasePlugin.commentsToContentConverter), ctx.logger) + } } -} - -object DescriptorCache { - private val cache: HashMap<DRI, DeclarationDescriptor> = HashMap() - - fun add(dri: DRI, descriptor: DeclarationDescriptor): Boolean = cache.putIfAbsent(dri, descriptor) == null - operator fun get(dri: DRI): DeclarationDescriptor? = cache[dri] -} - -class KotlinAsJavaDocumentationToPageTranslator( - private val context: DokkaContext -) : DocumentableToPageTranslator { - override fun invoke(module: Module): ModulePageNode = - KotlinAsJavaPageBuilder { node, kind, operation -> - KotlinAsJavaPageContentBuilder.group( - setOf(node.dri), - node.platformData, - kind, - context.plugin<DokkaBase>().querySingle { commentsToContentConverter }, - context.logger, - operation - ) - }.pageForModule(module) - }
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinToJVMResolver.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinToJVMResolver.kt deleted file mode 100644 index 7b0495e9..00000000 --- a/plugins/kotlin-as-java/src/main/kotlin/KotlinToJVMResolver.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.jetbrains.dokka.kotlinAsJava.conversions - -import org.jetbrains.dokka.kotlinAsJava.DescriptorCache -import org.jetbrains.dokka.links.* -import org.jetbrains.dokka.model.* -import org.jetbrains.dokka.model.Function -import org.jetbrains.dokka.model.Enum -import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType - -fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() - .find { it.typeFqName.asString() == this } - ?.let { JvmPrimitiveType.get(it) } - -fun TypeWrapper.getAsType(classId: ClassId, fqName: String, top: Boolean): TypeWrapper { - val fqNameSplitted = fqName.takeIf { top }?.getAsPrimitive()?.name?.toLowerCase() - ?.let { listOf(it) } ?: classId.asString().split("/") - return JavaTypeWrapper( - fqNameSplitted, - arguments.mapNotNull { it.asJava(false) }, - classId.toDRI(dri), - fqNameSplitted.last()[0].isLowerCase() - ) -} - -fun TypeWrapper?.asJava(top: Boolean = true): TypeWrapper? = this?.constructorFqName - ?.takeUnless { it.endsWith(".Unit") } - ?.let { fqName -> - fqName.mapToJava() - ?.let { getAsType(it, fqName, top) } ?: this - } - -fun Classlike.asJava(): Classlike = when { - this is Class -> this.asJava() - this is Enum -> this.asJava() - this is EnumEntry -> this - else -> throw IllegalArgumentException("$this shouldn't be here") -} - -fun Class.asJava(): Class = Class( - dri, name, kind, - constructors.map { it.asJava() }, - (functions + properties.flatMap { it.accessors }).map { it.asJava() }, - properties, classlikes.mapNotNull { (it as? Class)?.asJava() }, expected, actual, extra, visibility -) - -fun Enum.asJava(): Enum = Enum( - dri = dri, - name = name, - entries = entries.mapNotNull { it.asJava() as? EnumEntry }, - constructors = constructors.map(Function::asJava), - functions = (functions + properties.flatMap { it.accessors }).map(Function::asJava), - properties = properties, - classlikes = classlikes.map(Classlike::asJava), - expected = expected, - actual = actual, - extra = extra, - visibility = visibility -) - -fun tcAsJava(tc: TypeConstructor): TypeReference = - tc.fullyQualifiedName.mapToJava() - ?.let { - tc.copy( - fullyQualifiedName = it.asString(), - params = tc.params.map { it.asJava() } - ) - } ?: tc - -fun tpAsJava(tp: TypeParam): TypeReference = - tp.copy(bounds = tp.bounds.map { it.asJava() }) - -fun TypeReference.asJava(): TypeReference = when (this) { - is TypeConstructor -> tcAsJava(this) - is TypeParam -> tpAsJava(this) - else -> this -} - -fun Callable.asJava(): Callable = copy(params = params.mapNotNull { (it as? TypeConstructor)?.asJava() }) - - -fun Parameter.asJava(): Parameter = Parameter( - dri.copy(callable = dri.callable?.asJava()), - name, - type.asJava()!!, - expected, - actual, - extra -) - -fun Function.asJava(): Function { - val newName = when { - isConstructor -> "init" - else -> name - } - return Function( - dri.copy(callable = dri.callable?.asJava()), - newName, - returnType.asJava(), - isConstructor, - receiver, - parameters.map { it.asJava() }, - expected, - actual, - extra, - visibility - ) -} - -private fun String.mapToJava(): ClassId? = - JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) - -fun ClassId.toDRI(dri: DRI?): DRI = DRI( - packageName = packageFqName.asString(), - classNames = classNames(), - callable = dri?.callable?.asJava(), - extra = null, - target = null -) - -fun ClassId.classNames(): String = - shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") - -fun Function.asStatic(): Function = also { it.extra.add(STATIC) } - -fun Property.withClass(className: String, dri: DRI): Property { - val nDri = dri.withClass(className).copy( - callable = getDescriptor()?.let { Callable.from(it) } - ) - return Property( - nDri, name, receiver, expected, actual, extra, accessors, visibility - ) -} - -fun Function.withClass(className: String, dri: DRI): Function { - val nDri = dri.withClass(className).copy( - callable = getDescriptor()?.let { Callable.from(it) } - ) - return Function( - nDri, name, returnType, isConstructor, receiver, parameters, expected, actual, extra, visibility - ) -} - -fun Function.getDescriptor(): FunctionDescriptor? = DescriptorCache[dri].let { it as? FunctionDescriptor } - -fun Property.getDescriptor(): PropertyDescriptor? = DescriptorCache[dri].let { it as? PropertyDescriptor } diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt new file mode 100644 index 00000000..19c4ef19 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt @@ -0,0 +1,246 @@ +package org.jetbrains.dokka.kotlinAsJava.converters + +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.Annotation +import org.jetbrains.dokka.model.Enum +import org.jetbrains.dokka.model.Function +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 + +private fun <T : WithExpectActual> List<T>.groupedByLocation() = + map { it.sources to it } + .groupBy({ (location, _) -> + location.let { it.map.entries.first().value.path.split("/").last().split(".").first() + "Kt" } // TODO: first() does not look reasonable + }) { it.second } + +internal fun Package.asJava(): Package { + @Suppress("UNCHECKED_CAST") + val syntheticClasses = ((properties + functions) as List<WithExpectActual>) + .groupedByLocation() + .map { (syntheticClassName, nodes) -> + Class( + dri = dri.withClass(syntheticClassName), + name = syntheticClassName, + properties = nodes.filterIsInstance<Property>().map { it.asJava() }, + constructors = emptyList(), + functions = ( + nodes.filterIsInstance<Property>().map { it.javaAccessors() } + + nodes.filterIsInstance<Function>().map { it.asJava(syntheticClassName) } + ) as List<Function>, // TODO: methods are static and receiver is a param + classlikes = emptyList(), + sources = PlatformDependent.empty(), + visibility = PlatformDependent.empty(), // TODO: fix this with the new visibility model -> public + companion = null, + generics = emptyList(), + supertypes = PlatformDependent.empty(), + documentation = PlatformDependent.empty(), + modifier = WithAbstraction.Modifier.Final, + platformData = platformData, + extra = PropertyContainer.empty() + ) + } + + return copy( + functions = emptyList(), + properties = emptyList(), + classlikes = classlikes.map { it.asJava() } + syntheticClasses, + packages = packages.map { it.asJava() } + ) +} + +internal fun Property.asJava(isTopLevel: Boolean = false, relocateToClass: String? = null) = + copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + modifier = if (setter == null) { + WithAbstraction.Modifier.Final + } else { + WithAbstraction.Modifier.Empty + }, + type = type.asJava(isTopLevel), // TODO: check, + setter = null, + getter = null // Removing getters and setters as they will be available as functions + ) // TODO: visibility -> always private; if (isTopLevel) -> static + +internal fun Property.javaAccessors(isTopLevel: Boolean = false, relocateToClass: String? = null): List<Function> = + listOfNotNull( + getter?.copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + name = "get" + name.capitalize(), + modifier = if (setter == null) { + WithAbstraction.Modifier.Final + } else { + WithAbstraction.Modifier.Empty + }, + type = type.asJava(isTopLevel) // TODO: check, + ), + setter?.copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + name = "set" + name.capitalize(), + modifier = if (setter == null) { + WithAbstraction.Modifier.Final + } else { + WithAbstraction.Modifier.Empty + }, + type = type.asJava(isTopLevel) // TODO: check, + ) + ) // TODO: if (isTopLevel) -> static; visibility -> always? public + + +internal fun Function.asJava(containingClassName: String): Function { + val newName = when { + isConstructor -> containingClassName + else -> name + } + return copy( +// dri = dri.copy(callable = dri.callable?.asJava()), + name = newName, + type = type.asJava(), + parameters = parameters.map { it.asJava() } + ) // TODO: should receiver be the first param? +} + +internal fun Classlike.asJava(): Classlike = when (this) { + is Class -> asJava() + is Enum -> asJava() + is Annotation -> asJava() + is Object -> asJava() + is Interface -> asJava() + else -> throw IllegalArgumentException("$this shouldn't be here") +} + +internal fun Class.asJava(): Class = copy( + constructors = constructors.map { it.asJava(name) }, + functions = (functions + properties.map { it.getter } + properties.map { it.setter }).filterNotNull().map { + it.asJava( + name + ) + }, + properties = properties.map { it.asJava() }, + classlikes = classlikes.map { it.asJava() } +) // TODO: if modifier is from Kotlin, then Empty -> Final I think, Java ones stay the same + +internal fun Enum.asJava(): Enum = copy( + constructors = constructors.map { it.asJava(name) }, + functions = (functions + properties.map { it.getter } + properties.map { it.setter }).filterNotNull().map { + it.asJava( + name + ) + }, + properties = properties.map { it.asJava() }, + classlikes = classlikes.map { it.asJava() } +// , entries = entries.map { it.asJava() } +) // TODO: if modifier is from Kotlin, then Empty -> Final I think, Java ones stay the same + +internal fun Object.asJava(): Object = copy( + functions = (functions + properties.map { it.getter } + properties.map { it.setter }) + .filterNotNull() + .map { it.asJava(name.orEmpty()) }, + properties = properties.map { it.asJava() } + + Property( + name = "INSTANCE", + modifier = WithAbstraction.Modifier.Final, + dri = dri.copy(callable = Callable("INSTANCE", null, emptyList())), + documentation = PlatformDependent.empty(), + sources = PlatformDependent.empty(), + visibility = PlatformDependent.empty(), // TODO: public and static + type = JavaTypeWrapper( + dri.packageName?.split(".").orEmpty() + + dri.classNames?.split(".").orEmpty(), + emptyList(), + dri, + false + ), + setter = null, + getter = null, + platformData = platformData, + receiver = null + ), + classlikes = classlikes.map { it.asJava() } +) + +internal fun Interface.asJava(): Interface = copy( + functions = (functions + properties.map { it.getter } + properties.map { it.setter }) + .filterNotNull() + .map { it.asJava(name) }, + properties = emptyList(), + classlikes = classlikes.map { it.asJava() } // TODO: public static final class DefaultImpls with impls for methods (killme please) +) + +internal fun Annotation.asJava(): Annotation = 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 Parameter.asJava(): Parameter = copy( + type = type.asJava() +) + +internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() + .find { it.typeFqName.asString() == this } + ?.let { JvmPrimitiveType.get(it) } + +internal fun TypeWrapper.getAsType(classId: ClassId, fqName: String, top: Boolean): TypeWrapper { + val fqNameSplit = fqName + .takeIf { top } + ?.getAsPrimitive() + ?.name?.toLowerCase() + ?.let(::listOf) + ?: classId.asString().split("/") + + return JavaTypeWrapper( + fqNameSplit, + arguments.map { it.asJava(false) }, + classId.toDRI(dri), + fqNameSplit.last()[0].isLowerCase() + ) +} + +internal fun TypeWrapper.asJava(top: Boolean = true): TypeWrapper = constructorFqName + ?.takeUnless { it.endsWith(".Unit") } // TODO: ??? + ?.let { fqName -> fqName.mapToJava()?.let { getAsType(it, fqName, top) } } ?: this + + +private fun String.mapToJava(): ClassId? = + JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) + +internal fun ClassId.toDRI(dri: DRI?): DRI = DRI( + packageName = packageFqName.asString(), + classNames = classNames(), + callable = dri?.callable,//?.asJava(), TODO: ???? + extra = null, + target = null +) + +internal fun ClassId.classNames(): String = + shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") + +//fun TypeConstructor.asJava(): TypeReference = +// fullyQualifiedName.mapToJava() +// ?.let { tc.copy(fullyQualifiedName = it.asString(), params = tc.params.map { it.asJava() }) } ?: tc + +//fun TypeParam.asJava(): TypeReference = copy(bounds = bounds.map { it.asJava() }) + +//fun TypeReference.asJava(): TypeReference = when (this) { +// is TypeConstructor -> asJava() +// is TypeParam -> asJava() +// else -> this +//}
\ 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 new file mode 100644 index 00000000..8f51e105 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka.kotlinAsJava.transformers + +import org.jetbrains.dokka.kotlinAsJava.converters.asJava +import org.jetbrains.dokka.model.Module +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + +class KotlinAsJavaDocumentableTransformer : DocumentableTransformer { + override fun invoke(original: Module, context: DokkaContext): Module = + original.copy(packages = original.packages.map { it.asJava() }) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 04ce2baf..263a1b09 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ include("runners:cli") include("runners:maven-plugin") include("plugins:base") include("plugins:mathjax") -//include("plugins:kotlin-as-java") +include("plugins:kotlin-as-java") include("integration-tests:gradle-integration-tests") pluginManagement { |