aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/model/WithChildren.kt
blob: 01a188c803423910b8b115ffa1e2d984fe7e1834 (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
91
92
93
94
package org.jetbrains.dokka.model

interface WithChildren<out T> {
    val children: List<T>
}

inline fun <reified T> WithChildren<*>.firstChildOfTypeOrNull(): T? =
    children.filterIsInstance<T>().firstOrNull()

inline fun <reified T> WithChildren<*>.firstChildOfTypeOrNull(predicate: (T) -> Boolean): T? =
    children.filterIsInstance<T>().firstOrNull(predicate)

inline fun <reified T> WithChildren<*>.firstChildOfType(): T =
    children.filterIsInstance<T>().first()

inline fun <reified T> WithChildren<*>.childrenOfType(): List<T> =
    children.filterIsInstance<T>()

inline fun <reified T> WithChildren<*>.firstChildOfType(predicate: (T) -> Boolean): T =
    children.filterIsInstance<T>().first(predicate)

inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfType(): T where T : WithChildren<*> {
    return withDescendants().filterIsInstance<T>().first()
}

inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfType(
    predicate: (T) -> Boolean
): T where T : WithChildren<*> = withDescendants().filterIsInstance<T>().first(predicate)


inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfTypeOrNull(): T? where T : WithChildren<*> {
    return withDescendants().filterIsInstance<T>().firstOrNull()
}

fun <T> T.withDescendants(): Sequence<T> where T : WithChildren<T> {
    return sequence {
        yield(this@withDescendants)
        children.forEach { child ->
            yieldAll(child.withDescendants())
        }
    }
}

@JvmName("withDescendantsProjection")
fun WithChildren<*>.withDescendants(): Sequence<Any?> {
    return sequence {
        yield(this@withDescendants)
        children.forEach { child ->
            if (child is WithChildren<*>) {
                yieldAll(child.withDescendants())
            }
        }
    }
}

@JvmName("withDescendantsAny")
fun WithChildren<Any>.withDescendants(): Sequence<Any> {
    return sequence {
        yield(this@withDescendants)
        children.forEach { child ->
            if (child is WithChildren<*>) {
                yieldAll(child.withDescendants().filterNotNull())
            }
        }
    }
}

fun <T> T.dfs(predicate: (T) -> Boolean): T? where T : WithChildren<T> = if (predicate(this)) {
    this
} else {
    children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull()
}

fun <T : WithChildren<T>> T.asPrintableTree(
    nodeNameBuilder: Appendable.(T) -> Unit = { append(it.toString()) }
): String {
    fun Appendable.append(element: T, ownPrefix: String, childPrefix: String) {
        append(ownPrefix)
        nodeNameBuilder(element)
        appendLine()
        element.children.takeIf(Collection<*>::isNotEmpty)?.also { children ->
            val newOwnPrefix = "$childPrefix├─ "
            val lastOwnPrefix = "$childPrefix└─ "
            val newChildPrefix = "$childPrefix│  "
            val lastChildPrefix = "$childPrefix   "
            children.forEachIndexed { n, e ->
                if (n != children.lastIndex) append(e, newOwnPrefix, newChildPrefix)
                else append(e, lastOwnPrefix, lastChildPrefix)
            }
        }
    }

    return buildString { append(this@asPrintableTree, "", "") }
}