aboutsummaryrefslogtreecommitdiff
path: root/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt')
-rw-r--r--plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt298
1 files changed, 298 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..e66266a2
--- /dev/null
+++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt
@@ -0,0 +1,298 @@
+package org.jetbrains.dokka.kotlinAsJava.converters
+
+import org.jetbrains.dokka.links.*
+import org.jetbrains.dokka.links.Callable
+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 : WithExpectActual> 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 }
+
+internal fun DPackage.asJava(): DPackage {
+ @Suppress("UNCHECKED_CAST")
+ val syntheticClasses = ((properties + functions) as List<WithExpectActual>)
+ .groupedByLocation()
+ .map { (syntheticClassName, nodes) ->
+ DClass(
+ dri = dri.withClass(syntheticClassName),
+ name = syntheticClassName,
+ properties = nodes.filterIsInstance<DProperty>().map { it.asJava() },
+ constructors = emptyList(),
+ functions = (
+ nodes.filterIsInstance<DProperty>()
+ .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,
+ extra = PropertyContainer.empty()
+ )
+ }
+
+ return copy(
+ functions = emptyList(),
+ properties = emptyList(),
+ classlikes = classlikes.map { it.asJava() } + syntheticClasses
+ )
+}
+
+internal fun DProperty.asJava(isTopLevel: Boolean = false, relocateToClass: String? = null) =
+ copy(
+ dri = if (relocateToClass.isNullOrBlank()) {
+ dri
+ } else {
+ dri.withClass(relocateToClass)
+ },
+ modifier = javaModifierFromSetter(),
+ visibility = visibility.mapValues { 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) extra +
+ extra.mergeAdditionalModifiers(
+ sourceSets.map {
+ it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)
+ }.toMap()
+ )
+ else extra
+ )
+
+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?.copy(
+ dri = if (relocateToClass.isNullOrBlank()) {
+ 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()
+ )
+ 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()
+ )
+ else setter!!.extra
+ )
+ )
+
+
+internal fun DFunction.asJava(containingClassName: String): DFunction {
+ val newName = when {
+ isConstructor -> containingClassName
+ else -> name
+ }
+ return copy(
+// dri = dri.copy(callable = dri.callable?.asJava()),
+ name = newName,
+ type = type.asJava(),
+ modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Final } && isConstructor)
+ sourceSets.map { it to JavaModifier.Empty }.toMap()
+ else sourceSets.map { it to modifier.values.first() }.toMap(),
+ parameters = listOfNotNull(receiver?.asJava()) + parameters.map { it.asJava() },
+ receiver = null
+ ) // TODO static if toplevel
+}
+
+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.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() },
+ generics = generics.map { it.asJava() },
+ supertypes = supertypes.mapValues { it.value.map { it.asJava() } },
+ modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Empty }) sourceSets.map { it to JavaModifier.Final }
+ .toMap()
+ else sourceSets.map { it to modifier.values.first() }.toMap()
+)
+
+private fun DTypeParameter.asJava(): DTypeParameter = copy(
+ dri = dri.possiblyAsJava(),
+ bounds = bounds.map { it.asJava() }
+)
+
+private fun Bound.asJava(): Bound = when (this) {
+ is TypeConstructor -> copy(
+ dri = dri.possiblyAsJava()
+ )
+ is Nullable -> copy(
+ inner = inner.asJava()
+ )
+ else -> this
+}
+
+internal fun DEnum.asJava(): DEnum = 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() },
+ supertypes = supertypes.mapValues { it.value.map { it.asJava() } }
+// , entries = entries.map { it.asJava() }
+)
+
+internal fun DObject.asJava(): DObject = copy(
+ functions = (functions + properties.map { it.getter } + properties.map { it.setter })
+ .filterNotNull()
+ .map { it.asJava(name.orEmpty()) },
+ properties = properties.map { it.asJava() } +
+ DProperty(
+ name = "INSTANCE",
+ modifier = sourceSets.map { it to JavaModifier.Final }.toMap(),
+ dri = dri.copy(callable = Callable("INSTANCE", null, emptyList())),
+ documentation = emptyMap(),
+ sources = emptyMap(),
+ visibility = sourceSets.map {
+ it to JavaVisibility.Public
+ }.toMap(),
+ type = TypeConstructor(dri, emptyList()),
+ setter = null,
+ getter = null,
+ sourceSets = sourceSets,
+ receiver = null,
+ generics = emptyList(),
+ expectPresentInSet = expectPresentInSet,
+ 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 + 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
+ 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
+
+internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values()
+ .find { it.typeFqName.asString() == this }
+ ?.let { JvmPrimitiveType.get(it) }
+
+private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames
+private fun DRI.possiblyAsJava() = this.partialFqName().mapToJava()?.toDRI(this) ?: 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: check this
+ extra = null,
+ target = PointingToDeclaration
+)
+
+internal fun DriWithKind.asJava(): DriWithKind =
+ DriWithKind(
+ dri = dri.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 PropertyContainer<out Documentable>.mergeAdditionalModifiers(second: SourceSetDependent<Set<ExtraModifiers>>) =
+ this[AdditionalModifiers]?.squash(AdditionalModifiers(second)) ?: AdditionalModifiers(second)
+
+private fun AdditionalModifiers.squash(second: AdditionalModifiers) =
+ AdditionalModifiers(content + second.content)
+
+internal fun ClassId.classNames(): String =
+ shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "") \ No newline at end of file