aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/kotlin/links/DRI.kt
blob: 7ab254446a6b55220214bd866815abfb30e763f5 (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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package org.jetbrains.dokka.links

import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.types.KotlinType
import java.text.ParseException

/**
 * [DRI] stands for DokkaResourceIdentifier
 */
data class DRI(
    val packageName: String? = null,
    val classNames: String? = null,
    val callable: Callable? = null,
    val target: Int? = null,
    val extra: String? = null
) {

    constructor(
        packageName: String? = null,
        classNames: String? = null,
        callableName: String? = null,
        signature: String? = null,
        target: Int? = null,
        extra: String? = null
    ) : this(packageName, classNames, Callable.from(callableName, signature), target, extra)

    override fun toString(): String =
        "${packageName.orEmpty()}/${classNames.orEmpty()}/${callable?.name.orEmpty()}/${callable?.signature().orEmpty()}/${target?.toString().orEmpty()}/${extra.orEmpty()}"

    companion object {
        fun from(s: String): DRI = try {
            s.split('/')
                .map { it.takeIf(String::isNotBlank) }
                .let { (packageName, classNames, callableName, callableSignature, target, ext) ->
                    DRI(
                        packageName,
                        classNames,
                        try {
                            Callable.from(callableName, callableSignature)
                        } catch (e: ParseException) {
                            null
                        },
                        target?.toInt(),
                        ext
                    )
                }
        } catch (e: Throwable) {
            throw ParseException(s, 0)
        }

        val topLevel = DRI()
    }
}

fun DRI.withClass(name: String) = copy(classNames = classNames.orEmpty() + ".$name")

val DRI.parent: DRI
    get() = when {
        extra != null -> copy(extra = null)
        target != null -> copy(target = null)
        callable != null -> copy(callable = null)
        classNames != null -> copy(classNames = classNames.substringBeforeLast('.').takeIf { it.isNotBlank() })
        else -> DRI.topLevel
    }

data class Callable(val name: String, val receiver: String, val returnType: String, val params: List<String>) {
    fun signature() = "$receiver#$returnType#${params.joinToString("#")}"

    companion object {
        fun from(name: String?, signature: String?): Callable = try {
            signature.toString()
                .split('#', ignoreCase = false, limit = 3)
                .let { (receiver, returnType, params) ->
                    Callable(
                        name.toString(),
                        receiver,
                        returnType,
                        params.split('#')
                    )
                }
        } catch (e: Throwable) {
            throw ParseException(signature, 0)
        }

        fun from(s: String): Callable = try {
            s.split('/').let { (name, signature) -> from(name, signature) }
        } catch (e: Throwable) {
            throw ParseException(s, 0)
        }

        fun from(descriptor: CallableDescriptor) = with(descriptor) {
            Callable(
                name.asString(),
                extensionReceiverParameter?.value?.type?.constructorName.orEmpty(),
                returnType?.constructorName.orEmpty(),
                valueParameters.map { it.type.constructorName.orEmpty() }
            )
        }
    }
}

data class ClassReference(val dri: DRI, val subs: MutableList<ClassReference> = mutableListOf()) {
    private val subsText = subs.takeIf { it.isNotEmpty() }?.toString().orEmpty()
    override fun toString() = "$dri$subsText"
}

private operator fun <T> List<T>.component6(): T = get(5)

private val KotlinType.constructorName
    get() = constructor.declarationDescriptor?.name?.asString()