From a23e4a1048318946ed4719c48304aa77d5ed1c52 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Thu, 1 Dec 2022 11:48:42 +0100 Subject: [PATCH] [kotlin-tooling-core] Implement ExtrasProperty KT-55189 --- .../kotlin/tooling/core/ExtrasProperty.kt | 92 ++++++++++++++ .../kotlin/tooling/core/HasExtras.kt | 2 + .../kotlin/tooling/core/ExtrasPropertyTest.kt | 113 ++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasProperty.kt create mode 100644 libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasPropertyTest.kt diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasProperty.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasProperty.kt new file mode 100644 index 00000000000..e2618493c5a --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasProperty.kt @@ -0,0 +1,92 @@ +/* + * 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.properties.ReadOnlyProperty +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +interface ExtrasProperty { + val key: Extras.Key +} + +val Extras.Key.readProperty get() = extrasReadProperty(this) + +val Extras.Key.readWriteProperty get() = extrasReadWriteProperty(this) + +fun Extras.Key.factoryProperty(factory: () -> T) = extrasFactoryProperty(this, factory) + +fun extrasReadProperty(key: Extras.Key): ExtrasReadOnlyProperty = object : ExtrasReadOnlyProperty { + override val key: Extras.Key = key +} + +fun extrasReadWriteProperty(key: Extras.Key): ExtrasReadWriteProperty = object : ExtrasReadWriteProperty { + override val key: Extras.Key = key +} + +fun extrasFactoryProperty(key: Extras.Key, factory: () -> T) = object : ExtrasFactoryProperty { + override val key: Extras.Key = key + override val factory: () -> T = factory +} + +interface ExtrasReadOnlyProperty : ExtrasProperty, ReadOnlyProperty { + override fun getValue(thisRef: HasExtras, property: KProperty<*>): T? { + return thisRef.extras[key] + } + + fun notNull(defaultValue: T): NotNullExtrasReadOnlyProperty = object : NotNullExtrasReadOnlyProperty { + override val defaultValue: T = defaultValue + override val key: Extras.Key = this@ExtrasReadOnlyProperty.key + } +} + +interface NotNullExtrasReadOnlyProperty : ExtrasProperty, ReadOnlyProperty { + val defaultValue: T + + override fun getValue(thisRef: HasExtras, property: KProperty<*>): T { + return thisRef.extras[key] ?: defaultValue + } +} + +interface ExtrasReadWriteProperty : ExtrasProperty, ReadWriteProperty { + override fun getValue(thisRef: HasMutableExtras, property: KProperty<*>): T? { + return thisRef.extras[key] + } + + override fun setValue(thisRef: HasMutableExtras, property: KProperty<*>, value: T?) { + if (value != null) thisRef.extras[key] = value + else thisRef.extras.remove(key) + } + + fun notNull(defaultValue: T): NotNullExtrasReadWriteProperty = object : NotNullExtrasReadWriteProperty { + override val defaultValue: T = defaultValue + override val key: Extras.Key = this@ExtrasReadWriteProperty.key + } +} + +interface NotNullExtrasReadWriteProperty : ExtrasProperty, ReadWriteProperty { + val defaultValue: T + + override fun getValue(thisRef: HasMutableExtras, property: KProperty<*>): T { + return thisRef.extras[key] ?: defaultValue + } + + override fun setValue(thisRef: HasMutableExtras, property: KProperty<*>, value: T) { + thisRef.extras[key] = value + } +} + +interface ExtrasFactoryProperty : ExtrasProperty, ReadWriteProperty { + val factory: () -> T + + override fun getValue(thisRef: HasMutableExtras, property: KProperty<*>): T { + return thisRef.extras.getOrPut(key, factory) + } + + override fun setValue(thisRef: HasMutableExtras, property: KProperty<*>, value: T) { + thisRef.extras[key] = value + } +} diff --git a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/HasExtras.kt b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/HasExtras.kt index 7519b201449..aa29ee36de4 100644 --- a/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/HasExtras.kt +++ b/libraries/tools/kotlin-tooling-core/src/main/kotlin/org/jetbrains/kotlin/tooling/core/HasExtras.kt @@ -27,3 +27,5 @@ operator fun Extras.Key.setValue(receiver: HasMutableExtras, proper receiver.extras[this] = value } } + + diff --git a/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasPropertyTest.kt b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasPropertyTest.kt new file mode 100644 index 00000000000..371ee3eb1fb --- /dev/null +++ b/libraries/tools/kotlin-tooling-core/src/test/kotlin/org/jetbrains/kotlin/tooling/core/ExtrasPropertyTest.kt @@ -0,0 +1,113 @@ +/* + * 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.* + +class ExtrasPropertyTest { + + + class Subject : HasMutableExtras { + override val extras: MutableExtras = mutableExtrasOf() + } + + class Dummy + + private val keyA = extrasKeyOf("a") + private val keyB = extrasKeyOf("b") + + private val Subject.readA: Int? by keyA.readProperty + private val Subject.readB: Int? by keyB.readProperty + + private var Subject.readWriteA: Int? by keyA.readWriteProperty + private var Subject.readWriteB: Int? by keyB.readWriteProperty + + private val Subject.notNullReadA: Int by keyA.readProperty.notNull(1) + private val Subject.notNullReadB: Int by keyB.readProperty.notNull(2) + + private var Subject.notNullReadWriteA: Int by keyA.readWriteProperty.notNull(3) + private var Subject.notNullReadWriteB: Int by keyB.readWriteProperty.notNull(4) + + private val keyList = extrasKeyOf>() + private val Subject.factoryList: MutableList by keyList.factoryProperty { mutableListOf() } + + @Test + fun `test - readOnlyProperty`() { + val subject = Subject() + assertNull(subject.readA) + assertNull(subject.readB) + + subject.readWriteA = 1 + assertEquals(1, subject.readA) + assertNull(subject.readB) + + subject.readWriteB = 2 + assertEquals(1, subject.readA) + assertEquals(2, subject.readB) + } + + @Test + fun `test - readWriteProperty`() { + val subject = Subject() + assertNull(subject.readWriteA) + assertNull(subject.readWriteB) + + subject.readWriteA = 1 + assertEquals(1, subject.readWriteA) + assertNull(subject.readB) + + subject.readWriteB = 2 + assertEquals(1, subject.readWriteA) + assertEquals(2, subject.readWriteB) + } + + @Test + fun `test - readOnlyProperty - notNull`() { + val subject = Subject() + assertEquals(1, subject.notNullReadA) + assertEquals(2, subject.notNullReadB) + + subject.readWriteA = -1 + assertEquals(-1, subject.notNullReadA) + assertEquals(2, subject.notNullReadB) + + subject.readWriteB = -2 + assertEquals(-1, subject.notNullReadA) + assertEquals(-2, subject.notNullReadB) + } + + @Test + fun `test - readWriteProperty - notNull`() { + val subject = Subject() + assertEquals(3, subject.notNullReadWriteA) + assertEquals(4, subject.notNullReadWriteB) + + subject.notNullReadWriteA = -1 + assertEquals(-1, subject.notNullReadWriteA) + assertEquals(4, subject.notNullReadWriteB) + + subject.notNullReadWriteB = -2 + assertEquals(-1, subject.notNullReadWriteA) + assertEquals(-2, subject.notNullReadWriteB) + } + + @Test + fun `test - factoryProperty`() { + run { + val subject = Subject() + assertNotNull(subject.factoryList) + assertSame(subject.factoryList, subject.factoryList) + assertSame(subject.extras[keyList], subject.factoryList) + } + + run { + val subject = Subject() + val list = mutableListOf(Dummy()) + subject.extras[keyList] = list + assertSame(list, subject.factoryList) + } + } +}