aboutsummaryrefslogtreecommitdiff
path: root/plugins/kotlin-as-java/src/main/kotlin/converters
diff options
context:
space:
mode:
authorKamil Doległo <kamilok1965@interia.pl>2020-03-01 21:26:13 +0100
committerKamil Doległo <kamilok1965@users.noreply.github.com>2020-03-04 18:00:19 +0100
commite4044ec67ad90041b02bb84c4b966ffeac537617 (patch)
treea06e82ad218737771b7a6381537a98ba00c94eaf /plugins/kotlin-as-java/src/main/kotlin/converters
parentcf0c5043887b1dd38808b0fc12bd8700c9f3b6ba (diff)
downloaddokka-e4044ec67ad90041b02bb84c4b966ffeac537617.tar.gz
dokka-e4044ec67ad90041b02bb84c4b966ffeac537617.tar.bz2
dokka-e4044ec67ad90041b02bb84c4b966ffeac537617.zip
Add initial version of Kotlin as Java plugin
Diffstat (limited to 'plugins/kotlin-as-java/src/main/kotlin/converters')
-rw-r--r--plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt246
1 files changed, 246 insertions, 0 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
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