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 package net.mamoe.mirai.internal.network.component
import kotlin.reflect.KClass import kotlin.reflect.*
import kotlin.reflect.KClassifier
import kotlin.reflect.KType
import kotlin.reflect.KTypeParameter
import kotlin.reflect.full.allSupertypes 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. * @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`. * - If [qualified] is `true`, example: `net.mamoe.mirai.internal.network.components.PacketCodec`.
*/ */
fun componentName(qualified: Boolean = false): String { 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 { fun smartToString(qualified: Boolean = false): String {
return "ComponentKey<${componentName(qualified)}>" 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( private fun KClassifier?.renderClassifier(
fullName: Boolean fullName: Boolean
): String { ): String {
@ -53,16 +72,16 @@ internal interface ComponentKey<T : Any> {
val upperBounds = upperBounds val upperBounds = upperBounds
return when (upperBounds.size) { return when (upperBounds.size) {
0 -> toString() 0 -> toString()
1 -> "ComponentKey<${upperBounds[0].renderType(fullName)}>" 1 -> upperBounds[0].renderType(fullName)
else -> "ComponentKey<${upperBounds.joinToString(" & ") { it.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 } 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 org.junit.jupiter.api.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
private class TestComponent { private open class TestComponent {
companion object : ComponentKey<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() { internal class ComponentKeyTest : AbstractTest() {
@Test @Test
@ -25,9 +34,38 @@ internal class ComponentKeyTest : AbstractTest() {
assertEquals(TestComponent::class.qualifiedName!!, TestComponent.componentName(true)) 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 @Test
fun `test smartToString`() { fun `test smartToString`() {
assertEquals("ComponentKey<TestComponent>", TestComponent.smartToString(false)) assertEquals("ComponentKey<TestComponent>", TestComponent.smartToString(false))
assertEquals("ComponentKey<${TestComponent::class.qualifiedName!!}>", TestComponent.smartToString(true)) 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))
} }
} }