[kpm] Replace KotlinExternalModelContainer with new Extras implementation
This newly introduced `Extras` shall present a more generic mechanism of attaching data of a given type to any entity.
This commit is contained in:
committed by
Space
parent
c777ecd470
commit
4f4f749c08
+152
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import org.jetbrains.kotlin.tooling.core.Extras.Entry
|
||||
import org.jetbrains.kotlin.tooling.core.Extras.Key
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* A generic container holding typed and scoped values.
|
||||
* ### Attaching and getting simple typed values:
|
||||
* ```kotlin
|
||||
* val extras = mutableExtrasOf()
|
||||
* extras[extrasKeyOf<Int>()] = 42 // Attach arbitrary Int value
|
||||
* extras[extrasKeyOf<String>()] = "Hello" // Attach arbitrary String value
|
||||
*
|
||||
* extras[extrasKeyOf<Int>()] // -> returns 42
|
||||
* extras[extrasKeyOf<String>] // -> returns "Hello"
|
||||
* ```
|
||||
*
|
||||
* ### Attaching multiple values with the same type by naming the keys
|
||||
* ```kotlin
|
||||
* val extras = mutableExtrasOf()
|
||||
* extras[extrasKeyOf<Int>("a")] = 1 // Attach Int with name 'a'
|
||||
* extras[extrasKeyOf<Int>("b")] = 2 // Attach Int with name 'b'
|
||||
*
|
||||
* extras[extrasKeyOf<Int>("a")] // -> returns 1
|
||||
* extras[extrasKeyOf<Int>("b")] // -> returns 2
|
||||
* ```
|
||||
*
|
||||
* ### Creating immutable extras
|
||||
* ```kotlin
|
||||
* val extras = extrasOf(
|
||||
* extrasKeyOf<Int>() withValue 1,
|
||||
* extrasKeyOf<String>() withValue "Hello"
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* ### Converting to immutable extras
|
||||
* ```kotlin
|
||||
* val extras = mutableExtrasOf(
|
||||
* extrasKeyOf<Int>() withValue 0
|
||||
* )
|
||||
*
|
||||
* // Captures the content, similar to `.toList()` or `.toSet()`
|
||||
* val immutableExtras = extras.toExtras()
|
||||
* ```
|
||||
*
|
||||
* ### Use case example: Filtering Extras
|
||||
* ```kotlin
|
||||
* val extras = extrasOf(
|
||||
* extrasKeyOf<Int>() withValue 0,
|
||||
* extrasKeyOf<Int>("keep") withValue 1,
|
||||
* extrasKeyOf<String>() withValue "Hello"
|
||||
* )
|
||||
*
|
||||
* val filteredExtras = extras
|
||||
* .filter { (key, value) -> key.id.name == "keep" || value is String }
|
||||
* .toExtras()
|
||||
* ```
|
||||
*/
|
||||
interface Extras : Collection<Entry<out Any>> {
|
||||
/* Not implemented as data class to ensure more controllable binary compatibility */
|
||||
class Key<T : Any> @PublishedApi internal constructor(
|
||||
internal val type: ReifiedTypeSignature<T>,
|
||||
val name: String? = null,
|
||||
) : Serializable {
|
||||
|
||||
val stableString: String
|
||||
get() {
|
||||
return if (name == null) type.signature
|
||||
else "${type.signature};$name"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Key<*>) return false
|
||||
if (name != other.name) return false
|
||||
if (type != other.type) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = name?.hashCode() ?: 0
|
||||
result = 31 * result + type.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String = stableString
|
||||
|
||||
companion object {
|
||||
fun fromString(stableString: String): Key<*> {
|
||||
@OptIn(UnsafeApi::class) return if (stableString.contains(';')) {
|
||||
val split = stableString.split(';', limit = 2)
|
||||
Key(ReifiedTypeSignature(split[0]), split[1])
|
||||
} else Key(ReifiedTypeSignature(stableString))
|
||||
}
|
||||
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
/* Not implemented as data class to ensure more controllable binary compatibility */
|
||||
class Entry<T : Any>(val key: Key<T>, val value: T) : Serializable {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is Entry<*>) return false
|
||||
if (other.key != key) return false
|
||||
if (other.value != value) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = key.hashCode()
|
||||
result = 31 * result + value.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String = "$key=$value"
|
||||
|
||||
operator fun component1() = key
|
||||
operator fun component2() = value
|
||||
|
||||
internal companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
val keys: Set<Key<*>>
|
||||
val entries: Set<Entry<*>>
|
||||
operator fun <T : Any> get(key: Key<T>): T?
|
||||
operator fun contains(key: Key<*>): Boolean
|
||||
override fun iterator(): Iterator<Entry<out Any>>
|
||||
}
|
||||
|
||||
interface MutableExtras : Extras {
|
||||
/**
|
||||
* @return The previous value or null if no previous value was set
|
||||
*/
|
||||
operator fun <T : Any> set(key: Key<T>, value: T): T?
|
||||
|
||||
fun <T : Any> put(entry: Entry<T>): T?
|
||||
|
||||
fun putAll(from: Iterable<Entry<*>>)
|
||||
|
||||
fun <T : Any> remove(key: Key<T>): T?
|
||||
|
||||
fun clear()
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import org.jetbrains.kotlin.tooling.core.Extras.Entry
|
||||
import org.jetbrains.kotlin.tooling.core.Extras.Key
|
||||
import java.io.Serializable
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
internal class MutableExtrasImpl(
|
||||
initialEntries: Iterable<Entry<*>> = emptyList()
|
||||
) : MutableExtras, AbstractExtras(), Serializable {
|
||||
|
||||
private val extras: MutableMap<Key<*>, Entry<*>> =
|
||||
initialEntries.associateByTo(mutableMapOf()) { it.key }
|
||||
|
||||
override val keys: Set<Key<*>>
|
||||
get() = extras.keys.toSet()
|
||||
|
||||
override val entries: Set<Entry<*>>
|
||||
get() = extras.values.toSet()
|
||||
|
||||
override val size: Int
|
||||
get() = extras.size
|
||||
|
||||
override fun isEmpty(): Boolean = extras.isEmpty()
|
||||
|
||||
override fun <T : Any> set(key: Key<T>, value: T): T? {
|
||||
return put(Entry(key, value))
|
||||
}
|
||||
|
||||
override fun <T : Any> put(entry: Entry<T>): T? {
|
||||
return extras.put(entry.key, entry)?.let { it.value as T }
|
||||
}
|
||||
|
||||
override fun putAll(from: Iterable<Entry<*>>) {
|
||||
this.extras.putAll(from.associateBy { it.key })
|
||||
}
|
||||
|
||||
override fun <T : Any> get(key: Key<T>): T? {
|
||||
return extras[key]?.let { it.value as T }
|
||||
}
|
||||
|
||||
override fun <T : Any> remove(key: Key<T>): T? {
|
||||
return extras.remove(key)?.let { it.value as T }
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
extras.clear()
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
internal class ImmutableExtrasImpl private constructor(
|
||||
private val extras: Map<Key<*>, Entry<*>>
|
||||
) : AbstractExtras(), Serializable {
|
||||
constructor(extras: Iterable<Entry<*>>) : this(extras.associateBy { it.key })
|
||||
|
||||
constructor(extras: Array<out Entry<*>>) : this(extras.associateBy { it.key })
|
||||
|
||||
override val keys: Set<Key<*>> = extras.keys
|
||||
|
||||
override fun isEmpty(): Boolean = extras.isEmpty()
|
||||
|
||||
override val size: Int = extras.size
|
||||
|
||||
override val entries: Set<Entry<*>> = extras.values.toSet()
|
||||
|
||||
override fun <T : Any> get(key: Key<T>): T? {
|
||||
return extras[key]?.let { it.value as T }
|
||||
}
|
||||
|
||||
internal companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
|
||||
/* Replace during serialization */
|
||||
private fun writeReplace(): Any = Surrogate(entries)
|
||||
|
||||
private class Surrogate(private val entries: Set<Entry<*>>) : Serializable {
|
||||
fun readResolve(): Any = ImmutableExtrasImpl(entries)
|
||||
|
||||
private companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractExtras : Extras {
|
||||
|
||||
override val size: Int get() = keys.size
|
||||
|
||||
override fun isEmpty(): Boolean = keys.isEmpty()
|
||||
|
||||
override fun contains(key: Key<*>): Boolean = key in keys
|
||||
|
||||
override fun contains(element: Entry<*>): Boolean =
|
||||
entries.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<Entry<*>>): Boolean =
|
||||
entries.containsAll(elements)
|
||||
|
||||
override fun iterator(): Iterator<Entry<out Any>> = entries.iterator()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other === this) return true
|
||||
if (other !is Extras) return false
|
||||
if (other.entries != this.entries) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return 31 * entries.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Extras($entries)"
|
||||
}
|
||||
}
|
||||
|
||||
internal object EmptyExtras : AbstractExtras(), Serializable {
|
||||
|
||||
override val size: Int = 0
|
||||
|
||||
override val keys: Set<Key<*>> = emptySet()
|
||||
|
||||
override val entries: Set<Entry<*>> = emptySet()
|
||||
|
||||
override fun isEmpty(): Boolean = true
|
||||
|
||||
override fun <T : Any> get(key: Key<T>): T? = null
|
||||
|
||||
override fun contains(key: Key<*>): Boolean = false
|
||||
|
||||
override fun contains(element: Entry<out Any>): Boolean = false
|
||||
|
||||
@Suppress("unused") // Necessary for java.io.Serializable stability
|
||||
private const val serialVersionUID = 0L
|
||||
|
||||
/* Ensure single instance, even after deserialization */
|
||||
private fun readResolve(): Any = EmptyExtras
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
/**
|
||||
* Creates a value based key for accessing any [Extras] container
|
||||
*
|
||||
* @param T The type of data that is stored in the extras container
|
||||
* ```kotlin
|
||||
* extrasKeyOf<Int>() == extrasKeyOf<Int>()
|
||||
* extrasKeyOf<Int>() != extrasKeyOf<String>()
|
||||
* extrasKeyOf<List<Int>>() == extrasKeyOf<List<Int>>()
|
||||
* extrasKeyOf<List<*>>() != extrasKeyOf<List<Int>>()
|
||||
* ```
|
||||
*
|
||||
* @param name This typed keys can also be distinguished with an additional name. In this case
|
||||
* ```kotlin
|
||||
* extrasKeyOf<Int>() != extrasKeyOf<Int>("a")
|
||||
* extrasKeyOf<Int>("a") == extrasKeyOf<Int>("a")
|
||||
* extrasKeyOf<Int>("b") != extrasKeyOf<Int>("a")
|
||||
* extrasKeyOf<String>("a") != extrasKeyOf<Int>("a")
|
||||
* ```
|
||||
*/
|
||||
inline fun <reified T : Any> extrasKeyOf(name: String? = null): Extras.Key<T> =
|
||||
Extras.Key(ReifiedTypeSignature(), name)
|
||||
|
||||
fun emptyExtras(): Extras = EmptyExtras
|
||||
|
||||
fun extrasOf() = emptyExtras()
|
||||
|
||||
fun extrasOf(vararg entries: Extras.Entry<*>): Extras = if (entries.isEmpty()) EmptyExtras else ImmutableExtrasImpl(entries)
|
||||
|
||||
fun mutableExtrasOf(): MutableExtras = MutableExtrasImpl()
|
||||
|
||||
fun mutableExtrasOf(vararg entries: Extras.Entry<*>): MutableExtras = MutableExtrasImpl(entries.toList())
|
||||
|
||||
fun Iterable<Extras.Entry<*>>.toExtras(): Extras = ImmutableExtrasImpl(this)
|
||||
|
||||
fun Iterable<Extras.Entry<*>>.toMutableExtras(): MutableExtras = MutableExtrasImpl(this)
|
||||
|
||||
infix fun <T : Any> Extras.Key<T>.withValue(value: T): Extras.Entry<T> = Extras.Entry(this, value)
|
||||
|
||||
operator fun Extras.plus(entry: Extras.Entry<*>): Extras = ImmutableExtrasImpl(this.entries + entry)
|
||||
|
||||
operator fun Extras.plus(entries: Iterable<Extras.Entry<*>>): Extras = ImmutableExtrasImpl(this.entries + entries)
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("FunctionName")
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import java.io.Serializable
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.KVariance
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
@PublishedApi
|
||||
internal inline fun <reified T> ReifiedTypeSignature(): ReifiedTypeSignature<T> {
|
||||
@OptIn(UnsafeApi::class, ExperimentalStdlibApi::class)
|
||||
return ReifiedTypeSignature(renderReifiedTypeSignatureString(typeOf<T>()))
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal class ReifiedTypeSignature<T>
|
||||
@UnsafeApi("Use 'reifiedTypeSignatureOf' instead")
|
||||
@PublishedApi internal constructor(val signature: String) : Serializable {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is ReifiedTypeSignature<*>) return false
|
||||
if (other.signature != this.signature) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return 31 * signature.hashCode()
|
||||
}
|
||||
|
||||
override fun toString(): String = signature
|
||||
|
||||
internal companion object {
|
||||
private const val serialVersionUID = 0L
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@UnsafeApi("Use 'ReifiedTypeSignature' instead")
|
||||
internal fun renderReifiedTypeSignatureString(type: KType): String {
|
||||
val classifier = type.classifier ?: throw IllegalArgumentException("Expected denotable type, found $type")
|
||||
val classifierClass = classifier as? KClass<*> ?: throw IllegalArgumentException("Expected class type, found $type")
|
||||
val classifierName = classifierClass.qualifiedName ?: throw IllegalArgumentException(
|
||||
"Expected non-anonymous, non-local type, found $type"
|
||||
)
|
||||
|
||||
/* Fast path: Just a non-nullable class without arguments */
|
||||
if (type.arguments.isEmpty() && !type.isMarkedNullable) {
|
||||
return classifierName
|
||||
}
|
||||
|
||||
return buildString {
|
||||
append(classifierName)
|
||||
if (type.arguments.isNotEmpty()) {
|
||||
append("<")
|
||||
type.arguments.forEachIndexed forEach@{ index, argument ->
|
||||
if (argument.type == null || argument.variance == null) {
|
||||
append("*")
|
||||
return@forEach
|
||||
}
|
||||
when (argument.variance) {
|
||||
KVariance.IN -> append("in ")
|
||||
KVariance.OUT -> append("out ")
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
append(renderReifiedTypeSignatureString(argument.type ?: return@forEach))
|
||||
if (index != type.arguments.lastIndex) {
|
||||
append(", ")
|
||||
}
|
||||
}
|
||||
append(">")
|
||||
}
|
||||
if (type.isMarkedNullable) {
|
||||
append("?")
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Use safe API counterpart")
|
||||
annotation class UnsafeApi(val message: String = "")
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ExtrasKeyStableStringTest {
|
||||
@Test
|
||||
fun `test - sample 0`() {
|
||||
assertEquals("kotlin.String", extrasKeyOf<String>().stableString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 1`() {
|
||||
assertEquals("kotlin.String;withName", extrasKeyOf<String>("withName").stableString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 2`() {
|
||||
assertEquals(extrasKeyOf<String>(), Extras.Key.fromString(extrasKeyOf<String>().stableString))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 3`() {
|
||||
assertEquals(extrasKeyOf<String>("withName"), Extras.Key.fromString(extrasKeyOf<String>("withName").stableString))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 4`() {
|
||||
assertEquals(
|
||||
extrasKeyOf<List<Pair<Int, String>>>("withName"),
|
||||
Extras.Key.fromString(
|
||||
extrasKeyOf<List<Pair<Int, String>>>("withName").stableString
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotSame
|
||||
import kotlin.test.assertSame
|
||||
|
||||
class ExtrasSerializableTest {
|
||||
|
||||
@Test
|
||||
fun `test - serialize deserialize - MutableExtras`() {
|
||||
val extras = mutableExtrasOf(extrasKeyOf<String>() withValue "hello")
|
||||
val deserializedExtras = extras.serialize().deserialize<Extras>()
|
||||
assertNotSame(extras, deserializedExtras)
|
||||
assertEquals(extras, deserializedExtras)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - serialize deserialize - EmptyExtras`() {
|
||||
assertSame(emptyExtras(), emptyExtras())
|
||||
assertSame(emptyExtras(), emptyExtras().serialize().deserialize())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - serialize deserialize - ImmutableExtras`() {
|
||||
val extras = extrasOf(extrasKeyOf<String>() withValue "hello")
|
||||
val deserializedExtras = extras.serialize().deserialize<Extras>()
|
||||
assertNotSame(extras, deserializedExtras)
|
||||
assertEquals(extras, deserializedExtras)
|
||||
assertSame(extras.javaClass, deserializedExtras.javaClass)
|
||||
}
|
||||
|
||||
private fun Any.serialize(): ByteArray {
|
||||
return ByteArrayOutputStream().use { byteArrayOutputStream ->
|
||||
ObjectOutputStream(byteArrayOutputStream).use { objectOutputStream -> objectOutputStream.writeObject(this) }
|
||||
byteArrayOutputStream.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <reified T : Any> ByteArray.deserialize(): T {
|
||||
return ObjectInputStream(ByteArrayInputStream(this)).use { stream ->
|
||||
stream.readObject() as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
@file:Suppress("RemoveExplicitTypeArguments")
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
class ExtrasTest {
|
||||
|
||||
@Test
|
||||
fun `test - isEmpty`() {
|
||||
assertTrue(mutableExtrasOf().isEmpty())
|
||||
assertTrue(extrasOf().isEmpty())
|
||||
assertTrue(emptyExtras().isEmpty())
|
||||
|
||||
assertFalse(mutableExtrasOf().isNotEmpty())
|
||||
assertFalse(extrasOf().isNotEmpty())
|
||||
assertFalse(emptyExtras().isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - add and get`() {
|
||||
val extras = mutableExtrasOf()
|
||||
assertNull(extras[extrasKeyOf<String>()])
|
||||
assertNull(extras[extrasKeyOf<String>("a")])
|
||||
assertNull(extras[extrasKeyOf<String>("b")])
|
||||
|
||||
extras[extrasKeyOf()] = "22222"
|
||||
assertEquals("22222", extras[extrasKeyOf()])
|
||||
assertNull(extras[extrasKeyOf("a")])
|
||||
assertNull(extras[extrasKeyOf("b")])
|
||||
|
||||
extras[extrasKeyOf("a")] = "value a"
|
||||
assertEquals("22222", extras[extrasKeyOf()])
|
||||
assertEquals("value a", extras[extrasKeyOf("a")])
|
||||
assertNull(extras[extrasKeyOf("b")])
|
||||
|
||||
extras[extrasKeyOf("b")] = "value b"
|
||||
assertEquals("22222", extras[extrasKeyOf()])
|
||||
assertEquals("value a", extras[extrasKeyOf("a")])
|
||||
assertEquals("value b", extras[extrasKeyOf("b")])
|
||||
|
||||
assertNull(extras[extrasKeyOf("c")])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - ids`() {
|
||||
val stringKey = extrasKeyOf<String>()
|
||||
val stringKeyA = extrasKeyOf<String>("a")
|
||||
val intKey = extrasKeyOf<Int>()
|
||||
val intKeyA = extrasKeyOf<Int>("a")
|
||||
|
||||
val keys = setOf(stringKey, stringKeyA, intKey, intKeyA)
|
||||
|
||||
val extras = extrasOf(
|
||||
stringKey withValue "string",
|
||||
stringKeyA withValue "stringA",
|
||||
intKey withValue 1,
|
||||
intKeyA withValue 2
|
||||
)
|
||||
|
||||
val mutableExtras = mutableExtrasOf(
|
||||
stringKey withValue "string",
|
||||
stringKeyA withValue "stringA",
|
||||
intKey withValue 1,
|
||||
intKeyA withValue 2
|
||||
)
|
||||
|
||||
assertEquals(keys, extras.keys)
|
||||
assertEquals(keys, mutableExtras.keys)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - keys`() {
|
||||
val stringKey = extrasKeyOf<String>()
|
||||
val stringKeyA = extrasKeyOf<String>("a")
|
||||
val intKey = extrasKeyOf<Int>()
|
||||
val intKeyA = extrasKeyOf<Int>("a")
|
||||
|
||||
val keys = listOf(stringKey, stringKeyA, intKey, intKeyA)
|
||||
|
||||
val extras = extrasOf(
|
||||
stringKey withValue "string",
|
||||
stringKeyA withValue "stringA",
|
||||
intKey withValue 1,
|
||||
intKeyA withValue 2
|
||||
)
|
||||
|
||||
val mutableExtras = mutableExtrasOf(
|
||||
stringKey withValue "string",
|
||||
stringKeyA withValue "stringA",
|
||||
intKey withValue 1,
|
||||
intKeyA withValue 2
|
||||
)
|
||||
|
||||
assertEquals(keys, extras.entries.map { it.key })
|
||||
assertEquals(keys, mutableExtras.entries.map { it.key })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - equality`() {
|
||||
val stringKey = extrasKeyOf<String>()
|
||||
val stringKeyA = extrasKeyOf<String>("a")
|
||||
val intKey = extrasKeyOf<Int>()
|
||||
val intKeyA = extrasKeyOf<Int>("a")
|
||||
|
||||
val extras = extrasOf(
|
||||
stringKey withValue "string",
|
||||
stringKeyA withValue "stringA",
|
||||
intKey withValue 1,
|
||||
intKeyA withValue 2
|
||||
)
|
||||
|
||||
val mutableExtras = mutableExtrasOf(
|
||||
stringKey withValue "string",
|
||||
stringKeyA withValue "stringA",
|
||||
intKey withValue 1,
|
||||
intKeyA withValue 2
|
||||
)
|
||||
|
||||
assertEquals(extras, mutableExtras)
|
||||
assertEquals(extras, mutableExtras.toExtras())
|
||||
assertEquals(extras.toExtras(), mutableExtras.toExtras())
|
||||
assertEquals(extras.toMutableExtras(), mutableExtras.toExtras())
|
||||
assertEquals(extras.toMutableExtras(), mutableExtras)
|
||||
assertEquals(mutableExtras, extras)
|
||||
|
||||
assertNotEquals(extras, extras + extrasKeyOf<Int>("b").withValue(2))
|
||||
assertNotEquals(mutableExtras, mutableExtras + extrasKeyOf<Int>("b").withValue(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - equality - empty`() {
|
||||
val extras0 = extrasOf()
|
||||
val extras1 = mutableExtrasOf()
|
||||
val extras2 = mutableExtrasOf()
|
||||
|
||||
assertNotSame(extras0, extras1)
|
||||
assertNotSame(extras1, extras2)
|
||||
assertEquals(extras0, extras1)
|
||||
assertEquals(extras1, extras2)
|
||||
assertEquals(extras2, extras1)
|
||||
assertEquals(emptyExtras(), extras0)
|
||||
assertEquals(emptyExtras(), extras1)
|
||||
assertEquals(extras0, emptyExtras())
|
||||
assertEquals(extras1, emptyExtras())
|
||||
assertEquals(emptyExtras(), extras2)
|
||||
assertEquals(extras2, emptyExtras())
|
||||
|
||||
assertEquals(extras0.hashCode(), extras1.hashCode())
|
||||
assertEquals(extras1.hashCode(), extras2.hashCode())
|
||||
assertEquals(extras1.hashCode(), emptyExtras().hashCode())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - overwrite - mutable`() {
|
||||
val key = extrasKeyOf<Int>()
|
||||
val extras = mutableExtrasOf()
|
||||
assertNull(extras.set(key, 1))
|
||||
assertEquals(1, extras[key])
|
||||
assertEquals(1, extras.set(key, 2))
|
||||
assertEquals(2, extras[key])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - overwrite - immutable`() {
|
||||
val key = extrasKeyOf<Int>()
|
||||
val extras0 = extrasOf()
|
||||
val extras1 = extras0 + (key withValue 1)
|
||||
assertNull(extras0[key])
|
||||
assertEquals(1, extras1[key])
|
||||
|
||||
val extras2 = extras1 + (key withValue 2)
|
||||
assertNull(extras0[key])
|
||||
assertEquals(1, extras1[key])
|
||||
assertEquals(2, extras2[key])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - key equality`() {
|
||||
assertEquals(extrasKeyOf<Int>(), extrasKeyOf<Int>())
|
||||
assertEquals(extrasKeyOf<List<String>>(), extrasKeyOf<List<String>>())
|
||||
assertEquals(extrasKeyOf<Int>("a"), extrasKeyOf<Int>("a"))
|
||||
assertNotEquals<Extras.Key<*>>(extrasKeyOf<Int>(), extrasKeyOf<String>())
|
||||
assertNotEquals<Extras.Key<*>>(extrasKeyOf<Int>("a"), extrasKeyOf<Int>())
|
||||
assertNotEquals<Extras.Key<*>>(extrasKeyOf<Int>("a"), extrasKeyOf<Int>("b"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - add two extras`() {
|
||||
val keyA = extrasKeyOf<Int>("a")
|
||||
val keyB = extrasKeyOf<Int>("b")
|
||||
val keyC = extrasKeyOf<Int>("c")
|
||||
val keyD = extrasKeyOf<Int>()
|
||||
val keyE = extrasKeyOf<Int>()
|
||||
|
||||
val extras1 = extrasOf(
|
||||
keyA withValue 0,
|
||||
keyB withValue 1,
|
||||
keyC withValue 2,
|
||||
keyD withValue 3
|
||||
)
|
||||
|
||||
val extras2 = extrasOf(
|
||||
keyC withValue 4,
|
||||
keyE withValue 5
|
||||
)
|
||||
|
||||
val combinedExtras = extras1 + extras2
|
||||
|
||||
assertEquals(
|
||||
extrasOf(
|
||||
keyA withValue 0,
|
||||
keyB withValue 1,
|
||||
keyC withValue 4,
|
||||
keyE withValue 5
|
||||
),
|
||||
combinedExtras
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - mutable extras - remove`() {
|
||||
val extras = mutableExtrasOf(
|
||||
extrasKeyOf<String>() withValue "2",
|
||||
extrasKeyOf<String>("other") withValue "4",
|
||||
extrasKeyOf<Int>() withValue 1,
|
||||
extrasKeyOf<Int>("cash") withValue 1
|
||||
)
|
||||
|
||||
extras.remove(extrasKeyOf<Int>("sunny"))
|
||||
assertEquals(1, extras[extrasKeyOf<Int>("cash")])
|
||||
|
||||
assertEquals(1, extras.remove(extrasKeyOf<Int>("cash")))
|
||||
assertNull(extras[extrasKeyOf<Int>("cash")])
|
||||
|
||||
assertEquals(1, extras.remove(extrasKeyOf<Int>()))
|
||||
assertNull(extras[extrasKeyOf<Int>()])
|
||||
|
||||
assertEquals("4", extras.remove(extrasKeyOf<String>("other")))
|
||||
|
||||
assertNull(extras[extrasKeyOf<String>("other")])
|
||||
|
||||
assertEquals(
|
||||
extrasOf(extrasKeyOf<String>() withValue "2"),
|
||||
extras.toExtras()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - mutable extras - clear`() {
|
||||
val extras = mutableExtrasOf(
|
||||
extrasKeyOf<String>() withValue "2",
|
||||
extrasKeyOf<String>("other") withValue "4",
|
||||
extrasKeyOf<Int>() withValue 1,
|
||||
extrasKeyOf<Int>("cash") withValue 1
|
||||
)
|
||||
|
||||
assertFalse(extras.isEmpty(), "Expected non-empty extras")
|
||||
extras.clear()
|
||||
assertTrue(extras.isEmpty(), "Expected extras to be empty")
|
||||
|
||||
assertEquals(emptyExtras(), extras)
|
||||
assertEquals(emptyExtras(), extras.toExtras())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test mutable extras - putAll`() {
|
||||
val extras = mutableExtrasOf(
|
||||
extrasKeyOf<String>() withValue "1",
|
||||
extrasKeyOf<String>("overwrite") withValue "2"
|
||||
)
|
||||
|
||||
extras.putAll(
|
||||
extrasOf(
|
||||
extrasKeyOf<String>("overwrite") withValue "3",
|
||||
extrasKeyOf<Int>() withValue 1
|
||||
)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
extrasOf(
|
||||
extrasKeyOf<String>() withValue "1",
|
||||
extrasKeyOf<String>("overwrite") withValue "3",
|
||||
extrasKeyOf<Int>() withValue 1
|
||||
),
|
||||
extras
|
||||
)
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.tooling.core
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotEquals
|
||||
|
||||
class ReifiedTypeSignatureTest {
|
||||
|
||||
class Box<T>
|
||||
|
||||
class Outer {
|
||||
inner class Inner
|
||||
}
|
||||
|
||||
open class Recursive<T : Recursive<T>>
|
||||
|
||||
class AnyRecursive : Recursive<AnyRecursive>()
|
||||
|
||||
@Test
|
||||
fun `test - sample 0`() {
|
||||
assertEquals(
|
||||
"kotlin.collections.List<kotlin.Int>", ReifiedTypeSignature<List<Int>>().signature
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 1`() {
|
||||
assertEquals(
|
||||
"kotlin.collections.List<*>", ReifiedTypeSignature<List<*>>().signature
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 2`() {
|
||||
assertEquals(
|
||||
"kotlin.String", ReifiedTypeSignature<String>().signature
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 3`() {
|
||||
assertEquals(
|
||||
"kotlin.collections.Map<kotlin.String, kotlin.collections.List<kotlin.Pair<kotlin.Int, kotlin.String>>>",
|
||||
ReifiedTypeSignature<Map<String, List<Pair<Int, String>>>>().signature
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `test - sample 5 - inner class`() {
|
||||
assertEquals(
|
||||
"org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Outer.Inner",
|
||||
ReifiedTypeSignature<Outer.Inner>().signature
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test - sample 6 - recursive types`() {
|
||||
assertEquals(
|
||||
"org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Recursive<*>",
|
||||
ReifiedTypeSignature<Recursive<*>>().signature
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.AnyRecursive",
|
||||
ReifiedTypeSignature<AnyRecursive>().signature
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
"org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest" +
|
||||
".Recursive<org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.AnyRecursive>",
|
||||
ReifiedTypeSignature<Recursive<AnyRecursive>>().signature
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test sample 7 - nullable`() {
|
||||
assertEquals("kotlin.collections.List<*>?", ReifiedTypeSignature<List<*>?>().signature)
|
||||
assertEquals("kotlin.collections.List<kotlin.Int?>", ReifiedTypeSignature<List<Int?>>().signature)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test sample 8 - variance`() {
|
||||
assertEquals(
|
||||
"org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Box<out kotlin.Int>",
|
||||
ReifiedTypeSignature<Box<out Int>>().signature
|
||||
)
|
||||
assertEquals(
|
||||
"org.jetbrains.kotlin.tooling.core.ReifiedTypeSignatureTest.Box<in kotlin.Int>",
|
||||
ReifiedTypeSignature<Box<in Int>>().signature
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("RemoveExplicitTypeArguments", "TYPE_INFERENCE_ONLY_INPUT_TYPES_WARNING")
|
||||
@Test
|
||||
fun `test - equals`() {
|
||||
assertEquals(ReifiedTypeSignature<List<Int>>(), ReifiedTypeSignature<List<Int>>())
|
||||
assertNotEquals(ReifiedTypeSignature<List<Int?>>(), ReifiedTypeSignature<List<Int>>())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user