From 66c1e0ef2535d77e481acd0ddabeacb35d19ccc3 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Fri, 2 Aug 2024 12:14:22 +0200 Subject: [PATCH 01/24] Improve performance of conversion to LionWeb --- .../com/strumenta/kolasu/model/Model.kt | 9 +++++++- .../com/strumenta/kolasu/testing/Testing.kt | 10 +++++++++ .../kolasu/traversing/Structurally.kt | 2 +- .../kolasu/lionweb/LionWebModelConverter.kt | 22 ++++++++++++++----- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/kolasu/model/Model.kt b/core/src/main/kotlin/com/strumenta/kolasu/model/Model.kt index a49290235..c13731f3f 100644 --- a/core/src/main/kotlin/com/strumenta/kolasu/model/Model.kt +++ b/core/src/main/kotlin/com/strumenta/kolasu/model/Model.kt @@ -122,7 +122,14 @@ open class Node() : Origin, Destination, Serializable { get() = explicitlySetSource ?: (position?.source ?: origin?.source) set(value) { explicitlySetSource = value - require(this.source === value) { "The source has not been set correctly. It should be $value while it is ${this.source}" } + if (value == null) { + require(this.source == null) + } else { + require(this.source === value) { + "The source has not been set correctly. It should be $value " + + "while it is ${this.source}" + } + } } fun setSourceForTree(source: Source): Node { diff --git a/core/src/main/kotlin/com/strumenta/kolasu/testing/Testing.kt b/core/src/main/kotlin/com/strumenta/kolasu/testing/Testing.kt index f15f2b979..e26e479c2 100644 --- a/core/src/main/kotlin/com/strumenta/kolasu/testing/Testing.kt +++ b/core/src/main/kotlin/com/strumenta/kolasu/testing/Testing.kt @@ -105,6 +105,16 @@ class IgnoreChildren : MutableList { override fun subList(fromIndex: Int, toIndex: Int): MutableList { TODO("Not yet implemented") } + + // Sometimes the compiler complains about these methods not being overriden + fun toArray(): Array { + TODO() + } + + // Sometimes the compiler complains about these methods not being overriden + fun toArray(base: Array): Array { + TODO() + } } class ASTDifferenceException(val context: String, val expected: Any, val actual: Any) : diff --git a/core/src/main/kotlin/com/strumenta/kolasu/traversing/Structurally.kt b/core/src/main/kotlin/com/strumenta/kolasu/traversing/Structurally.kt index 8b65db581..863abed09 100644 --- a/core/src/main/kotlin/com/strumenta/kolasu/traversing/Structurally.kt +++ b/core/src/main/kotlin/com/strumenta/kolasu/traversing/Structurally.kt @@ -173,7 +173,7 @@ class FastWalker(val node: Node) { return if (childrenMap.containsKey(child)) { childrenMap[child]!! } else { - childrenMap.put(child, child.walkChildren().toList()) + childrenMap[child] = child.walkChildren().toList() childrenMap[child]!! } } diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt index ba07ecd58..994d666f4 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt @@ -4,6 +4,7 @@ import com.strumenta.kolasu.ids.IDGenerationException import com.strumenta.kolasu.ids.NodeIdProvider import com.strumenta.kolasu.ids.SimpleSourceIdProvider import com.strumenta.kolasu.ids.SourceShouldBeSetException +import com.strumenta.kolasu.language.Feature import com.strumenta.kolasu.language.KolasuLanguage import com.strumenta.kolasu.model.CompositeDestination import com.strumenta.kolasu.model.Multiplicity @@ -58,6 +59,7 @@ import kotlin.reflect.KFunction import kotlin.reflect.KMutableProperty import kotlin.reflect.KParameter import kotlin.reflect.full.primaryConstructor +import io.lionweb.lioncore.java.language.Feature as LWFeature interface PrimitiveValueSerialization { fun serialize(value: E): String @@ -123,6 +125,9 @@ class LionWebModelConverter( } } + private val kFeaturesCache = mutableMapOf, Map>() + private val lwFeaturesCache = mutableMapOf, List>>() + fun exportModelToLionWeb( kolasuTree: KNode, nodeIdProvider: NodeIdProvider = this.nodeIdProvider, @@ -131,8 +136,10 @@ class LionWebModelConverter( kolasuTree.assignParents() val myIDManager = object { + private val cache = mutableMapOf() + fun nodeId(kNode: KNode): String { - return nodeIdProvider.id(kNode) + return cache.getOrPut(kNode) { nodeIdProvider.id(kNode) } } } @@ -151,14 +158,17 @@ class LionWebModelConverter( "It was produced while exporting this Kolasu Node: $kNode" ) } - val kFeatures = kNode.javaClass.kotlin.allFeatures() - lwNode.classifier.allFeatures().forEach { feature -> + val kFeatures = kFeaturesCache.getOrPut(kNode.javaClass) { + kNode.javaClass.kotlin.allFeatures().associateBy { it.name } + } + val lwFeatures = lwFeaturesCache.getOrPut(lwNode.classifier) { lwNode.classifier.allFeatures() } + lwFeatures.forEach { feature -> when (feature) { is Property -> { if (feature == StarLasuLWLanguage.ASTNodePosition) { lwNode.setPropertyValue(StarLasuLWLanguage.ASTNodePosition, kNode.position) } else { - val kAttribute = kFeatures.find { it.name == feature.name } + val kAttribute = kFeatures[feature.name] as? com.strumenta.kolasu.language.Attribute ?: throw IllegalArgumentException("Property ${feature.name} not found in $kNode") val kValue = kNode.getAttributeValue(kAttribute) @@ -173,7 +183,7 @@ class LionWebModelConverter( is Containment -> { try { val kContainment = ( - kFeatures.find { it.name == feature.name } ?: throw IllegalStateException( + kFeatures[feature.name] ?: throw IllegalStateException( "Cannot find containment for ${feature.name} when considering node $kNode" ) ) @@ -230,7 +240,7 @@ class LionWebModelConverter( } lwNode.setReferenceValues(StarLasuLWLanguage.ASTNodeTranspiledNodes, referenceValues) } else { - val kReference = kFeatures.find { it.name == feature.name } + val kReference = kFeatures[feature.name] as com.strumenta.kolasu.language.Reference val kValue = kNode.getReference(kReference) if (kValue == null) { From 149c75a1344f21661de9ff82fc98502002129c3c Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Fri, 2 Aug 2024 13:02:51 +0200 Subject: [PATCH 02/24] Improve performance of conversion from LionWeb --- .../kolasu/lionweb/LionWebModelConverter.kt | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt index 994d666f4..79695bfbf 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt @@ -55,7 +55,6 @@ import io.lionweb.lioncore.kotlin.getOnlyChildByContainmentName import java.util.IdentityHashMap import java.util.concurrent.ConcurrentHashMap import kotlin.reflect.KClass -import kotlin.reflect.KFunction import kotlin.reflect.KMutableProperty import kotlin.reflect.KParameter import kotlin.reflect.full.primaryConstructor @@ -125,8 +124,16 @@ class LionWebModelConverter( } } - private val kFeaturesCache = mutableMapOf, Map>() - private val lwFeaturesCache = mutableMapOf, List>>() + companion object { + private val kFeaturesCache = mutableMapOf, Map>() + private val lwFeaturesCache = mutableMapOf, Map>>() + + fun lwFeatureByName(classifier: Classifier<*>, featureName: String): LWFeature<*>? { + return lwFeaturesCache.getOrPut(classifier) { + classifier.allFeatures().associateBy { it.name!! } + }[featureName] + } + } fun exportModelToLionWeb( kolasuTree: KNode, @@ -161,8 +168,10 @@ class LionWebModelConverter( val kFeatures = kFeaturesCache.getOrPut(kNode.javaClass) { kNode.javaClass.kotlin.allFeatures().associateBy { it.name } } - val lwFeatures = lwFeaturesCache.getOrPut(lwNode.classifier) { lwNode.classifier.allFeatures() } - lwFeatures.forEach { feature -> + val lwFeatures = lwFeaturesCache.getOrPut(lwNode.classifier) { + lwNode.classifier.allFeatures().associateBy { it.name!! } + } + lwFeatures.values.forEach { feature -> when (feature) { is Property -> { if (feature == StarLasuLWLanguage.ASTNodePosition) { @@ -661,20 +670,21 @@ class LionWebModelConverter( if (specialObject != null) { return specialObject as T } - val constructor: KFunction = when { - kClass.constructors.size == 1 -> { - kClass.constructors.first() - } - kClass.primaryConstructor != null -> { - kClass.primaryConstructor!! - } - else -> { - TODO() + val constructor = + when { + kClass.constructors.size == 1 -> { + kClass.constructors.first() + } + kClass.primaryConstructor != null -> { + kClass.primaryConstructor!! + } + else -> { + TODO() + } } - } val params = mutableMapOf() constructor.parameters.forEach { param -> - val feature = data.classifier.getFeatureByName(param.name!!) + val feature = lwFeatureByName(data.classifier, param.name!!) if (feature == null) { throw java.lang.IllegalStateException( "We could not find a feature named as the parameter ${param.name} " + @@ -712,7 +722,7 @@ class LionWebModelConverter( } } propertiesNotSetAtConstructionTime.forEach { property -> - val feature = data.classifier.getFeatureByName(property.name) + val feature = lwFeatureByName(data.classifier, property.name) if (property !is KMutableProperty<*>) { if (property.isContainment() && property.asContainment().multiplicity == Multiplicity.MANY) { val currentValue = property.get(kNode) as MutableList From a7408f3d5594861198fd7c9afeda4cd7908d0c91 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Sun, 4 Aug 2024 18:08:47 +0200 Subject: [PATCH 03/24] Use IdentifyHashMap in Node cache --- .../com/strumenta/kolasu/lionweb/LionWebModelConverter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt index 79695bfbf..8d8c17928 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt @@ -143,7 +143,7 @@ class LionWebModelConverter( kolasuTree.assignParents() val myIDManager = object { - private val cache = mutableMapOf() + private val cache = IdentityHashMap() fun nodeId(kNode: KNode): String { return cache.getOrPut(kNode) { nodeIdProvider.id(kNode) } From 02c3be09c3b34ee9fa981c1c1531ae00544b2173 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Sun, 4 Aug 2024 18:23:11 +0200 Subject: [PATCH 04/24] Update dependencies --- gradle.properties | 2 +- gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 182c37c49..d98fec0f7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,6 @@ lionwebJavaVersion=0.2.18 kspVersion=1.0.11 lionwebGenGradlePluginID=com.strumenta.kolasu.lionwebgen kotestVersion=1.3.3 -lionwebRepositoryCommitID=4a5dc8bacedbaa2c9153d5dbbb9c42c5693705e4 +lionwebRepositoryCommitID=bf25f21d8baea6b7b7918febefc40ee4151f5088 SONATYPE_CONNECT_TIMEOUT_SECONDS=180 SONATYPE_CLOSE_TIMEOUT_SECONDS=900 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f02546152..1d40c6c56 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ buildConfig = { id = "com.github.gmazzo.buildconfig", version = "5.3.5" } superPublish = { id = "com.vanniktech.maven.publish", version = "0.22.0" } [versions] -lwkotlin = "0.2.3" +lwkotlin = "0.2.4-SNAPSHOT" [libraries] lionwebjava = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-core", version = "0.2.18" } From fba8b0be36c02f118c9bddb304c0fc9780356203 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Mon, 5 Aug 2024 12:08:57 +0200 Subject: [PATCH 05/24] Remove unnecessary Dockerfile --- .../resources/lionweb-repository-Dockerfile | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 lionwebrepo-client/src/functionalTest/resources/lionweb-repository-Dockerfile diff --git a/lionwebrepo-client/src/functionalTest/resources/lionweb-repository-Dockerfile b/lionwebrepo-client/src/functionalTest/resources/lionweb-repository-Dockerfile deleted file mode 100644 index dfaa0175e..000000000 --- a/lionwebrepo-client/src/functionalTest/resources/lionweb-repository-Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node -ARG lionwebRepositoryCommitId=xxxxxx -RUN git clone https://github.com/LionWeb-io/lionweb-repository.git -WORKDIR lionweb-repository -RUN git checkout ${lionwebRepositoryCommitId} -RUN npm install -RUN npm run build -RUN echo "npm run database create" > run.sh -RUN echo "npm run database init" >> run.sh -RUN echo "cd packages/server" >> run.sh -RUN echo "npm run dev" >> run.sh -CMD ["sh", "run.sh"] From d01a886b198b6e2ffc0b7730b6d99c6a19e747be Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Wed, 7 Aug 2024 08:41:36 +0200 Subject: [PATCH 06/24] Correct exporting of PrimitiveType to LionWeb --- .../com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt index d8822a729..54a73cdee 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt @@ -314,9 +314,10 @@ class LionWebLanguageConverter { val primitiveType = classesAndPrimitiveTypes.byA(kClass) if (primitiveType == null) { val newPrimitiveName = kClass.simpleName - val newPrimitiveTypeID = (lionwebLanguage.id ?: "unknown_language") + "_" + newPrimitiveName + val newPrimitiveTypeID = (lionwebLanguage.id ?: "unknown_language") + "-" + newPrimitiveName + "-id" val newPrimitiveType = PrimitiveType(lionwebLanguage, newPrimitiveName, newPrimitiveTypeID) - newPrimitiveType.setKey(newPrimitiveName) + val newPrimitiveTypeKey = (lionwebLanguage.id ?: "unknown_language") + "-" + newPrimitiveName + "-key" + newPrimitiveType.setKey(newPrimitiveTypeKey) lionwebLanguage.addElement(newPrimitiveType) classesAndPrimitiveTypes.associate(kClass, newPrimitiveType) return newPrimitiveType From 35d6f122ae795b0e3dd885e09c0933a748c295ec Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Wed, 7 Aug 2024 13:38:52 +0200 Subject: [PATCH 07/24] Minor --- gradle/libs.versions.toml | 7 ++++--- .../kolasu/lionwebclient/KolasuClient.kt | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1d40c6c56..b12ba10d2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,11 +3,12 @@ buildConfig = { id = "com.github.gmazzo.buildconfig", version = "5.3.5" } superPublish = { id = "com.vanniktech.maven.publish", version = "0.22.0" } [versions] -lwkotlin = "0.2.4-SNAPSHOT" +lwjava = "0.2.18" +lwkotlin = "0.2.4" [libraries] -lionwebjava = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-core", version = "0.2.18" } -lionwebjavaemf = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-emf", version = "0.2.18" } +lionwebjava = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-core", version.ref = "lwjava" } +lionwebjavaemf = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-emf", version.ref = "lwjava" } lionwebkotlinrepoclient = { group = "io.lionweb.lionweb-kotlin", name = "lionweb-kotlin-2024.1-repo-client", version.ref = "lwkotlin" } lionwebkotlinrepoclienttesting = { group = "io.lionweb.lionweb-kotlin", name = "lionweb-kotlin-2024.1-repo-client-testing", version.ref = "lwkotlin" } lionwebkotlincore = { group = "io.lionweb.lionweb-kotlin", name = "lionweb-kotlin-2024.1-core", version.ref = "lwkotlin" } diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt index 14e0af21e..bca29a565 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt +++ b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt @@ -329,7 +329,11 @@ class KolasuClient( child: LWNode, parent: LWNode, property: KProperty1<*, *>, + skipRetrievalOfParent: Boolean = false ): String { + if (skipRetrievalOfParent) { + return attachLionWebChild(child, property.name!!, parent) + } return attachLionWebChild(child, parent.id!!, property.name!!) } @@ -346,8 +350,7 @@ class KolasuClient( parentID: String, propertyName: String, ): String { - val updatedParent = - lionWebClient.retrieve( + val updatedParent = lionWebClient.retrieve( parentID, withProxyParent = true, retrievalMode = RetrievalMode.SINGLE_NODE, @@ -355,6 +358,14 @@ class KolasuClient( return attachLionWebChild(child, updatedParent, propertyName) } + fun attachLionWebChild( + child: LWNode, + propertyName: String, + providedUpdatedParent: LWNode + ): String { + return attachLionWebChild(child, providedUpdatedParent, propertyName) + } + fun attachLionWebChild( child: LWNode, parent: LWNode, @@ -362,6 +373,7 @@ class KolasuClient( ): String { parent.addChild(parent.classifier.requireContainmentByName(propertyName), child) lionWebClient.storeTree(parent) + (child as HasSettableParent).setParentID(parent.id) return child.id!! } From b19ada991910d0e4fcf63a4a4be718b1f284d56f Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Thu, 8 Aug 2024 12:40:13 +0200 Subject: [PATCH 08/24] KolasuClient pass idProvider to LionWebModelConverter --- .../kolasu/lionweb/FileBasedIDShorterner.kt | 117 ++++++++++++++++++ .../strumenta/kolasu/lionweb}/Performance.kt | 9 +- .../kolasu/lionwebclient/KolasuClient.kt | 12 +- 3 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt rename {lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient => lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb}/Performance.kt (84%) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt new file mode 100644 index 000000000..6f8f6c0ed --- /dev/null +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt @@ -0,0 +1,117 @@ +package com.strumenta.kolasu.lionweb + +import com.strumenta.kolasu.ids.NodeIdProvider +import com.strumenta.kolasu.model.Node +import java.io.BufferedWriter +import java.io.File +import java.io.FileOutputStream +import java.io.OutputStreamWriter +import java.util.IdentityHashMap + +class FileBasedIDShortener(val file: File, val baseNodeIdProvider: NodeIdProvider = StructuralLionWebNodeIdProvider()) : NodeIdProvider { + + private var next = 1L + private val map = BiMap() + private val fos = FileOutputStream(file, true) + private val writer = OutputStreamWriter(fos, Charsets.UTF_8) + private val bWriter = BufferedWriter(writer) + + init { + if (file.exists()) { + file.readLines().filter { it.isNotBlank() }.forEach { line -> + val parts = line.split(",") + require(parts.size == 2) + val originalNodeID = parts[0] + val shorterNodeID = parts[1] + map.associate(originalNodeID, shorterNodeID) + val n = shorterNodeID.toLong() + next = next.coerceAtLeast(n + 1) + } + } + } + + override fun id(kNode: Node): String { + val baseID = baseNodeIdProvider.id(kNode) + return shorterID(baseID) + } + + override var parentProvider: NodeIdProvider? + get() = null + set(value) { + throw UnsupportedOperationException() + } + + fun flush() { + bWriter.flush() + bWriter.close() + writer.close() + fos.close() + } + + private fun shorterID(originalNodeID: String) : String { + if (!map.containsA(originalNodeID)) { + return assignShorterID(originalNodeID) + } + return map.byA(originalNodeID)!! + } + + private fun originalNodeID(shorterID: String) : String { + if (!map.containsB(shorterID)) { + throw IllegalStateException() + } + return map.byB(shorterID)!! + } + + private fun assignShorterID(originalNodeID: String) : String { + val shorterID = next.toString() + map.associate(originalNodeID, shorterID) + next++ + bWriter.write("${originalNodeID},${shorterID}\n") + return shorterID + } +} + + +class SequenceIDAssigner(val file: File) : NodeIdProvider { + + private var next = 1L + private val cache = IdentityHashMap() + private val fos = FileOutputStream(file, true) + private val writer = OutputStreamWriter(fos, Charsets.UTF_8) + private val bWriter = BufferedWriter(writer) + + init { + if (file.exists()) { + file.readLines().filter { it.isNotBlank() }.forEach { line -> + val n = line.toLong() + next = next.coerceAtLeast(n + 1) + } + } + } + + override fun id(kNode: Node): String { + return cache.getOrPut(kNode) { + assignNextID() + } + } + + override var parentProvider: NodeIdProvider? + get() = null + set(value) { + throw UnsupportedOperationException() + } + + fun flush() { + bWriter.flush() + bWriter.close() + writer.close() + fos.close() + } + + private fun assignNextID() : String { + val id = next.toString() + next++ + bWriter.write("${id}\n") + return id + } +} \ No newline at end of file diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/Performance.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/Performance.kt similarity index 84% rename from lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/Performance.kt rename to lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/Performance.kt index c5abfe560..f1450d827 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/Performance.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/Performance.kt @@ -1,4 +1,4 @@ -package com.strumenta.kolasu.lionwebclient +package com.strumenta.kolasu.lionweb import java.io.File @@ -10,6 +10,13 @@ object PerformanceLogger { return sumByStep.getOrDefault(description, 0) } + fun printAllSums() { + println("== Performance logger sums ===") + sumByStep.forEach { + println("${it.key}: ${it.value}") + } + } + private fun ensureFileIsReady() { if (!file.exists()) { file.appendText("event,time\n") diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt index bca29a565..984a3cbc2 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt +++ b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt @@ -9,6 +9,7 @@ import com.strumenta.kolasu.lionweb.LWLanguage import com.strumenta.kolasu.lionweb.LWNode import com.strumenta.kolasu.lionweb.LionWebModelConverter import com.strumenta.kolasu.lionweb.LionWebSource +import com.strumenta.kolasu.lionweb.PerformanceLogger import com.strumenta.kolasu.lionweb.PrimitiveValueSerialization import com.strumenta.kolasu.lionweb.ProxyBasedNodeResolver import com.strumenta.kolasu.model.ASTRoot @@ -57,22 +58,16 @@ class KolasuClient( val connectTimeOutInSeconds: Long = 60, val callTimeoutInSeconds: Long = 60, val authorizationToken: String? = null, + val idProvider: NodeIdProvider = CommonNodeIdProvider().caching() ) { /** * Exposed for testing purposes */ val nodeConverter = - LionWebModelConverter().apply { + LionWebModelConverter(idProvider).apply { externalNodeResolver = ProxyBasedNodeResolver } - /** - * This is the logic we use to assign Node IDs. This can be customized, if needed. - * For example, we may want to use some form of semantic Node ID for certain kinds of Nodes, like qualified names - * for Class Declarations. - */ - val idProvider: NodeIdProvider = CommonNodeIdProvider().caching() - val lionWebClient = LionWebClient( hostname, @@ -460,7 +455,6 @@ class KolasuClient( containmentIndex: Int, ): LWNode { require(kNode.javaClass.annotations.any { it is ASTRoot }) - kNode.assignParents() nodeConverter.clearNodesMapping() return nodeConverter.exportModelToLionWeb( kNode, From 182665e38fcc4809d16416ed151ec446275fedca Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Thu, 8 Aug 2024 12:43:47 +0200 Subject: [PATCH 09/24] Linting --- .../kolasu/lionweb/FileBasedIDShorterner.kt | 117 ------------------ .../{Performance.kt => PerformanceLogger.kt} | 8 +- .../kolasu/lionweb/UUIDNodeIdProvider.kt | 23 ++++ 3 files changed, 27 insertions(+), 121 deletions(-) delete mode 100644 lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt rename lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/{Performance.kt => PerformanceLogger.kt} (82%) create mode 100644 lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt deleted file mode 100644 index 6f8f6c0ed..000000000 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/FileBasedIDShorterner.kt +++ /dev/null @@ -1,117 +0,0 @@ -package com.strumenta.kolasu.lionweb - -import com.strumenta.kolasu.ids.NodeIdProvider -import com.strumenta.kolasu.model.Node -import java.io.BufferedWriter -import java.io.File -import java.io.FileOutputStream -import java.io.OutputStreamWriter -import java.util.IdentityHashMap - -class FileBasedIDShortener(val file: File, val baseNodeIdProvider: NodeIdProvider = StructuralLionWebNodeIdProvider()) : NodeIdProvider { - - private var next = 1L - private val map = BiMap() - private val fos = FileOutputStream(file, true) - private val writer = OutputStreamWriter(fos, Charsets.UTF_8) - private val bWriter = BufferedWriter(writer) - - init { - if (file.exists()) { - file.readLines().filter { it.isNotBlank() }.forEach { line -> - val parts = line.split(",") - require(parts.size == 2) - val originalNodeID = parts[0] - val shorterNodeID = parts[1] - map.associate(originalNodeID, shorterNodeID) - val n = shorterNodeID.toLong() - next = next.coerceAtLeast(n + 1) - } - } - } - - override fun id(kNode: Node): String { - val baseID = baseNodeIdProvider.id(kNode) - return shorterID(baseID) - } - - override var parentProvider: NodeIdProvider? - get() = null - set(value) { - throw UnsupportedOperationException() - } - - fun flush() { - bWriter.flush() - bWriter.close() - writer.close() - fos.close() - } - - private fun shorterID(originalNodeID: String) : String { - if (!map.containsA(originalNodeID)) { - return assignShorterID(originalNodeID) - } - return map.byA(originalNodeID)!! - } - - private fun originalNodeID(shorterID: String) : String { - if (!map.containsB(shorterID)) { - throw IllegalStateException() - } - return map.byB(shorterID)!! - } - - private fun assignShorterID(originalNodeID: String) : String { - val shorterID = next.toString() - map.associate(originalNodeID, shorterID) - next++ - bWriter.write("${originalNodeID},${shorterID}\n") - return shorterID - } -} - - -class SequenceIDAssigner(val file: File) : NodeIdProvider { - - private var next = 1L - private val cache = IdentityHashMap() - private val fos = FileOutputStream(file, true) - private val writer = OutputStreamWriter(fos, Charsets.UTF_8) - private val bWriter = BufferedWriter(writer) - - init { - if (file.exists()) { - file.readLines().filter { it.isNotBlank() }.forEach { line -> - val n = line.toLong() - next = next.coerceAtLeast(n + 1) - } - } - } - - override fun id(kNode: Node): String { - return cache.getOrPut(kNode) { - assignNextID() - } - } - - override var parentProvider: NodeIdProvider? - get() = null - set(value) { - throw UnsupportedOperationException() - } - - fun flush() { - bWriter.flush() - bWriter.close() - writer.close() - fos.close() - } - - private fun assignNextID() : String { - val id = next.toString() - next++ - bWriter.write("${id}\n") - return id - } -} \ No newline at end of file diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/Performance.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/PerformanceLogger.kt similarity index 82% rename from lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/Performance.kt rename to lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/PerformanceLogger.kt index f1450d827..6f445d2ca 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/Performance.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/PerformanceLogger.kt @@ -6,7 +6,7 @@ object PerformanceLogger { var file = File("performance-log.csv") val sumByStep = mutableMapOf() - fun sumByStep(description: String) : Long { + fun sumByStep(description: String): Long { return sumByStep.getOrDefault(description, 0) } @@ -23,14 +23,14 @@ object PerformanceLogger { } } - fun log(description: String, baseTime: Long? = null) : Long { + fun log(description: String, baseTime: Long? = null): Long { ensureFileIsReady() val t = System.currentTimeMillis() if (baseTime == null) { - file.appendText("\"${description}\",${t},\n") + file.appendText("\"${description}\",$t,\n") } else { val delta = t - baseTime - file.appendText("\"${description}\",${t},${delta}\n") + file.appendText("\"${description}\",$t,${delta}\n") val baseDescription = description.removeSuffix(" - end") sumByStep[baseDescription] = sumByStep.getOrDefault(baseDescription, 0) + delta } diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt new file mode 100644 index 000000000..5e2d3b599 --- /dev/null +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt @@ -0,0 +1,23 @@ +package com.strumenta.kolasu.lionweb + +import com.strumenta.kolasu.ids.NodeIdProvider +import com.strumenta.kolasu.model.Node +import java.util.IdentityHashMap +import java.util.UUID + +class UUIDNodeIdProvider : NodeIdProvider { + + private val cache = IdentityHashMap() + + override fun id(kNode: Node): String { + return cache.getOrPut(kNode) { + UUID.randomUUID().toString() + } + } + + override var parentProvider: NodeIdProvider? + get() = null + set(value) { + throw UnsupportedOperationException() + } +} From 8b2e2734b62ae3a7fb64537c3fb2f58f21ea9166 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Thu, 8 Aug 2024 12:51:26 +0200 Subject: [PATCH 10/24] Update dependency on LW Kotlin --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b12ba10d2..19d46d58a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ superPublish = { id = "com.vanniktech.maven.publish", version = "0.22.0" } [versions] lwjava = "0.2.18" -lwkotlin = "0.2.4" +lwkotlin = "0.2.5-SNAPSHOT" [libraries] lionwebjava = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-core", version.ref = "lwjava" } From 9835f7d1f9ec543918582f5d66c8304cececd413 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Thu, 8 Aug 2024 13:33:04 +0200 Subject: [PATCH 11/24] Adding SequentialNodeIdProvider --- .../strumenta/kolasu/ids/NodeIdProvider.kt | 4 +++ .../kolasu/lionweb/LionWebModelConverter.kt | 1 + .../lionweb/SequentialNodeIdProvider.kt | 32 +++++++++++++++++++ .../kolasu/lionweb/UUIDNodeIdProvider.kt | 8 +++++ 4 files changed, 45 insertions(+) create mode 100644 lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt diff --git a/core/src/main/kotlin/com/strumenta/kolasu/ids/NodeIdProvider.kt b/core/src/main/kotlin/com/strumenta/kolasu/ids/NodeIdProvider.kt index 97e60eea7..a282bda0d 100644 --- a/core/src/main/kotlin/com/strumenta/kolasu/ids/NodeIdProvider.kt +++ b/core/src/main/kotlin/com/strumenta/kolasu/ids/NodeIdProvider.kt @@ -10,6 +10,10 @@ import com.strumenta.kolasu.model.Node as KNode */ interface NodeIdProvider { fun id(kNode: KNode): String + fun registerMapping(kNode: KNode, nodeId: String) { + // do nothing + } + var parentProvider: NodeIdProvider? val topLevelProvider: NodeIdProvider get() = if (parentProvider == null) this else parentProvider!!.topLevelProvider diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt index 8d8c17928..96332326b 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt @@ -348,6 +348,7 @@ class LionWebModelConverter( val instantiated = instantiate(kClass, lwNode, referencesPostponer) if (instantiated is KNode) { instantiated.assignParents() + nodeIdProvider.registerMapping(instantiated, lwNode.id!!) } associateNodes(instantiated, lwNode) } catch (e: RuntimeException) { diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt new file mode 100644 index 000000000..98b7f2c7f --- /dev/null +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt @@ -0,0 +1,32 @@ +package com.strumenta.kolasu.lionweb + +import com.strumenta.kolasu.ids.NodeIdProvider +import com.strumenta.kolasu.model.Node +import java.util.IdentityHashMap + +class SequentialNodeIdProvider(startId: Long = 1L) : NodeIdProvider { + + private val cache = IdentityHashMap() + private var next = startId + + + override fun id(kNode: Node): String { + return cache.getOrPut(kNode) { + (next++).toString() + } + } + + override var parentProvider: NodeIdProvider? + get() = null + set(value) { + throw UnsupportedOperationException() + } + + override fun registerMapping(kNode: Node, nodeId: String) { + if (cache.containsKey(kNode)) { + require(cache[kNode] == nodeId) + } else { + cache[kNode] = nodeId + } + } +} diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt index 5e2d3b599..4fd7e7e91 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/UUIDNodeIdProvider.kt @@ -20,4 +20,12 @@ class UUIDNodeIdProvider : NodeIdProvider { set(value) { throw UnsupportedOperationException() } + + override fun registerMapping(kNode: Node, nodeId: String) { + if (cache.containsKey(kNode)) { + require(cache[kNode] == nodeId) + } else { + cache[kNode] = nodeId + } + } } From 5a5bb73536b70ba44eb89f5385177f51de50858f Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Thu, 8 Aug 2024 15:56:08 +0200 Subject: [PATCH 12/24] Wip --- .../com/strumenta/kolasu/lionwebclient/KolasuClient.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt index 984a3cbc2..f6b575040 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt +++ b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt @@ -24,6 +24,7 @@ import io.lionweb.lioncore.java.serialization.UnavailableNodePolicy import io.lionweb.lioncore.kotlin.repoclient.ClassifierResult import io.lionweb.lioncore.kotlin.repoclient.LionWebClient import io.lionweb.lioncore.kotlin.repoclient.RetrievalMode +import io.lionweb.lioncore.kotlin.repoclient.SerializationDecorator import io.lionweb.lioncore.kotlin.repoclient.debugFileHelper import kotlin.reflect.KClass import kotlin.reflect.KProperty1 @@ -469,4 +470,8 @@ class KolasuClient( ) { debugFileHelper(debug, relativePath, text) } + + fun registerSerializationDecorator(decorator: SerializationDecorator) { + lionWebClient.registerSerializationDecorator(decorator) + } } From 4972b25107df59bf19e6ec2e9e01979d7af8be11 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Fri, 9 Aug 2024 09:04:12 +0200 Subject: [PATCH 13/24] Update LW Java --- gradle/libs.versions.toml | 2 +- .../com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 19d46d58a..92c260513 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ buildConfig = { id = "com.github.gmazzo.buildconfig", version = "5.3.5" } superPublish = { id = "com.vanniktech.maven.publish", version = "0.22.0" } [versions] -lwjava = "0.2.18" +lwjava = "0.2.19-SNAPSHOT" lwkotlin = "0.2.5-SNAPSHOT" [libraries] diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt index 98b7f2c7f..85f7f0b77 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/SequentialNodeIdProvider.kt @@ -9,7 +9,6 @@ class SequentialNodeIdProvider(startId: Long = 1L) : NodeIdProvider { private val cache = IdentityHashMap() private var next = startId - override fun id(kNode: Node): String { return cache.getOrPut(kNode) { (next++).toString() From d4d9592c021a517d03517244d91dbb57f510ce8f Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Fri, 9 Aug 2024 09:20:20 +0200 Subject: [PATCH 14/24] Update KolasuClient.lionWebClient --- .../kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt index f6b575040..8184ae005 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt +++ b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt @@ -474,4 +474,8 @@ class KolasuClient( fun registerSerializationDecorator(decorator: SerializationDecorator) { lionWebClient.registerSerializationDecorator(decorator) } + + fun updateJsonSerialization() { + lionWebClient.updateJsonSerialization() + } } From d845b21af5bd1f073699b5bdcf7ecc3c6cf2c38a Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Fri, 9 Aug 2024 09:49:45 +0200 Subject: [PATCH 15/24] Revise JsonSerialization update in KolasuClient --- .../kolasu/lionwebclient/KolasuClient.kt | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt index 8184ae005..a3653de97 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt +++ b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt @@ -80,19 +80,37 @@ class KolasuClient( authorizationToken = authorizationToken, ) + private val serializationDecorators = mutableListOf() + + init { + registerSerializationDecorator { + it.apply { + enableDynamicNodes() + unavailableParentPolicy = UnavailableNodePolicy.NULL_REFERENCES + unavailableReferenceTargetPolicy = UnavailableNodePolicy.PROXY_NODES + } + nodeConverter.prepareSerialization( + it + ) as JsonSerialization + } + } + /** * Exposed for testing purposes */ - val jsonSerialization: JsonSerialization - get() { - return nodeConverter.prepareSerialization( - SerializationProvider.getStandardJsonSerialization().apply { - enableDynamicNodes() - unavailableParentPolicy = UnavailableNodePolicy.NULL_REFERENCES - unavailableReferenceTargetPolicy = UnavailableNodePolicy.PROXY_NODES - }, - ) as JsonSerialization - } + var jsonSerialization: JsonSerialization = calculateSerialization() + private set + + fun updateSerialization() { + this.jsonSerialization = calculateSerialization() + lionWebClient.updateJsonSerialization() + } + + private fun calculateSerialization() : JsonSerialization { + val jsonSerialization = SerializationProvider.getStandardJsonSerialization() + serializationDecorators.forEach { serializationDecorator -> serializationDecorator.invoke(jsonSerialization) } + return jsonSerialization + } // // Configuration @@ -472,10 +490,9 @@ class KolasuClient( } fun registerSerializationDecorator(decorator: SerializationDecorator) { - lionWebClient.registerSerializationDecorator(decorator) + // We do not need to specify them also for the lionWebClient, as it uses ours version of JsonSerialization + // lionWebClient.registerSerializationDecorator(decorator) + serializationDecorators.add(decorator) } - fun updateJsonSerialization() { - lionWebClient.updateJsonSerialization() - } } From b4989c0efd37bf25eaff0d8f101f6c3aed24f975 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Fri, 9 Aug 2024 13:51:15 +0200 Subject: [PATCH 16/24] Move in StarLasuLWLanguage the logic to of its primitive types serializers and deserializers --- lionweb/build.gradle | 2 +- .../lionweb/LionWebLanguageConverter.kt | 2 +- .../kolasu/lionweb/LionWebModelConverter.kt | 54 +----------------- .../kolasu/lionweb/StarLasuLWLanguage.kt | 55 ++++++++++++++++++- .../lionweb/LionWebModelConverterTest.kt | 1 + 5 files changed, 60 insertions(+), 54 deletions(-) diff --git a/lionweb/build.gradle b/lionweb/build.gradle index 1378906de..695af2a69 100644 --- a/lionweb/build.gradle +++ b/lionweb/build.gradle @@ -21,7 +21,7 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" testImplementation "com.google.code.gson:gson:$gson_version" api(libs.lionwebjava) - api("io.lionweb.lionweb-kotlin:lionweb-kotlin-2024.1-core:0.2.2") { + api(libs.lionwebkotlincore) { exclude group: "org.jetbrains.kotlin", module: "kotlin-test-junit5" } api(project(":core")) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt index 54a73cdee..83aab6908 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebLanguageConverter.kt @@ -332,7 +332,7 @@ class LionWebLanguageConverter { Long::class.createType() -> LionCoreBuiltins.getInteger() String::class.createType() -> LionCoreBuiltins.getString() Boolean::class.createType() -> LionCoreBuiltins.getBoolean() - Char::class.createType() -> StarLasuLWLanguage.char + Char::class.createType() -> StarLasuLWLanguage.Char else -> { val kClass = kType.classifier as KClass<*> val isEnum = kClass.supertypes.any { it.classifier == Enum::class } diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt index 96332326b..81a0e7fa4 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverter.kt @@ -8,7 +8,6 @@ import com.strumenta.kolasu.language.Feature import com.strumenta.kolasu.language.KolasuLanguage import com.strumenta.kolasu.model.CompositeDestination import com.strumenta.kolasu.model.Multiplicity -import com.strumenta.kolasu.model.Point import com.strumenta.kolasu.model.Position import com.strumenta.kolasu.model.PossiblyNamed import com.strumenta.kolasu.model.ReferenceByName @@ -45,11 +44,10 @@ import io.lionweb.lioncore.java.model.impl.EnumerationValueImpl import io.lionweb.lioncore.java.model.impl.ProxyNode import io.lionweb.lioncore.java.serialization.AbstractSerialization import io.lionweb.lioncore.java.serialization.JsonSerialization -import io.lionweb.lioncore.java.serialization.PrimitiveValuesSerialization.PrimitiveDeserializer -import io.lionweb.lioncore.java.serialization.PrimitiveValuesSerialization.PrimitiveSerializer import io.lionweb.lioncore.java.serialization.SerializationProvider import io.lionweb.lioncore.java.utils.CommonChecks import io.lionweb.lioncore.kotlin.BaseNode +import io.lionweb.lioncore.kotlin.MetamodelRegistry import io.lionweb.lioncore.kotlin.getChildrenByContainmentName import io.lionweb.lioncore.kotlin.getOnlyChildByContainmentName import java.util.IdentityHashMap @@ -393,54 +391,8 @@ class LionWebModelConverter( serialization: AbstractSerialization = SerializationProvider.getStandardJsonSerialization() ): AbstractSerialization { - serialization.primitiveValuesSerialization.registerSerializer( - StarLasuLWLanguage.char.id - ) { value -> "$value" } - serialization.primitiveValuesSerialization.registerDeserializer( - StarLasuLWLanguage.char.id - ) { serialized -> serialized[0] } - val pointSerializer: PrimitiveSerializer = - PrimitiveSerializer { value -> - if (value == null) { - return@PrimitiveSerializer null - } - "L${value.line}:${value.column}" - } - val pointDeserializer: PrimitiveDeserializer = - PrimitiveDeserializer { serialized -> - if (serialized == null) { - return@PrimitiveDeserializer null - } - require(serialized.startsWith("L")) - require(serialized.removePrefix("L").isNotEmpty()) - val parts = serialized.removePrefix("L").split(":") - require(parts.size == 2) - Point(parts[0].toInt(), parts[1].toInt()) - } - serialization.primitiveValuesSerialization.registerSerializer( - StarLasuLWLanguage.Point.id, - pointSerializer - ) - serialization.primitiveValuesSerialization.registerDeserializer( - StarLasuLWLanguage.Point.id, - pointDeserializer - ) - serialization.primitiveValuesSerialization.registerSerializer( - StarLasuLWLanguage.Position.id - ) { value -> - "${pointSerializer.serialize((value as Position).start)} to ${pointSerializer.serialize(value.end)}" - } - serialization.primitiveValuesSerialization.registerDeserializer( - StarLasuLWLanguage.Position.id - ) { serialized -> - if (serialized == null) { - null - } else { - val parts = serialized.split(" to ") - require(parts.size == 2) - Position(pointDeserializer.deserialize(parts[0]), pointDeserializer.deserialize(parts[1])) - } - } + StarLasuLWLanguage + MetamodelRegistry.prepareJsonSerialization(serialization) synchronized(languageConverter) { languageConverter.knownLWLanguages().forEach { serialization.primitiveValuesSerialization.registerLanguage(it) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt index 9fb8b28a3..00849f856 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt @@ -1,6 +1,8 @@ package com.strumenta.kolasu.lionweb import com.strumenta.kolasu.model.Multiplicity +import com.strumenta.kolasu.model.Point +import com.strumenta.kolasu.model.Position import com.strumenta.kolasu.validation.IssueSeverity import com.strumenta.kolasu.validation.IssueType import io.lionweb.lioncore.java.language.Annotation @@ -12,6 +14,9 @@ import io.lionweb.lioncore.java.language.PrimitiveType import io.lionweb.lioncore.java.language.Property import io.lionweb.lioncore.java.language.Reference import io.lionweb.lioncore.java.self.LionCore +import io.lionweb.lioncore.java.serialization.PrimitiveValuesSerialization.PrimitiveDeserializer +import io.lionweb.lioncore.java.serialization.PrimitiveValuesSerialization.PrimitiveSerializer +import io.lionweb.lioncore.kotlin.MetamodelRegistry private const val PLACEHOLDER_NODE = "PlaceholderNode" @@ -59,6 +64,7 @@ object StarLasuLWLanguage : Language("com.strumenta.StarLasu") { addContainment("root", ASTNode, Multiplicity.OPTIONAL) addProperty("code", LionCoreBuiltins.getString(), Multiplicity.OPTIONAL) } + registerSerializersAndDeserializersInMetamodelRegistry() } private fun addPlaceholderNodeAnnotation(astNode: Concept) { @@ -100,7 +106,7 @@ object StarLasuLWLanguage : Language("com.strumenta.StarLasu") { val ASTNode: Concept get() = StarLasuLWLanguage.getConceptByName("ASTNode")!! - val char: PrimitiveType + val Char: PrimitiveType get() = StarLasuLWLanguage.getPrimitiveTypeByName("Char")!! val PlaceholderNode: Annotation @@ -128,3 +134,50 @@ object StarLasuLWLanguage : Language("com.strumenta.StarLasu") { val TypeAnnotation: Interface get() = StarLasuLWLanguage.getInterfaceByName("TypeAnnotation")!! } + +private fun registerSerializersAndDeserializersInMetamodelRegistry() { + val charSerializer = PrimitiveSerializer { value -> "$value" } + val charDeserializer = PrimitiveDeserializer { serialized -> + require(serialized.length == 1) + serialized[0] + } + MetamodelRegistry.addSerializerAndDeserializer(StarLasuLWLanguage.Char, charSerializer, charDeserializer) + + val pointSerializer: PrimitiveSerializer = + PrimitiveSerializer { value -> + if (value == null) { + return@PrimitiveSerializer null + } + "L${value.line}:${value.column}" + } + val pointDeserializer: PrimitiveDeserializer = + PrimitiveDeserializer { serialized -> + if (serialized == null) { + return@PrimitiveDeserializer null + } + require(serialized.startsWith("L")) + require(serialized.removePrefix("L").isNotEmpty()) + val parts = serialized.removePrefix("L").split(":") + require(parts.size == 2) + Point(parts[0].toInt(), parts[1].toInt()) + } + MetamodelRegistry.addSerializerAndDeserializer(StarLasuLWLanguage.Point, pointSerializer, pointDeserializer) + + val positionSerializer = PrimitiveSerializer { value -> + "${pointSerializer.serialize((value as Position).start)} to ${pointSerializer.serialize(value.end)}" + } + val positionDeserializer = PrimitiveDeserializer { serialized -> + if (serialized == null) { + null + } else { + val parts = serialized.split(" to ") + require(parts.size == 2) + Position(pointDeserializer.deserialize(parts[0]), pointDeserializer.deserialize(parts[1])) + } + } + MetamodelRegistry.addSerializerAndDeserializer( + StarLasuLWLanguage.Position, + positionSerializer, + positionDeserializer + ) +} diff --git a/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt b/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt index 75aa9edba..01550cc40 100644 --- a/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt +++ b/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt @@ -717,6 +717,7 @@ class LionWebModelConverterTest { @Test fun canDeserializePosition() { + StarLasuLWLanguage val kl = KolasuLanguage("my.language").apply { addClass(NodeWithEnum::class) } From 39cc0828c006774e4484bf816b135e93a062e742 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Mon, 12 Aug 2024 13:35:21 +0200 Subject: [PATCH 17/24] Ensure we do not map common elements when populating Kolasu Languages --- .../com/strumenta/kolasu/language/KolasuLanguage.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/com/strumenta/kolasu/language/KolasuLanguage.kt b/core/src/main/kotlin/com/strumenta/kolasu/language/KolasuLanguage.kt index 99e98041f..112d72840 100644 --- a/core/src/main/kotlin/com/strumenta/kolasu/language/KolasuLanguage.kt +++ b/core/src/main/kotlin/com/strumenta/kolasu/language/KolasuLanguage.kt @@ -115,9 +115,13 @@ class KolasuLanguage(val qualifiedName: String) { fun tentativeAddClass( kClass: KClass, - exceptions: MutableList = mutableListOf() + exceptions: MutableList = mutableListOf() ): Attempt { - if (kClass == Node::class || kClass == Named::class || kClass == PossiblyNamed::class) { + if (kClass == Node::class || kClass == Named::class || kClass == PossiblyNamed::class || + kClass.superclasses.contains( + CommonElement::class + ) + ) { return Attempt(false, exceptions) } if (!_astClasses.contains(kClass) && _astClasses.add(kClass)) { From 12435500f27f9495020af727c5757c61103533bb Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Mon, 12 Aug 2024 14:44:24 +0200 Subject: [PATCH 18/24] Updating dependencies --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 92c260513..b1e42c947 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,8 +3,8 @@ buildConfig = { id = "com.github.gmazzo.buildconfig", version = "5.3.5" } superPublish = { id = "com.vanniktech.maven.publish", version = "0.22.0" } [versions] -lwjava = "0.2.19-SNAPSHOT" -lwkotlin = "0.2.5-SNAPSHOT" +lwjava = "0.2.19" +lwkotlin = "0.2.5" [libraries] lionwebjava = { group = "io.lionweb.lionweb-java", name = "lionweb-java-2023.1-core", version.ref = "lwjava" } From d1b2f76d1c11e96abee9759cdc83e20e626f3ee5 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Mon, 12 Aug 2024 14:46:18 +0200 Subject: [PATCH 19/24] Cleaning --- .../kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt index a3653de97..e5b7a5b4a 100644 --- a/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt +++ b/lionwebrepo-client/src/main/kotlin/com/strumenta/kolasu/lionwebclient/KolasuClient.kt @@ -491,7 +491,6 @@ class KolasuClient( fun registerSerializationDecorator(decorator: SerializationDecorator) { // We do not need to specify them also for the lionWebClient, as it uses ours version of JsonSerialization - // lionWebClient.registerSerializationDecorator(decorator) serializationDecorators.add(decorator) } From ac319c8a986b7ad65292c29f6cf8199732d57135 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Mon, 12 Aug 2024 14:49:01 +0200 Subject: [PATCH 20/24] Making serialized representation of Position more concise --- .../kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt | 4 ++-- .../com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt index 00849f856..e039df1f6 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt @@ -164,13 +164,13 @@ private fun registerSerializersAndDeserializersInMetamodelRegistry() { MetamodelRegistry.addSerializerAndDeserializer(StarLasuLWLanguage.Point, pointSerializer, pointDeserializer) val positionSerializer = PrimitiveSerializer { value -> - "${pointSerializer.serialize((value as Position).start)} to ${pointSerializer.serialize(value.end)}" + "${pointSerializer.serialize((value as Position).start)}-${pointSerializer.serialize(value.end)}" } val positionDeserializer = PrimitiveDeserializer { serialized -> if (serialized == null) { null } else { - val parts = serialized.split(" to ") + val parts = serialized.split("-") require(parts.size == 2) Position(pointDeserializer.deserialize(parts[0]), pointDeserializer.deserialize(parts[1])) } diff --git a/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt b/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt index 01550cc40..785fdd0ce 100644 --- a/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt +++ b/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt @@ -709,7 +709,7 @@ class LionWebModelConverterTest { mc.prepareSerialization(jsonSerialization) val serializationBlock = jsonSerialization.serializeNodesToSerializationBlock(lwNode) assertEquals( - "L3:5 to L27:200", + "L3:5-L27:200", serializationBlock.classifierInstancesByID["MySource"]!! .getPropertyValue("com_strumenta_starlasu-ASTNode-position-key") ) From c39467ed756514f28350419489ad160945101758 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Tue, 13 Aug 2024 07:31:33 +0200 Subject: [PATCH 21/24] Adding comments --- .../kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt | 4 ++++ .../com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt index e039df1f6..34ce5caee 100644 --- a/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt +++ b/lionweb/src/main/kotlin/com/strumenta/kolasu/lionweb/StarLasuLWLanguage.kt @@ -20,6 +20,10 @@ import io.lionweb.lioncore.kotlin.MetamodelRegistry private const val PLACEHOLDER_NODE = "PlaceholderNode" +/** + * When this object is referenced the initialization is performed. When that happens the serializers and deserializers + * for Position and other primitive types are registered in the MetamodelRegistry. + */ object StarLasuLWLanguage : Language("com.strumenta.StarLasu") { val CommonElement: Interface diff --git a/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt b/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt index 785fdd0ce..d8f605b80 100644 --- a/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt +++ b/lionweb/src/test/kotlin/com/strumenta/kolasu/lionweb/LionWebModelConverterTest.kt @@ -717,6 +717,8 @@ class LionWebModelConverterTest { @Test fun canDeserializePosition() { + // This has the side effect on ensuring that the serializers and deserializers for primitive types in the + // StarLasu Language are registered in the MetamodelRegistry StarLasuLWLanguage val kl = KolasuLanguage("my.language").apply { addClass(NodeWithEnum::class) From 1b7101b1261b37be8cbccd1e09f47bb804149580 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Tue, 13 Aug 2024 07:33:28 +0200 Subject: [PATCH 22/24] Add .java-version to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 28cb6430b..9e927d053 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ generated-test-src **/bin/ .netlify .kotlin +.java-version From 6b8af492f50b32c228afb876e296fbd559d938c4 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Tue, 13 Aug 2024 07:38:41 +0200 Subject: [PATCH 23/24] [Gradle Release Plugin] - pre tag commit: '1.5.69'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d98fec0f7..7c2c4aa68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=1.5.69-SNAPSHOT +version=1.5.69 # Note that we use the EXACT same version of Kotlin as the one included in Gradle, to avoid # conflicts kotlin_version=1.8.20 From 19215e31dfd72ee7d78d5a33e367a13cf43417cc Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Tue, 13 Aug 2024 07:38:49 +0200 Subject: [PATCH 24/24] [Gradle Release Plugin] - new version commit: '1.5.70-SNAPSHOT'. --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7c2c4aa68..b008d126c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=1.5.69 +version=1.5.70-SNAPSHOT # Note that we use the EXACT same version of Kotlin as the one included in Gradle, to avoid # conflicts kotlin_version=1.8.20