diff --git a/commitlint.config.js b/commitlint.config.js index 3a1afd8088..509ee7f6d9 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -23,6 +23,7 @@ module.exports = { "mps-model-adapters", "mps-model-server", "mps-sync-plugin", + "mps-multiplatform-lib", "openapi", "streams", "ts-model-api", diff --git a/model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReference.kt b/model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReference.kt index 6112a84681..b286cdcb9f 100644 --- a/model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReference.kt +++ b/model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReference.kt @@ -66,3 +66,10 @@ class NodeReferenceKSerializer : KSerializer { } class UnresolvableNodeReferenceException(val reference: INodeReference) : IllegalArgumentException("Node not found: $reference") + +interface NodeReferenceConverter { + fun tryConvert(ref: INodeReference): E? + fun convert(ref: INodeReference): E = requireNotNull(tryConvert(ref)) { + "Reference cannot be converted to $this: $ref" + } +} diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt index 1d095e0f44..4b1faa4629 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt @@ -14,13 +14,20 @@ import org.modelix.model.api.IConcept import org.modelix.model.api.IConceptReference import org.modelix.model.api.INode import org.modelix.model.api.INodeReference -import org.modelix.model.api.NodeReference import org.modelix.model.area.IArea import org.modelix.model.area.IAreaListener import org.modelix.model.area.IAreaReference +import org.modelix.mps.multiplatform.model.MPSDevKitDependencyReference +import org.modelix.mps.multiplatform.model.MPSJavaModuleFacetReference +import org.modelix.mps.multiplatform.model.MPSModelImportReference import org.modelix.mps.multiplatform.model.MPSModelReference +import org.modelix.mps.multiplatform.model.MPSModuleDependencyReference import org.modelix.mps.multiplatform.model.MPSModuleReference import org.modelix.mps.multiplatform.model.MPSNodeReference +import org.modelix.mps.multiplatform.model.MPSProjectModuleReference +import org.modelix.mps.multiplatform.model.MPSProjectReference +import org.modelix.mps.multiplatform.model.MPSRepositoryReference +import org.modelix.mps.multiplatform.model.MPSSingleLanguageDependencyReference data class MPSArea(val repository: SRepository) : IArea, IAreaReference { @@ -162,89 +169,38 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference { } private fun resolveMPSDevKitDependencyReference(ref: INodeReference): MPSDevKitDependencyAsNode? { - if (ref is MPSDevKitDependencyReference) { - return when { - ref.userModule != null -> ref.userModule.resolve(repository) - ?.let { MPSModuleAsNode(it).findDevKitDependency(ref.usedModuleId) } - ref.userModel != null -> ref.userModel.resolve(repository) - ?.let { MPSModelAsNode(it).findDevKitDependency(ref.usedModuleId) } - else -> error("No importer found.") - } - } - val serialized = ref.serialize() - val serializedModuleId = serialized.substringAfter("${MPSDevKitDependencyReference.PREFIX}:") - .substringBefore(MPSDevKitDependencyReference.SEPARATOR) - - val importer = serialized.substringAfter(MPSDevKitDependencyReference.SEPARATOR) - val foundImporter = resolveNode(NodeReference(importer))?.asWritableNode() - - val moduleId = PersistenceFacade.getInstance().createModuleId(serializedModuleId) - - return when (foundImporter) { - is MPSModelAsNode -> foundImporter.findDevKitDependency(moduleId) - is MPSModuleAsNode<*> -> foundImporter.findDevKitDependency(moduleId) - else -> null + val ref = MPSDevKitDependencyReference.tryConvert(ref) ?: return null + val userModule = ref.userModule + val userModel = ref.userModel + return when { + userModule != null -> userModule.toMPS().resolve(repository) + ?.let { MPSModuleAsNode(it).findDevKitDependency(ref.usedModuleId.toMPS().moduleId) } + userModel != null -> userModel.toMPS().resolve(repository) + ?.let { MPSModelAsNode(it).findDevKitDependency(ref.usedModuleId.toMPS().moduleId) } + else -> error("No importer found.") } } private fun resolveMPSJavaModuleFacetReference(ref: INodeReference): MPSJavaModuleFacetAsNode? { - val moduleRef = if (ref is MPSJavaModuleFacetReference) { - ref.moduleReference - } else { - val serialized = ref.serialize() - val serializedModuleRef = serialized.substringAfter("${MPSJavaModuleFacetReference.PREFIX}:") - MPSReferenceParser.parseSModuleReference(serializedModuleRef) - } - - val facet = moduleRef.resolve(repository)?.getFacetOfType(JavaModuleFacet.FACET_TYPE) + val ref = MPSJavaModuleFacetReference.tryConvert(ref) ?: return null + val facet = ref.moduleReference.toMPS().resolve(repository)?.getFacetOfType(JavaModuleFacet.FACET_TYPE) return facet?.let { MPSJavaModuleFacetAsNode(it as JavaModuleFacet) } } private fun resolveMPSModelImportReference(ref: INodeReference): MPSModelImportAsNode? { - val serialized = ref.serialize() - val importedModelRef = if (ref is MPSModelImportReference) { - ref.importedModel - } else { - val serializedModelRef = serialized - .substringAfter("${MPSModelImportReference.PREFIX}:") - .substringBefore(MPSModelImportReference.SEPARATOR) - MPSReferenceParser.parseSModelReference(serializedModelRef) - } - - val importingModelRef = if (ref is MPSModelImportReference) { - ref.importingModel - } else { - val serializedModelRef = serialized.substringAfter(MPSModelImportReference.SEPARATOR) - MPSReferenceParser.parseSModelReference(serializedModelRef) - } + val ref = MPSModelImportReference.tryConvert(ref) ?: return null - val importingModel = importingModelRef.resolve(repository) ?: return null - if (!ModelImports(importingModel).importedModels.contains(importedModelRef)) return null + val importingModel = ref.importingModel.toMPS().resolve(repository) ?: return null + if (!ModelImports(importingModel).importedModels.contains(ref.importedModel.toMPS())) return null - return MPSModelImportAsNode(importedModel = importedModelRef, importingModel = importingModel) + return MPSModelImportAsNode(importedModel = ref.importedModel.toMPS(), importingModel = importingModel) } private fun resolveMPSModuleDependencyReference(ref: INodeReference): MPSModuleDependencyAsNode? { - val serialized = ref.serialize() - val usedModuleId = if (ref is MPSModuleDependencyReference) { - ref.usedModuleId - } else { - val serializedModuleId = serialized - .substringAfter("${MPSModuleDependencyReference.PREFIX}:") - .substringBefore(MPSModuleDependencyReference.SEPARATOR) - PersistenceFacade.getInstance().createModuleId(serializedModuleId) - } - - val userModuleReference = if (ref is MPSModuleDependencyReference) { - ref.userModuleReference - } else { - val serializedModuleRef = serialized.substringAfter(MPSModuleDependencyReference.SEPARATOR) - MPSReferenceParser.parseSModuleReference(serializedModuleRef) - } - - return userModuleReference.resolve(repository) + val ref = MPSModuleDependencyReference.tryConvert(ref) ?: return null + return ref.userModuleReference.toMPS().resolve(repository) ?.let { MPSModuleAsNode(it) } - ?.findModuleDependency(usedModuleId) + ?.findModuleDependency(ref.usedModuleId.toMPS().moduleId) } private fun resolveMPSModuleReferenceReference(ref: INodeReference): MPSModuleReferenceAsNode? { @@ -281,28 +237,15 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference { } private fun resolveMPSSingleLanguageDependencyReference(ref: INodeReference): MPSSingleLanguageDependencyAsNode? { - if (ref is MPSSingleLanguageDependencyReference) { - return when { - ref.userModule != null -> ref.userModule.resolve(repository) - ?.let { MPSModuleAsNode(it).findSingleLanguageDependency(ref.usedModuleId) } - ref.userModel != null -> ref.userModel.resolve(repository) - ?.let { MPSModelAsNode(it).findSingleLanguageDependency(ref.usedModuleId) } - else -> error("No importer found.") - } - } - val serialized = ref.serialize() - val serializedModuleId = serialized.substringAfter("${MPSSingleLanguageDependencyReference.PREFIX}:") - .substringBefore(MPSSingleLanguageDependencyReference.SEPARATOR) - - val importer = serialized.substringAfter(MPSSingleLanguageDependencyReference.SEPARATOR) - val foundImporter = resolveNode(NodeReference(importer))?.asWritableNode() - - val moduleId = PersistenceFacade.getInstance().createModuleId(serializedModuleId) - - return when (foundImporter) { - is MPSModelAsNode -> foundImporter.findSingleLanguageDependency(moduleId) - is MPSModuleAsNode<*> -> foundImporter.findSingleLanguageDependency(moduleId) - else -> null + val ref = MPSSingleLanguageDependencyReference.tryConvert(ref) ?: return null + val userModule = ref.userModule + val userModel = ref.userModel + return when { + userModule != null -> userModule.toMPS().resolve(repository) + ?.let { MPSModuleAsNode(it).findSingleLanguageDependency(ref.usedModuleId.toMPS().moduleId) } + userModel != null -> userModel.toMPS().resolve(repository) + ?.let { MPSModelAsNode(it).findSingleLanguageDependency(ref.usedModuleId.toMPS().moduleId) } + else -> error("No importer found.") } } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelImportAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelImportAsNode.kt index c14e56a758..3d16e1474f 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelImportAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModelImportAsNode.kt @@ -9,6 +9,7 @@ import org.modelix.model.api.INodeReference import org.modelix.model.api.IPropertyReference import org.modelix.model.api.IReferenceLinkReference import org.modelix.model.api.IWritableNode +import org.modelix.mps.multiplatform.model.MPSModelImportReference data class MPSModelImportAsNode(val importedModel: SModelReference, val importingModel: SModel) : MPSGenericNodeAdapter() { @@ -60,8 +61,8 @@ data class MPSModelImportAsNode(val importedModel: SModelReference, val importin override fun getNodeReference(): INodeReference { return MPSModelImportReference( - importedModel = importedModel, - importingModel = importingModel.reference, + importedModel = importedModel.toModelix(), + importingModel = importingModel.reference.toModelix(), ) } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleDependencyAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleDependencyAsNode.kt index c3adcd60a7..b6906c6d7e 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleDependencyAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSModuleDependencyAsNode.kt @@ -13,6 +13,7 @@ import org.modelix.model.api.INodeReference import org.modelix.model.api.IPropertyReference import org.modelix.model.api.IReferenceLinkReference import org.modelix.model.api.IWritableNode +import org.modelix.mps.multiplatform.model.MPSModuleDependencyReference data class MPSModuleDependencyAsNode( val owner: SModule, @@ -167,8 +168,8 @@ data class MPSModuleDependencyAsNode( override fun getNodeReference(): INodeReference { return MPSModuleDependencyReference( - usedModuleId = moduleReference.moduleId, - userModuleReference = owner.moduleReference, + usedModuleId = moduleReference.moduleId.toModelix(), + userModuleReference = owner.moduleReference.toModelix(), ) } diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSProjectAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSProjectAsNode.kt index 1e5dcebf5c..ef0d0bd309 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSProjectAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSProjectAsNode.kt @@ -11,6 +11,7 @@ import org.modelix.model.api.IReferenceLinkReference import org.modelix.model.api.IWritableNode import org.modelix.mps.api.ModelixMpsApi import org.modelix.mps.multiplatform.model.MPSModuleReference +import org.modelix.mps.multiplatform.model.MPSProjectReference data class MPSProjectAsNode(val project: IMPSProject) : MPSGenericNodeAdapter() { diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSReferences.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSReferences.kt index a4aab773b2..b1352df9f4 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSReferences.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSReferences.kt @@ -8,13 +8,13 @@ import org.jetbrains.mps.openapi.model.SNodeReference import org.jetbrains.mps.openapi.module.SModuleId import org.jetbrains.mps.openapi.module.SModuleReference import org.jetbrains.mps.openapi.persistence.PersistenceFacade -import org.modelix.model.api.INodeReference -import org.modelix.model.api.NodeReference import org.modelix.mps.multiplatform.model.MPSModelReference import org.modelix.mps.multiplatform.model.MPSModuleReference import org.modelix.mps.multiplatform.model.MPSNodeReference +import org.modelix.mps.multiplatform.model.MPSProjectReference -fun SModuleReference.toModelix() = MPSModuleReference(moduleId.toString()) +fun SModuleId.toModelix() = MPSModuleReference(toString()) +fun SModuleReference.toModelix() = moduleId.toModelix() fun MPSModuleReference.toMPS(): SModuleReference = PersistenceFacade.getInstance().createModuleReference( PersistenceFacade.getInstance().createModuleId(moduleId), null, @@ -54,153 +54,22 @@ fun SModuleReference.withoutNames(): String { return StringUtil.escapeRefChars(moduleId.toString()) } -data class MPSDevKitDependencyReference( - val usedModuleId: SModuleId, - val userModule: SModuleReference? = null, - val userModel: SModelReference? = null, -) : INodeReference() { +fun MPSDevKitDependencyReference( + usedModuleId: SModuleId, + userModule: SModuleReference? = null, + userModel: SModelReference? = null, +) = org.modelix.mps.multiplatform.model.MPSDevKitDependencyReference(usedModuleId.toModelix(), userModule?.toModelix(), userModel?.toModelix()) - companion object { - internal const val PREFIX = "mps-devkit" - internal const val SEPARATOR = "#IN#" - } - - override fun serialize(): String { - val importer = userModule?.let { "mps-module:${it.withoutNames()}" } - ?: userModel?.let { "mps-model:${it.withoutNames()}" } - ?: error("importer not found") +fun MPSJavaModuleFacetReference(moduleReference: SModuleReference) = org.modelix.mps.multiplatform.model.MPSJavaModuleFacetReference(moduleReference.toModelix()) - return "$PREFIX:$usedModuleId$SEPARATOR$importer" - } -} +fun MPSProjectReference(projectName: String) = org.modelix.mps.multiplatform.model.MPSProjectReference(projectName) +fun MPSProjectReference(project: IMPSProject) = MPSProjectReference(project.getName()) +fun MPSProjectReference(project: org.jetbrains.mps.openapi.project.Project) = MPSProjectReference(MPSProjectAdapter(project)) -data class MPSJavaModuleFacetReference(val moduleReference: SModuleReference) : INodeReference() { +fun MPSProjectModuleReference(moduleRef: SModuleReference, projectRef: MPSProjectReference) = org.modelix.mps.multiplatform.model.MPSProjectModuleReference(moduleRef.toModelix(), projectRef) - companion object { - internal const val PREFIX = "mps-java-facet" - } - - override fun serialize(): String { - return "$PREFIX:${moduleReference.withoutNames()}" - } -} - -data class MPSModelImportReference( - val importedModel: SModelReference, - val importingModel: SModelReference, -) : INodeReference() { - - companion object { - internal const val PREFIX = "mps-model-import" - internal const val SEPARATOR = "#IN#" - } - - override fun serialize(): String { - return "$PREFIX:${importedModel.withoutNames()}$SEPARATOR${importingModel.withoutNames()}" - } -} - -data class MPSModuleDependencyReference( - val usedModuleId: SModuleId, - val userModuleReference: SModuleReference, -) : INodeReference() { - - companion object { - internal const val PREFIX = "mps-module-dep" - internal const val SEPARATOR = "#IN#" - } - - override fun serialize(): String { - return "$PREFIX:$usedModuleId$SEPARATOR${userModuleReference.withoutNames()}" - } -} - -data class MPSProjectReference(val projectName: String) : INodeReference() { - - companion object { - internal const val PREFIX = "mps-project" - internal const val PREFIX_COLON = "$PREFIX:" - - fun tryConvert(ref: INodeReference): MPSProjectReference? { - if (ref is MPSProjectReference) return ref - val serialized = ref.serialize() - return if (serialized.startsWith(PREFIX_COLON)) { - MPSProjectReference(serialized.substringAfter(PREFIX_COLON)) - } else { - null - } - } - - fun convert(ref: INodeReference): MPSProjectReference = requireNotNull(tryConvert(ref)) { - "Not a project reference: $ref" - } - } - - constructor(project: IMPSProject) : this(project.getName()) - constructor(project: org.jetbrains.mps.openapi.project.Project) : this(MPSProjectAdapter(project)) - - override fun serialize(): String { - return "$PREFIX:$projectName" - } -} - -data class MPSProjectModuleReference(val moduleRef: SModuleReference, val projectRef: MPSProjectReference) : INodeReference() { - - constructor(moduleRef: MPSModuleReference, projectRef: MPSProjectReference) : this(moduleRef.toMPS(), projectRef) - - companion object { - internal const val PREFIX = "mps-project-module" - internal const val PREFIX_COLON = "$PREFIX:" - internal const val SEPARATOR = "#IN#" - - fun tryConvert(ref: INodeReference): MPSProjectModuleReference? { - if (ref is MPSProjectModuleReference) return ref - val serialized = ref.serialize() - if (!serialized.startsWith(PREFIX_COLON)) return null - val moduleRef = serialized - .substringAfter(PREFIX_COLON) - .substringBefore(SEPARATOR) - .let { MPSReferenceParser.parseSModuleReference(it) } - val projectRef = NodeReference(serialized.substringAfter(SEPARATOR)) - .let { MPSProjectReference.tryConvert(it) } - .let { requireNotNull(it) { "Invalid project reference: $it" } } - return MPSProjectModuleReference(moduleRef, projectRef) - } - - fun convert(ref: INodeReference) = requireNotNull(tryConvert(ref)) { - "Not a project module: $ref" - } - } - - override fun serialize(): String { - return "$PREFIX:${moduleRef.withoutNames()}$SEPARATOR${projectRef.serialize()}" - } -} - -data class MPSSingleLanguageDependencyReference( - val usedModuleId: SModuleId, - val userModule: SModuleReference? = null, - val userModel: SModelReference? = null, -) : INodeReference() { - - companion object { - internal const val PREFIX = "mps-lang" - internal const val SEPARATOR = "#IN#" - } - - override fun serialize(): String { - val importer = userModule?.let { "mps-module:${it.withoutNames()}" } - ?: userModel?.let { "mps-model:${it.withoutNames()}" } - ?: error("importer not found") - - return "$PREFIX:$usedModuleId$SEPARATOR$importer" - } -} - -object MPSRepositoryReference : INodeReference() { - internal const val PREFIX = "mps-repository" - - override fun serialize(): String { - return "$PREFIX:repository" - } -} +fun MPSSingleLanguageDependencyReference( + usedModuleId: SModuleId, + userModule: SModuleReference? = null, + userModel: SModelReference? = null, +) = org.modelix.mps.multiplatform.model.MPSSingleLanguageDependencyReference(usedModuleId.toModelix(), userModule?.toModelix(), userModel?.toModelix()) diff --git a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt index 9dc16c4c73..7f3b141133 100644 --- a/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt +++ b/mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSRepositoryAsNode.kt @@ -23,6 +23,7 @@ import org.modelix.model.api.IReadableNode import org.modelix.model.api.IReferenceLinkReference import org.modelix.model.api.IWritableNode import org.modelix.model.api.NullChildLinkReference +import org.modelix.mps.multiplatform.model.MPSRepositoryReference fun SRepository.asLegacyNode(): INode = MPSRepositoryAsNode(this).asLegacyNode() fun SRepository.asWritableNode(): IWritableNode = MPSRepositoryAsNode(this) diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSDevKitDependencyReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSDevKitDependencyReference.kt new file mode 100644 index 0000000000..8a67fa8289 --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSDevKitDependencyReference.kt @@ -0,0 +1,40 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSDevKitDependencyReference( + val usedModuleId: MPSModuleReference, + val userModule: MPSModuleReference? = null, + val userModel: MPSModelReference? = null, +) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-devkit" + const val PREFIX_COLON = "$PREFIX:" + const val SEPARATOR = "#IN#" + + override fun tryConvert(ref: INodeReference): MPSDevKitDependencyReference? { + if (ref is MPSDevKitDependencyReference) return ref + + val serialized = ref.serialize() + val target = serialized.substringAfter(PREFIX_COLON).substringBefore(SEPARATOR) + val source = serialized.substringAfter(SEPARATOR).let { NodeReference(it) } + + return MPSDevKitDependencyReference( + MPSModuleReference.parseSModuleReference(target), + MPSModuleReference.tryConvert(source), + MPSModelReference.tryConvert(source), + ) + } + } + + override fun serialize(): String { + val importer = userModule?.serialize() + ?: userModel?.serialize() + ?: error("importer not found") + + return "$PREFIX:${usedModuleId.toMPSString()}$SEPARATOR$importer" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSJavaModuleFacetReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSJavaModuleFacetReference.kt new file mode 100644 index 0000000000..fd82d70ab0 --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSJavaModuleFacetReference.kt @@ -0,0 +1,25 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSJavaModuleFacetReference(val moduleReference: MPSModuleReference) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-java-facet" + const val PREFIX_COLON = "$PREFIX:" + + override fun tryConvert(ref: INodeReference): MPSJavaModuleFacetReference? { + if (ref is MPSJavaModuleFacetReference) return ref + + val serialized = ref.serialize() + if (!serialized.startsWith(PREFIX_COLON)) return null + + return MPSJavaModuleFacetReference(MPSModuleReference.parseSModuleReference(serialized.substringAfter(PREFIX_COLON))) + } + } + + override fun serialize(): String { + return "$PREFIX:${moduleReference.toMPSString()}" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelImportReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelImportReference.kt new file mode 100644 index 0000000000..60b8c83211 --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelImportReference.kt @@ -0,0 +1,34 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSModelImportReference( + val importedModel: MPSModelReference, + val importingModel: MPSModelReference, +) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-model-import" + const val PREFIX_COLON = "$PREFIX:" + const val SEPARATOR = "#IN#" + + override fun tryConvert(ref: INodeReference): MPSModelImportReference? { + if (ref is MPSModelImportReference) return ref + + val serialized = ref.serialize() + if (!serialized.startsWith(PREFIX_COLON)) return null + + val serializedTarget = serialized.substringAfter(PREFIX_COLON).substringBefore(SEPARATOR) + val serializedSource = serialized.substringAfter(SEPARATOR) + val source = MPSModelReference.parseSModelReference(serializedSource) + val target = MPSModelReference.parseSModelReference(serializedTarget) + + return MPSModelImportReference(target, source) + } + } + + override fun serialize(): String { + return "$PREFIX:${importedModel.toMPSString()}$SEPARATOR${importingModel.toMPSString()}" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelReference.kt index 2f0a38f9b3..b36f650b93 100644 --- a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelReference.kt +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModelReference.kt @@ -1,14 +1,15 @@ package org.modelix.mps.multiplatform.model import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter import org.modelix.model.randomUUID data class MPSModelReference(val moduleReference: MPSModuleReference?, val modelId: String) : INodeReference() { - companion object { + companion object : NodeReferenceConverter { const val PREFIX = "mps-model" - fun tryConvert(ref: INodeReference): MPSModelReference? { + override fun tryConvert(ref: INodeReference): MPSModelReference? { if (ref is MPSModelReference) return ref val serialized = ref.serialize() val serializedMPSRef = when { diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleDependencyReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleDependencyReference.kt new file mode 100644 index 0000000000..d73142e8c0 --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleDependencyReference.kt @@ -0,0 +1,35 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSModuleDependencyReference( + val usedModuleId: MPSModuleReference, + val userModuleReference: MPSModuleReference, +) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-module-dep" + const val PREFIX_COLON = "$PREFIX:" + const val SEPARATOR = "#IN#" + + override fun tryConvert(ref: INodeReference): MPSModuleDependencyReference? { + if (ref is MPSModuleDependencyReference) return ref + + val serialized = ref.serialize() + if (!serialized.startsWith(PREFIX_COLON)) return null + + val serializedTargetId = serialized.substringAfter(PREFIX_COLON).substringBefore(SEPARATOR) + val serializedSourceId = serialized.substringAfter(SEPARATOR) + + return MPSModuleDependencyReference( + MPSModuleReference.parseSModuleReference(serializedTargetId), + MPSModuleReference.parseSModuleReference(serializedSourceId), + ) + } + } + + override fun serialize(): String { + return "$PREFIX:${usedModuleId.toMPSString()}$SEPARATOR${userModuleReference.toMPSString()}" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleReference.kt index 2e713a3834..39b8e6077b 100644 --- a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleReference.kt +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSModuleReference.kt @@ -1,15 +1,16 @@ package org.modelix.mps.multiplatform.model import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter import org.modelix.model.randomUUID data class MPSModuleReference(val moduleId: String) : INodeReference() { - companion object { + companion object : NodeReferenceConverter { const val PREFIX = "mps-module" - internal const val PREFIX_COLON = "$PREFIX:" + const val PREFIX_COLON = "$PREFIX:" - fun tryConvert(ref: INodeReference): MPSModuleReference? { + override fun tryConvert(ref: INodeReference): MPSModuleReference? { if (ref is MPSModuleReference) return ref val serialized = ref.serialize() if (!serialized.startsWith(PREFIX_COLON)) return null @@ -17,10 +18,6 @@ data class MPSModuleReference(val moduleId: String) : INodeReference() { return parseSModuleReference(withoutPrefix) } - fun convert(ref: INodeReference) = requireNotNull(tryConvert(ref)) { - "Not a module reference: $ref" - } - fun parseSModuleReference(serialized: String): MPSModuleReference { val moduleId = if (serialized.contains('(') && serialized.contains(')')) { serialized.substringBefore('(') diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSNodeReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSNodeReference.kt index 682d8cccd8..eac6ecbbc0 100644 --- a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSNodeReference.kt +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSNodeReference.kt @@ -1,13 +1,14 @@ package org.modelix.mps.multiplatform.model import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter data class MPSNodeReference(val modelReference: MPSModelReference?, val nodeId: String) : INodeReference() { - companion object { + companion object : NodeReferenceConverter { const val PREFIX = "mps" - fun tryConvert(ref: INodeReference): MPSNodeReference? { + override fun tryConvert(ref: INodeReference): MPSNodeReference? { if (ref is MPSNodeReference) return ref val serialized = ref.serialize() val serializedMPSRef = when { diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSProjectModuleReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSProjectModuleReference.kt new file mode 100644 index 0000000000..f7edbefe6e --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSProjectModuleReference.kt @@ -0,0 +1,32 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSProjectModuleReference(val moduleRef: MPSModuleReference, val projectRef: MPSProjectReference) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-project-module" + const val PREFIX_COLON = "$PREFIX:" + const val SEPARATOR = "#IN#" + + override fun tryConvert(ref: INodeReference): MPSProjectModuleReference? { + if (ref is MPSProjectModuleReference) return ref + val serialized = ref.serialize() + if (!serialized.startsWith(PREFIX_COLON)) return null + val moduleRef = serialized + .substringAfter(PREFIX_COLON) + .substringBefore(SEPARATOR) + .let { MPSModuleReference.parseSModuleReference(it) } + val projectRef = NodeReference(serialized.substringAfter(SEPARATOR)) + .let { MPSProjectReference.tryConvert(it) } + .let { requireNotNull(it) { "Invalid project reference: $it" } } + return MPSProjectModuleReference(moduleRef, projectRef) + } + } + + override fun serialize(): String { + return "$PREFIX:${moduleRef.toMPSString()}$SEPARATOR${projectRef.serialize()}" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSProjectReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSProjectReference.kt new file mode 100644 index 0000000000..e5e4c36dcd --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSProjectReference.kt @@ -0,0 +1,26 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSProjectReference(val projectName: String) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-project" + const val PREFIX_COLON = "$PREFIX:" + + override fun tryConvert(ref: INodeReference): MPSProjectReference? { + if (ref is MPSProjectReference) return ref + val serialized = ref.serialize() + return if (serialized.startsWith(PREFIX_COLON)) { + MPSProjectReference(serialized.substringAfter(PREFIX_COLON)) + } else { + null + } + } + } + + override fun serialize(): String { + return "$PREFIX:$projectName" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSRepositoryReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSRepositoryReference.kt new file mode 100644 index 0000000000..f54fb05c31 --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSRepositoryReference.kt @@ -0,0 +1,11 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference + +object MPSRepositoryReference : INodeReference() { + const val PREFIX = "mps-repository" + + override fun serialize(): String { + return "$PREFIX:repository" + } +} diff --git a/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSSingleLanguageDependencyReference.kt b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSSingleLanguageDependencyReference.kt new file mode 100644 index 0000000000..6ae562b7fe --- /dev/null +++ b/mps-multiplatform-lib/src/commonMain/kotlin/org/modelix/mps/multiplatform/model/MPSSingleLanguageDependencyReference.kt @@ -0,0 +1,40 @@ +package org.modelix.mps.multiplatform.model + +import org.modelix.model.api.INodeReference +import org.modelix.model.api.NodeReference +import org.modelix.model.api.NodeReferenceConverter + +data class MPSSingleLanguageDependencyReference( + val usedModuleId: MPSModuleReference, + val userModule: MPSModuleReference? = null, + val userModel: MPSModelReference? = null, +) : INodeReference() { + + companion object : NodeReferenceConverter { + const val PREFIX = "mps-lang" + const val PREFIX_COLON = "$PREFIX:" + const val SEPARATOR = "#IN#" + + override fun tryConvert(ref: INodeReference): MPSSingleLanguageDependencyReference? { + if (ref is MPSSingleLanguageDependencyReference) return ref + + val serialized = ref.serialize() + val target = serialized.substringAfter(PREFIX_COLON).substringBefore(SEPARATOR) + val source = serialized.substringAfter(SEPARATOR).let { NodeReference(it) } + + return MPSSingleLanguageDependencyReference( + MPSModuleReference.parseSModuleReference(target), + MPSModuleReference.tryConvert(source), + MPSModelReference.tryConvert(source), + ) + } + } + + override fun serialize(): String { + val importer = userModule?.serialize() + ?: userModel?.serialize() + ?: error("importer not found") + + return "$PREFIX:${usedModuleId.toMPSString()}$SEPARATOR$importer" + } +} diff --git a/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/BindingWorker.kt b/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/BindingWorker.kt index af171dc82f..9965c134e8 100644 --- a/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/BindingWorker.kt +++ b/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/BindingWorker.kt @@ -38,7 +38,6 @@ import org.modelix.model.lazy.BranchReference import org.modelix.model.lazy.CLVersion import org.modelix.model.mpsadapters.MPSProjectAdapter import org.modelix.model.mpsadapters.MPSProjectAsNode -import org.modelix.model.mpsadapters.MPSProjectReference import org.modelix.model.mpsadapters.MPSRepositoryAsNode import org.modelix.model.mpsadapters.computeRead import org.modelix.model.mutable.DummyIdGenerator @@ -51,6 +50,7 @@ import org.modelix.model.sync.bulk.IdentityPreservingNodeAssociation import org.modelix.model.sync.bulk.ModelSynchronizer import org.modelix.model.sync.bulk.NodeAssociationToModelServer import org.modelix.mps.model.sync.bulk.MPSProjectSyncMask +import org.modelix.mps.multiplatform.model.MPSProjectReference import org.modelix.streams.iterateSuspending import java.util.concurrent.atomic.AtomicBoolean diff --git a/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/SyncTargetModel.kt b/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/SyncTargetModel.kt index e478952e20..eb9713f398 100644 --- a/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/SyncTargetModel.kt +++ b/mps-sync-plugin3/src/main/kotlin/org/modelix/mps/sync3/SyncTargetModel.kt @@ -16,10 +16,9 @@ import org.modelix.model.api.NullChildLinkReference import org.modelix.model.api.remove import org.modelix.model.api.syncNewChild import org.modelix.model.api.syncNewChildren -import org.modelix.model.mpsadapters.MPSProjectModuleReference -import org.modelix.model.mpsadapters.MPSProjectReference -import org.modelix.model.mpsadapters.toModelix import org.modelix.mps.multiplatform.model.MPSModuleReference +import org.modelix.mps.multiplatform.model.MPSProjectModuleReference +import org.modelix.mps.multiplatform.model.MPSProjectReference class SyncTargetModel(val models: List) : IMutableModel { private val repositoryNode: RepositoryWrapper = RepositoryWrapper() @@ -339,7 +338,7 @@ class SyncTargetModel(val models: List) : IMutableModel { role.matches(BuiltinLanguages.MPSRepositoryConcepts.Project.projectModules.toReference()) -> { return delegates().first().syncNewChildren(role, index, specs).map { val id = MPSProjectModuleReference.convert(it.getNodeReference()) - it.setReferenceTargetRef(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference(), id.moduleRef.toModelix()) + it.setReferenceTargetRef(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference(), id.moduleRef) ProjectModuleWrapper(this, id) } } @@ -351,14 +350,14 @@ class SyncTargetModel(val models: List) : IMutableModel { inner class ProjectModuleWrapper(val project: ProjectWrapper, val id: MPSProjectModuleReference) : WrapperBase() { override fun delegates(): Sequence = project.delegates() .flatMap { it.getChildren(BuiltinLanguages.MPSRepositoryConcepts.Project.projectModules.toReference()) } - .filter { it.getReferenceTargetRef(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference()) == id.moduleRef.toModelix() } + .filter { it.getReferenceTargetRef(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference()) == id.moduleRef } fun getOrCreateDelegate(): IWritableNode { return delegates().firstOrNull() ?: project.delegates().first().syncNewChild(getContainmentLink(), -1, NewNodeSpec(this)) } - private fun targetModule() = tryResolveNode(id.moduleRef.toModelix()) + private fun targetModule() = tryResolveNode(id.moduleRef) override fun getModel(): IMutableModel { return this@SyncTargetModel @@ -381,7 +380,7 @@ class SyncTargetModel(val models: List) : IMutableModel { override fun getReferenceTargetRef(role: IReferenceLinkReference): INodeReference? { if (role.matches(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference())) { - return id.moduleRef.toModelix() + return id.moduleRef } return null } @@ -391,7 +390,7 @@ class SyncTargetModel(val models: List) : IMutableModel { } override fun getAllReferenceTargetRefs(): List> { - return listOfNotNull(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference() to id.moduleRef.toModelix()) + return listOfNotNull(BuiltinLanguages.MPSRepositoryConcepts.ModuleReference.module.toReference() to id.moduleRef) } override fun getParent(): IWritableNode? { diff --git a/mps-sync-plugin3/src/test/kotlin/org/modelix/mps/sync3/MultipleBindingsTest.kt b/mps-sync-plugin3/src/test/kotlin/org/modelix/mps/sync3/MultipleBindingsTest.kt index 5a62e771e7..ef46e41baf 100644 --- a/mps-sync-plugin3/src/test/kotlin/org/modelix/mps/sync3/MultipleBindingsTest.kt +++ b/mps-sync-plugin3/src/test/kotlin/org/modelix/mps/sync3/MultipleBindingsTest.kt @@ -7,12 +7,12 @@ import org.modelix.model.client2.ModelClientV2 import org.modelix.model.client2.runWriteOnModel import org.modelix.model.client2.runWriteOnTree import org.modelix.model.lazy.RepositoryId -import org.modelix.model.mpsadapters.MPSProjectModuleReference import org.modelix.model.mpsadapters.MPSProjectReference import org.modelix.model.mutable.getRootNode import org.modelix.model.mutable.setProperty import org.modelix.mps.multiplatform.model.MPSIdGenerator import org.modelix.mps.multiplatform.model.MPSModuleReference +import org.modelix.mps.multiplatform.model.MPSProjectModuleReference import kotlin.io.path.writeText class MultipleBindingsTest : ProjectSyncTestBase() {