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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
package org.jetbrains.dokka.links
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
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)
}
fun from(descriptor: DeclarationDescriptor) = descriptor.parentsWithSelf.run {
val callable = firstIsInstanceOrNull<CallableDescriptor>()
val params = callable?.let { listOfNotNull(it.extensionReceiverParameter) + it.valueParameters }.orEmpty()
DRI(
firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString(),
filterIsInstance<ClassDescriptor>().toList().takeIf { it.isNotEmpty() }?.asReversed()
?.joinToString(separator = ".") { it.name.asString() },
callable?.let { Callable.from(it) },
firstIsInstanceOrNull<ParameterDescriptor>()?.let { params.indexOf(it) },
null
)
}
val topLevel = DRI()
}
}
fun DRI.withClass(name: String) = copy(classNames = if(classNames.isNullOrBlank()) name else "$classNames.$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()
|