fix componentName and smartToString

This commit is contained in:
Him188 2021-04-24 15:26:38 +08:00
parent c35c2c97c8
commit 3806d7ce78
2 changed files with 70 additions and 13 deletions

View File

@ -9,14 +9,11 @@
package net.mamoe.mirai.internal.network.component
import kotlin.reflect.KClass
import kotlin.reflect.KClassifier
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.*
import kotlin.reflect.full.allSupertypes
/**
* A key for specific component [T]. Component are not polymorphic.
* A key for specific component [T]. Components are not polymorphic.
*
* @param T is a type hint.
*/
@ -28,14 +25,36 @@ internal interface ComponentKey<T : Any> {
* - If [qualified] is `true`, example: `net.mamoe.mirai.internal.network.components.PacketCodec`.
*/
fun componentName(qualified: Boolean = false): String {
return getComponentTypeArgumentClassifier().renderClassifier(fullName = qualified)
val argument = getComponentTypeArgument()
argument?.render(qualified)?.let { return it }
return argument?.type?.classifier.renderClassifier(qualified)
}
fun smartToString(qualified: Boolean = false): String {
return "ComponentKey<${componentName(qualified)}>"
}
private companion object {
companion object {
// reflection is slow, but it is initialized once only (if memory sufficient).
private fun KTypeProjection.render(
fullName: Boolean
): String? {
val projection = this
projection.type?.classifier?.let { classifier ->
if (classifier is KClass<*>) {
return classifier.run { if (fullName) qualifiedName else simpleName } ?: "?"
}
}
projection.type?.arguments?.firstOrNull()?.let { argument ->
return argument.render(fullName)
}
return null
}
private fun KClassifier?.renderClassifier(
fullName: Boolean
): String {
@ -53,16 +72,16 @@ internal interface ComponentKey<T : Any> {
val upperBounds = upperBounds
return when (upperBounds.size) {
0 -> toString()
1 -> "ComponentKey<${upperBounds[0].renderType(fullName)}>"
else -> "ComponentKey<${upperBounds.joinToString(" & ") { it.renderType(fullName) }}>"
1 -> upperBounds[0].renderType(fullName)
else -> upperBounds.joinToString(" & ") { it.renderType(fullName) }
}
}
private fun ComponentKey<*>.getComponentTypeArgumentClassifier(): KClassifier? {
private fun ComponentKey<*>.getComponentTypeArgument(): KTypeProjection? {
val thisType = this::class.allSupertypes.find { it.classifier == COMPONENT_KEY_K_CLASS }
return thisType?.arguments?.firstOrNull()?.type?.classifier
return thisType?.arguments?.firstOrNull()
}
val COMPONENT_KEY_K_CLASS = ComponentKey::class
private val COMPONENT_KEY_K_CLASS = ComponentKey::class
}
}

View File

@ -13,10 +13,19 @@ import net.mamoe.mirai.internal.test.AbstractTest
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
private class TestComponent {
private open class TestComponent {
companion object : ComponentKey<TestComponent>
}
private interface MyInterface
private open class TestComponentExt : TestComponent(), MyInterface
private open class Key<R> : ComponentKey<R> where R : TestComponent, R : MyInterface
private class KeyActual : Key<TestComponentExt>()
internal class ComponentKeyTest : AbstractTest() {
@Test
@ -25,9 +34,38 @@ internal class ComponentKeyTest : AbstractTest() {
assertEquals(TestComponent::class.qualifiedName!!, TestComponent.componentName(true))
}
@Test
fun `componentName with erased type argument`() {
assertEquals(
"TestComponent & MyInterface",
Key<TestComponentExt>().componentName(false)
)
assertEquals(
"${TestComponent::class.qualifiedName!!} & ${MyInterface::class.qualifiedName!!}",
Key<TestComponentExt>().componentName(true)
)
}
@Test
fun `componentName with actual type argument`() {
assertEquals(
"TestComponentExt",
KeyActual().componentName(false)
)
assertEquals(
TestComponentExt::class.qualifiedName!!,
KeyActual().componentName(true)
)
}
@Test
fun `test smartToString`() {
assertEquals("ComponentKey<TestComponent>", TestComponent.smartToString(false))
assertEquals("ComponentKey<${TestComponent::class.qualifiedName!!}>", TestComponent.smartToString(true))
assertEquals(
"ComponentKey<${TestComponent::class.qualifiedName!!} & ${MyInterface::class.qualifiedName!!}>",
Key<TestComponentExt>().smartToString(true)
)
assertEquals("ComponentKey<${TestComponentExt::class.qualifiedName!!}>", KeyActual().smartToString(true))
}
}