Skip to content

Commit f4dc31d

Browse files
committed
feat(model-api): introduced INodeResolutionScope and deprecated INodeReferenceSerializer
1 parent 680ec45 commit f4dc31d

File tree

6 files changed

+90
-10
lines changed

6 files changed

+90
-10
lines changed

model-api/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ kotlin {
4444
implementation(kotlin("stdlib-common"))
4545
implementation(libs.kotlin.logging)
4646
implementation(libs.kotlin.serialization.json)
47+
implementation(libs.kotlin.coroutines.core)
4748
}
4849
}
4950
val commonTest by getting {
@@ -55,18 +56,19 @@ kotlin {
5556
val jvmMain by getting {
5657
dependencies {
5758
implementation(kotlin("stdlib-jdk8"))
59+
implementation(libs.kotlin.coroutines.core)
5860
}
5961
}
6062
val jvmTest by getting {
6163
dependencies {
6264
implementation(kotlin("test"))
63-
implementation(kotlin("test-junit"))
6465
}
6566
}
6667
val jsMain by getting {
6768
dependencies {
6869
implementation(kotlin("stdlib-js"))
6970
api(npm("@modelix/ts-model-api", rootDir.resolve("ts-model-api")))
71+
implementation(libs.kotlin.coroutines.core)
7072
}
7173
kotlin.srcDir(rootDir.resolve("ts-model-api").resolve("build/dukat"))
7274
}

model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReference.kt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515

1616
package org.modelix.model.api
1717

18+
import kotlinx.serialization.KSerializer
19+
import kotlinx.serialization.Serializable
20+
import kotlinx.serialization.descriptors.PrimitiveKind
21+
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
22+
import kotlinx.serialization.descriptors.SerialDescriptor
23+
import kotlinx.serialization.encoding.Decoder
24+
import kotlinx.serialization.encoding.Encoder
1825
import org.modelix.model.area.IArea
1926

2027
/**
@@ -23,12 +30,37 @@ import org.modelix.model.area.IArea
2330
* The relation between an [INodeReference] and an [INode] is n to 1.
2431
* Two [INodeReference]s that are not equal can resolve to the same [INode].
2532
*/
33+
@Serializable(with = NodeReferenceKSerializer::class)
2634
interface INodeReference {
2735
/**
2836
* Tries to find the referenced node in the given [IArea].
2937
*
3038
* @param area area to be searched in
3139
* @return the node, or null if the node could not be found
3240
*/
33-
fun resolveNode(area: IArea?): INode?
41+
@Deprecated("use .resolveIn(INodeResolutionScope)", ReplaceWith("resolveIn(area!!)"))
42+
fun resolveNode(area: IArea?): INode? = resolveIn(area as INodeResolutionScope)
43+
44+
fun serialize(): String = INodeReferenceSerializer.serialize(this)
45+
}
46+
47+
fun INodeReference.resolveIn(scope: INodeResolutionScope): INode? {
48+
if (this is NodeReference) {
49+
val deserialized = INodeReferenceSerializer.tryDeserialize(serialized)
50+
if (deserialized != null) return deserialized.resolveIn(scope)
51+
}
52+
return scope.resolveNode(this)
53+
}
54+
55+
class NodeReferenceKSerializer : KSerializer<INodeReference> {
56+
override fun deserialize(decoder: Decoder): INodeReference {
57+
val serialized = decoder.decodeString()
58+
return INodeReferenceSerializer.tryDeserialize(serialized) ?: SerializedNodeReference(serialized)
59+
}
60+
61+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("modelix.INodeReference", PrimitiveKind.STRING)
62+
63+
override fun serialize(encoder: Encoder, value: INodeReference) {
64+
encoder.encodeString(value.serialize())
65+
}
3466
}

model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReferenceSerializer.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import kotlin.reflect.KClass
55
/**
66
* Serializer for [INodeReference]s.
77
*/
8+
@Deprecated("INodeResolutionScope.resolveNode(INodeReference) is now responsible for deserializing supported references")
89
interface INodeReferenceSerializer {
910

1011
/**
@@ -96,6 +97,10 @@ interface INodeReferenceSerializer {
9697
}
9798

9899
fun deserialize(serialized: String): INodeReference {
100+
return tryDeserialize(serialized) ?: SerializedNodeReference(serialized)
101+
}
102+
103+
fun tryDeserialize(serialized: String): INodeReference? {
99104
val parts = serialized.split(INodeReferenceSerializerEx.SEPARATOR, limit = 2)
100105
if (parts.size == 2) {
101106
val deserializer = deserializerForPrefix[parts[0]]
@@ -105,11 +110,11 @@ interface INodeReferenceSerializer {
105110
}
106111

107112
return legacySerializers.map { it.deserialize(serialized) }.firstOrNull { it != null }
108-
?: throw RuntimeException("No deserializer found for: $serialized")
109113
}
110114
}
111115
}
112116

117+
@Deprecated("INodeResolutionScope.resolveNode(INodeReference) is now responsible for deserializing supported references")
113118
interface INodeReferenceSerializerEx : INodeReferenceSerializer {
114119
val prefix: String
115120
val supportedReferenceClasses: Set<KClass<out INodeReference>>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.modelix.model.api
2+
3+
import kotlin.coroutines.CoroutineContext
4+
5+
interface INodeResolutionScope : CoroutineContext.Element {
6+
override val key: CoroutineContext.Key<*>
7+
get() = Key
8+
9+
fun resolveNode(ref: INodeReference): INode?
10+
11+
companion object Key : CoroutineContext.Key<INodeResolutionScope>
12+
13+
override fun plus(context: CoroutineContext): CoroutineContext {
14+
// coroutines are not compiled with -Xjvm-default=all-compatibility
15+
// to not break existing IAreas implemented in Java, this override is necessary
16+
return super.plus(context)
17+
}
18+
19+
override fun <R> fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R {
20+
// coroutines are not compiled with -Xjvm-default=all-compatibility
21+
// to not break existing IAreas implemented in Java, this override is necessary
22+
return super.fold(initial, operation)
23+
}
24+
25+
override fun <E : CoroutineContext.Element> get(key: CoroutineContext.Key<E>): E? {
26+
// coroutines are not compiled with -Xjvm-default=all-compatibility
27+
// to not break existing IAreas implemented in Java, this override is necessary
28+
return super.get(key)
29+
}
30+
31+
override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext {
32+
// coroutines are not compiled with -Xjvm-default=all-compatibility
33+
// to not break existing IAreas implemented in Java, this override is necessary
34+
return super.minusKey(key)
35+
}
36+
}
37+
38+
class CompositeNodeResolutionScope(val scopes: List<INodeResolutionScope>) : INodeResolutionScope {
39+
override fun resolveNode(ref: INodeReference): INode? {
40+
return scopes.asSequence().mapNotNull { it.resolveNode(ref) }.firstOrNull()
41+
}
42+
}
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package org.modelix.model.api
22

3-
import org.modelix.model.area.IArea
3+
@Deprecated("renamed to NodeReference", ReplaceWith("NodeReference"))
4+
typealias SerializedNodeReference = NodeReference
45

5-
data class SerializedNodeReference(val serialized: String) : INodeReference {
6-
override fun resolveNode(area: IArea?): INode? {
7-
return INodeReferenceSerializer.deserialize(serialized).resolveNode(area)
8-
}
6+
data class NodeReference(val serialized: String) : INodeReference {
7+
override fun serialize(): String = serialized
98
}

model-api/src/commonMain/kotlin/org/modelix/model/area/IArea.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import org.modelix.model.api.*
2222
* It's like a unix filesystem with mount points. The model inside an area can also be an MPS model that is not a
2323
* persistent data structure.
2424
*/
25-
interface IArea {
25+
interface IArea : INodeResolutionScope {
2626
/**
2727
* The root of an area is not allowed to change
2828
*/
@@ -37,7 +37,7 @@ interface IArea {
3737
*
3838
* This method requires resolveNode().getNode() == this
3939
*/
40-
fun resolveNode(ref: INodeReference): INode?
40+
override fun resolveNode(ref: INodeReference): INode?
4141

4242
/**
4343
* This method allows resolveOriginalNode().getArea() != this

0 commit comments

Comments
 (0)