package org.jetbrains.dokka.pages import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.Documentable import java.util.* interface PageNode { val name: String val children: List fun modified( name: String = this.name, children: List = this.children ): PageNode } interface ContentPage: PageNode { val content: ContentNode val dri: Set val documentable: Documentable? val embeddedResources: List fun modified( name: String = this.name, content: ContentNode = this.content, dri: Set = this.dri, embeddedResources: List = this.embeddedResources, children: List = this.children ): ContentPage } abstract class RootPageNode: PageNode { val parentMap: Map by lazy { IdentityHashMap().apply { fun process(parent: PageNode) { parent.children.forEach { child -> put(child, parent) process(child) } } process(this@RootPageNode) } } fun transformPageNodeTree(operation: (PageNode) -> PageNode) = this.transformNode(operation) as RootPageNode fun transformContentPagesTree(operation: (ContentPage) -> ContentPage) = transformPageNodeTree { if (it is ContentPage) operation(it) else it } private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode = operation(this).let { newNode -> newNode.modified(children = newNode.children.map { it.transformNode(operation) }) } abstract override fun modified( name: String, children: List ): RootPageNode } class ModulePageNode( override val name: String, override val content: ContentNode, override val documentable: Documentable?, override val children: List, override val embeddedResources: List = listOf() ) : RootPageNode(), ContentPage { override val dri: Set = setOf(DRI.topLevel) override fun modified(name: String, children: List): ModulePageNode = modified(name = name, content = this.content, dri = dri, children = children) override fun modified( name: String, content: ContentNode, dri: Set, embeddedResources: List, children: List ): ModulePageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this else ModulePageNode(name, content, documentable, children, embeddedResources) } class PackagePageNode( override val name: String, override val content: ContentNode, override val dri: Set, override val documentable: Documentable?, override val children: List, override val embeddedResources: List = listOf() ) : ContentPage { override fun modified(name: String, children: List): PackagePageNode = modified(name = name, content = this.content, children = children) override fun modified( name: String, content: ContentNode, dri: Set, embeddedResources: List, children: List ): PackagePageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this else PackagePageNode(name, content, dri, documentable, children, embeddedResources) } class ClasslikePageNode( override val name: String, override val content: ContentNode, override val dri: Set, override val documentable: Documentable?, override val children: List, override val embeddedResources: List = listOf() ) : ContentPage { override fun modified(name: String, children: List): ClasslikePageNode = modified(name = name, content = this.content, children = children) override fun modified( name: String, content: ContentNode, dri: Set, embeddedResources: List, children: List ): ClasslikePageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this else ClasslikePageNode(name, content, dri, documentable, children, embeddedResources) } class MemberPageNode( override val name: String, override val content: ContentNode, override val dri: Set, override val documentable: Documentable?, override val children: List = emptyList(), override val embeddedResources: List = listOf() ) : ContentPage { override fun modified(name: String, children: List): MemberPageNode = modified(name = name, content = this.content, children = children) as MemberPageNode override fun modified( name: String, content: ContentNode, dri: Set, embeddedResources: List, children: List ): MemberPageNode = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this else MemberPageNode(name, content, dri, documentable, children, embeddedResources) } fun PageNode.dfs(predicate: (PageNode) -> Boolean): PageNode? = if (predicate(this)) { this } else { this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } fun PageNode.asSequence(): Sequence = sequence { yield(this@asSequence) children.asSequence().flatMap { it.asSequence() }.forEach { yield(it) } } class MultimoduleRootPageNode( override val name: String, override val dri: Set, override val content: ContentNode, override val embeddedResources: List = emptyList() ) : RootPageNode(), ContentPage { override val children: List = emptyList() override val documentable: Documentable? = null override fun modified(name: String, children: List): RootPageNode = MultimoduleRootPageNode(name, dri, content, embeddedResources) override fun modified( name: String, content: ContentNode, dri: Set, embeddedResources: List, children: List ) = if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this else MultimoduleRootPageNode(name, dri, content, embeddedResources) } inline fun PageNode.children() = children.filterIsInstance() private infix fun List.shallowEq(other: List) = this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b })