package org.jetbrains.dokka.model import com.intellij.psi.PsiNamedElement import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.model.properties.PropertyContainer import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages.PlatformData import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.load.kotlin.toSourceElement abstract class Documentable { abstract val name: String? abstract val dri: DRI abstract val children: List abstract val documentation: PlatformDependent abstract val platformData: List override fun toString(): String = "${javaClass.simpleName}($dri)" override fun equals(other: Any?) = other is Documentable && this.dri == other.dri // TODO: https://github.com/Kotlin/dokka/pull/667#discussion_r382555806 override fun hashCode() = dri.hashCode() } data class PlatformDependent( val map: Map, val expect: T? = null ) : Map by map { val prevalentValue: T? get() = map.values.distinct().singleOrNull() val allValues: Sequence = sequence { expect?.also { yield(it) } yieldAll(map.values) } val allEntries: Sequence> = sequence { expect?.also { yield(null to it) } map.forEach { (k, v) -> yield(k to v) } } fun getOrExpect(platform: PlatformData): T? = map[platform] ?: expect companion object { fun empty(): PlatformDependent = PlatformDependent(emptyMap()) fun from(platformData: PlatformData, element: T) = PlatformDependent(mapOf(platformData to element)) @Suppress("UNCHECKED_CAST") fun from(pairs: Iterable>) = PlatformDependent( pairs.filter { it.first != null }.toMap() as Map, pairs.firstOrNull { it.first == null }?.second ) fun from(vararg pairs: Pair) = from(pairs.asIterable()) fun expectFrom(element: T) = PlatformDependent(map = emptyMap(), expect = element) } } interface WithExpectActual { val sources: PlatformDependent } interface WithScope { val functions: List val properties: List val classlikes: List } interface WithVisibility { val visibility: PlatformDependent } interface WithType { val type: Bound } interface WithAbstraction { val modifier: PlatformDependent } sealed class Modifier(val name: String) sealed class KotlinModifier(name: String) : Modifier(name) { object Abstract : KotlinModifier("abstract") object Open : KotlinModifier("open") object Final : KotlinModifier("final") object Sealed : KotlinModifier("sealed") object Empty : KotlinModifier("") } sealed class JavaModifier(name: String) : Modifier(name) { object Abstract : JavaModifier("abstract") object Final : JavaModifier("final") object Empty : JavaModifier("") } interface WithCompanion { val companion: DObject? } interface WithConstructors { val constructors: List } interface WithGenerics { val generics: List } interface WithSupertypes { val supertypes: PlatformDependent> } interface Callable : WithVisibility, WithType, WithAbstraction, WithExpectActual { val receiver: DParameter? } abstract class DClasslike : Documentable(), WithScope, WithVisibility, WithExpectActual data class DModule( override val name: String, val packages: List, override val documentation: PlatformDependent, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), WithExtraProperties { override val dri: DRI = DRI.topLevel override val children: List get() = packages override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DPackage( override val dri: DRI, override val functions: List, override val properties: List, override val classlikes: List, override val documentation: PlatformDependent, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), WithScope, WithExtraProperties { override val name = dri.packageName.orEmpty() override val children: List get() = (properties + functions + classlikes) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DClass( override val dri: DRI, override val name: String, override val constructors: List, override val functions: List, override val properties: List, override val classlikes: List, override val sources: PlatformDependent, override val visibility: PlatformDependent, override val companion: DObject?, override val generics: List, override val supertypes: PlatformDependent>, override val documentation: PlatformDependent, override val modifier: PlatformDependent, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, WithExtraProperties { override val children: List get() = (functions + properties + classlikes + constructors) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DEnum( override val dri: DRI, override val name: String, val entries: List, override val documentation: PlatformDependent, override val sources: PlatformDependent, override val functions: List, override val properties: List, override val classlikes: List, override val visibility: PlatformDependent, override val companion: DObject?, override val constructors: List, override val supertypes: PlatformDependent>, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : DClasslike(), WithCompanion, WithConstructors, WithSupertypes, WithExtraProperties { override val children: List get() = (entries + functions + properties + classlikes + constructors) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DEnumEntry( override val dri: DRI, override val name: String, override val documentation: PlatformDependent, override val functions: List, override val properties: List, override val classlikes: List, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), WithScope, WithExtraProperties { override val children: List get() = (functions + properties + classlikes) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DFunction( override val dri: DRI, override val name: String, val isConstructor: Boolean, val parameters: List, override val documentation: PlatformDependent, override val sources: PlatformDependent, override val visibility: PlatformDependent, override val type: Bound, override val generics: List, override val receiver: DParameter?, override val modifier: PlatformDependent, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), Callable, WithGenerics, WithExtraProperties { override val children: List get() = parameters override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DInterface( override val dri: DRI, override val name: String, override val documentation: PlatformDependent, override val sources: PlatformDependent, override val functions: List, override val properties: List, override val classlikes: List, override val visibility: PlatformDependent, override val companion: DObject?, override val generics: List, override val supertypes: PlatformDependent>, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : DClasslike(), WithCompanion, WithGenerics, WithSupertypes, WithExtraProperties { override val children: List get() = (functions + properties + classlikes) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DObject( override val name: String?, override val dri: DRI, override val documentation: PlatformDependent, override val sources: PlatformDependent, override val functions: List, override val properties: List, override val classlikes: List, override val visibility: PlatformDependent, override val supertypes: PlatformDependent>, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : DClasslike(), WithSupertypes, WithExtraProperties { override val children: List get() = (functions + properties + classlikes) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DAnnotation( override val name: String, override val dri: DRI, override val documentation: PlatformDependent, override val sources: PlatformDependent, override val functions: List, override val properties: List, override val classlikes: List, override val visibility: PlatformDependent, override val companion: DObject?, override val constructors: List, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : DClasslike(), WithCompanion, WithConstructors, WithExtraProperties { override val children: List get() = (functions + properties + classlikes + constructors) as List override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DProperty( override val dri: DRI, override val name: String, override val documentation: PlatformDependent, override val sources: PlatformDependent, override val visibility: PlatformDependent, override val type: Bound, override val receiver: DParameter?, val setter: DFunction?, val getter: DFunction?, override val modifier: PlatformDependent, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), Callable, WithExtraProperties { override val children: List get() = emptyList() override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } // TODO: treat named Parameters and receivers differently data class DParameter( override val dri: DRI, override val name: String?, override val documentation: PlatformDependent, val type: Bound, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), WithExtraProperties { override val children: List get() = emptyList() override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } data class DTypeParameter( override val dri: DRI, override val name: String, override val documentation: PlatformDependent, val bounds: List, override val platformData: List, override val extra: PropertyContainer = PropertyContainer.empty() ) : Documentable(), WithExtraProperties { override val children: List get() = emptyList() override fun withNewExtras(newExtras: PropertyContainer) = copy(extra = newExtras) } sealed class Projection sealed class Bound : Projection() data class OtherParameter(val name: String) : Bound() object Star : Projection() data class TypeConstructor( val dri: DRI, val projections: List, val modifier: FunctionModifiers = FunctionModifiers.NONE ) : Bound() data class Nullable(val inner: Bound) : Bound() data class Variance(val kind: Kind, val inner: Bound) : Projection() { enum class Kind { In, Out } } data class PrimitiveJavaType(val name: String) : Bound() object Void : Bound() object JavaObject : Bound() enum class FunctionModifiers { NONE, FUNCTION, EXTENSION } enum class ExtraModifiers { STATIC, INLINE, INFIX, SUSPEND, REIFIED, CROSSINLINE, NOINLINE, OVERRIDE, DATA, CONST, DYNAMIC, EXTERNAL, INNER, LATEINIT, OPERATOR, TAILREC, VARARG, NATIVE, SYNCHRONIZED, STRICTFP, TRANSIENT, VOLATILE, TRANSITIVE } private fun String.shorten(maxLength: Int) = lineSequence().first().let { if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it } fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = if (predicate(this)) { this } else { this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } sealed class Visibility(val name: String) sealed class KotlinVisibility(name: String) : Visibility(name) { object Public : KotlinVisibility("public") object Private : KotlinVisibility("private") object Protected : KotlinVisibility("protected") object Internal : KotlinVisibility("internal") } sealed class JavaVisibility(name: String) : Visibility(name) { object Public : JavaVisibility("public") object Private : JavaVisibility("private") object Protected : JavaVisibility("protected") object Default : JavaVisibility("") } fun PlatformDependent?.orEmpty(): PlatformDependent = this ?: PlatformDependent.empty() sealed class DocumentableSource(val path: String) class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource(descriptor.toSourceElement.containingFile.toString()) class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource(psi.containingFile.virtualFile.path)