aboutsummaryrefslogtreecommitdiff
path: root/core/src/test/kotlin/NodeSelect.kt
blob: fe0394f948f9ae634002be0437daf48e60f322d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package org.jetbrains.dokka.tests

import org.jetbrains.dokka.DocumentationNode
import org.jetbrains.dokka.NodeKind
import org.jetbrains.dokka.RefKind

class SelectBuilder {
    private val root = ChainFilterNode(SubgraphTraverseFilter(), null)
    private var activeNode = root
    private val chainEnds = mutableListOf<SelectFilter>()

    fun withName(name: String) = matching { it.name == name }

    fun withKind(kind: NodeKind) = matching{ it.kind == kind }

    fun matching(block: (DocumentationNode) -> Boolean) {
        attachFilterAndMakeActive(PredicateFilter(block))
    }

    fun subgraph() {
        attachFilterAndMakeActive(SubgraphTraverseFilter())
    }

    fun subgraphOf(kind: RefKind) {
        attachFilterAndMakeActive(DirectEdgeFilter(kind))
    }

    private fun attachFilterAndMakeActive(next: SelectFilter) {
        activeNode = ChainFilterNode(next, activeNode)
    }

    private fun endChain() {
        chainEnds += activeNode
    }

    fun build(): SelectFilter {
        endChain()
        return CombineFilterNode(chainEnds)
    }
}

private class ChainFilterNode(val filter: SelectFilter, val previous: SelectFilter?): SelectFilter() {
    override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
        return filter.select(previous?.select(roots) ?: roots)
    }
}

private class CombineFilterNode(val previous: List<SelectFilter>): SelectFilter() {
    override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
        return previous.asSequence().flatMap { it.select(roots) }
    }
}

abstract class SelectFilter {
    abstract fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode>
}

private class SubgraphTraverseFilter: SelectFilter() {
    override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
        val visited = mutableSetOf<DocumentationNode>()
        return roots.flatMap {
            generateSequence(listOf(it)) { nodes ->
                nodes.flatMap { it.allReferences() }
                    .map { it.to }
                    .filter { visited.add(it) }
                    .takeUnless { it.isEmpty() }
            }
        }.flatten()
    }

}

private class PredicateFilter(val condition: (DocumentationNode) -> Boolean): SelectFilter() {
    override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
        return roots.filter(condition)
    }
}

private class DirectEdgeFilter(val kind: RefKind): SelectFilter() {
    override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> {
        return roots.flatMap { it.references(kind).asSequence() }.map { it.to }
    }
}


fun selectNodes(root: DocumentationNode, block: SelectBuilder.() -> Unit): List<DocumentationNode> {
    val builder = SelectBuilder()
    builder.apply(block)
    return builder.build().select(sequenceOf(root)).toMutableSet().toList()
}