Skip to content

Commit 23d47d3

Browse files
authored
Merge pull request #292 from modelix/feature/mps-tree-adapters
MODELIX-569 Implement model-adapters for tree elements
2 parents 67fd660 + 63aaa48 commit 23d47d3

19 files changed

+1592
-253
lines changed

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

Lines changed: 302 additions & 6 deletions
Large diffs are not rendered by default.

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,7 @@ open class SimpleLanguage(private val name: String, private val uid: String? = n
4444
}
4545

4646
override fun getName() = name
47+
48+
// just a dummy field to make sure that the (lazy) concepts are registered automatically
49+
protected open var includedConcepts: Array<SimpleConcept> = arrayOf()
4750
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (c) 2023.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.modelix.model.mpsadapters
18+
19+
import org.modelix.model.api.IChildLink
20+
import org.modelix.model.api.IConcept
21+
import org.modelix.model.api.IConceptReference
22+
import org.modelix.model.api.IDeprecatedNodeDefaults
23+
import org.modelix.model.api.INode
24+
import org.modelix.model.api.INodeReference
25+
import org.modelix.model.api.IProperty
26+
import org.modelix.model.api.IReferenceLink
27+
28+
interface IDefaultNodeAdapter : IDeprecatedNodeDefaults {
29+
override val allChildren: Iterable<INode>
30+
get() = emptyList()
31+
32+
override val isValid: Boolean
33+
get() = true
34+
35+
override fun getConceptReference(): IConceptReference? {
36+
return concept?.getReference()
37+
}
38+
39+
override fun getPropertyLinks(): List<IProperty> {
40+
return concept?.getAllProperties() ?: emptyList()
41+
}
42+
43+
override fun getReferenceLinks(): List<IReferenceLink> {
44+
return concept?.getAllReferenceLinks() ?: emptyList()
45+
}
46+
47+
override fun getChildren(link: IChildLink): Iterable<INode> {
48+
throw UnsupportedOperationException("Concept $concept does not have children.")
49+
}
50+
51+
override fun moveChild(role: IChildLink, index: Int, child: INode) {
52+
throw UnsupportedOperationException("Children in role $role of concept $concept cannot be moved.")
53+
}
54+
55+
override fun addNewChild(role: IChildLink, index: Int, concept: IConcept?): INode {
56+
throw UnsupportedOperationException("Children cannot be added to role $role in concept ${this.concept}.")
57+
}
58+
59+
override fun addNewChild(role: IChildLink, index: Int, concept: IConceptReference?): INode {
60+
throw UnsupportedOperationException("Children cannot be added to role $role in concept ${this.concept}.")
61+
}
62+
63+
override fun getReferenceTarget(link: IReferenceLink): INode? {
64+
return null
65+
}
66+
67+
override fun setReferenceTarget(link: IReferenceLink, target: INode?) {
68+
require(target == null) { "$concept doesn't contain a reference link $link" }
69+
}
70+
71+
override fun setReferenceTarget(role: IReferenceLink, target: INodeReference?) {
72+
require(target == null) { "$concept doesn't contain a reference link $role" }
73+
}
74+
75+
override fun getReferenceTargetRef(role: IReferenceLink): INodeReference? {
76+
return null
77+
}
78+
79+
override fun getPropertyValue(property: IProperty): String? {
80+
return null
81+
}
82+
83+
override fun setPropertyValue(property: IProperty, value: String?) {
84+
if (getPropertyValue(property) != value) {
85+
throw UnsupportedOperationException("Concept $concept does not contain property $property or it is read-only.")
86+
}
87+
}
88+
89+
override fun removeChild(child: INode) {
90+
val link = child.getContainmentLink() ?: error("ContainmentLink not found for node $child")
91+
throw UnsupportedOperationException("Cannot remove child in link $link of concept $concept.")
92+
}
93+
}

mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSArea.kt

Lines changed: 218 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,37 @@
1313
*/
1414
package org.modelix.model.mpsadapters
1515

16+
import jetbrains.mps.project.ProjectBase
17+
import jetbrains.mps.project.ProjectManager
18+
import jetbrains.mps.project.facets.JavaModuleFacet
19+
import jetbrains.mps.project.structure.modules.ModuleReference
1620
import jetbrains.mps.smodel.GlobalModelAccess
21+
import jetbrains.mps.smodel.SNodePointer
1722
import org.jetbrains.mps.openapi.module.SRepository
23+
import org.jetbrains.mps.openapi.persistence.PersistenceFacade
1824
import org.modelix.model.api.IBranch
1925
import org.modelix.model.api.IConcept
2026
import org.modelix.model.api.IConceptReference
2127
import org.modelix.model.api.INode
2228
import org.modelix.model.api.INodeReference
29+
import org.modelix.model.api.NodeReference
2330
import org.modelix.model.area.IArea
2431
import org.modelix.model.area.IAreaListener
2532
import org.modelix.model.area.IAreaReference
2633

2734
data class MPSArea(val repository: SRepository) : IArea, IAreaReference {
35+
36+
private fun resolveMPSModelReference(ref: INodeReference): INode {
37+
if (ref is MPSModelReference) {
38+
return ref.modelReference.resolve(repository).let { MPSModelAsNode(it) }
39+
}
40+
41+
val serialized = ref.serialize().substringAfter("${MPSModelReference.PREFIX}:")
42+
val modelRef = PersistenceFacade.getInstance().createModelReference(serialized)
43+
44+
return MPSModelAsNode(modelRef.resolve(repository))
45+
}
46+
2847
override fun getRoot(): INode {
2948
return MPSRepositoryAsNode(repository)
3049
}
@@ -35,7 +54,22 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference {
3554
}
3655

3756
override fun resolveNode(ref: INodeReference): INode? {
38-
return MPSNodeReference.tryConvert(ref)?.ref?.resolve(repository)?.let { MPSNode(it) }
57+
val serialized = ref.serialize()
58+
val prefix = serialized.substringBefore(":")
59+
return when (prefix) {
60+
MPSModuleReference.PREFIX -> resolveMPSModuleReference(ref)
61+
MPSModelReference.PREFIX -> resolveMPSModelReference(ref)
62+
MPSNodeReference.PREFIX, "mps-node" -> resolveMPSNodeReference(ref) // mps-node for backwards compatibility
63+
MPSDevKitDependencyReference.PREFIX -> resolveMPSDevKitDependencyReference(ref)
64+
MPSJavaModuleFacetReference.PREFIX -> resolveMPSJavaModuleFacetReference(ref)
65+
MPSModelImportReference.PREFIX -> resolveMPSModelImportReference(ref)
66+
MPSModuleDependencyReference.PREFIX -> resolveMPSModuleDependencyReference(ref)
67+
MPSProjectReference.PREFIX -> resolveMPSProjectReference(ref)
68+
MPSProjectModuleReference.PREFIX -> resolveMPSProjectModuleReference(ref)
69+
MPSSingleLanguageDependencyReference.PREFIX -> resolveMPSSingleLanguageDependencyReference(ref)
70+
MPSRepositoryReference.PREFIX -> resolveMPSRepositoryReference()
71+
else -> null
72+
}
3973
}
4074

4175
override fun resolveOriginalNode(ref: INodeReference): INode? {
@@ -91,4 +125,187 @@ data class MPSArea(val repository: SRepository) : IArea, IAreaReference {
91125
override fun removeListener(l: IAreaListener) {
92126
throw UnsupportedOperationException("Not implemented")
93127
}
128+
129+
private fun resolveMPSModuleReference(ref: INodeReference): MPSModuleAsNode? {
130+
val moduleRef = if (ref is MPSModuleReference) {
131+
ref.moduleReference
132+
} else {
133+
val serializedRef = ref.serialize().substringAfter("${MPSModuleReference.PREFIX}:")
134+
ModuleReference.parseReference(serializedRef)
135+
}
136+
137+
return moduleRef.resolve(repository)?.let { MPSModuleAsNode(it) }
138+
}
139+
140+
private fun resolveMPSNodeReference(ref: INodeReference): MPSNode? {
141+
val sNodeReference = if (ref is MPSNodeReference) {
142+
ref.ref
143+
} else {
144+
val serialized = ref.serialize()
145+
val serializedMPSRef = when {
146+
serialized.startsWith("mps-node:") -> serialized.substringAfter("mps-node:")
147+
serialized.startsWith("mps:") -> serialized.substringAfter("mps:")
148+
else -> return null
149+
}
150+
SNodePointer.deserialize(serializedMPSRef)
151+
}
152+
153+
return sNodeReference.resolve(repository)?.let { MPSNode(it) }
154+
}
155+
156+
private fun resolveMPSDevKitDependencyReference(ref: INodeReference): MPSDevKitDependencyAsNode? {
157+
if (ref is MPSDevKitDependencyReference) {
158+
return when {
159+
ref.userModule != null -> ref.userModule.resolve(repository)
160+
?.let { MPSModuleAsNode(it).findDevKitDependency(ref.usedModuleId) }
161+
ref.userModel != null -> ref.userModel.resolve(repository)
162+
?.let { MPSModelAsNode(it).findDevKitDependency(ref.usedModuleId) }
163+
else -> error("No importer found.")
164+
}
165+
}
166+
val serialized = ref.serialize()
167+
val serializedModuleId = serialized.substringAfter("${MPSDevKitDependencyReference.PREFIX}:")
168+
.substringBefore(MPSDevKitDependencyReference.SEPARATOR)
169+
170+
val importer = serialized.substringAfter(MPSDevKitDependencyReference.SEPARATOR)
171+
val foundImporter = resolveNode(NodeReference(importer))
172+
173+
val moduleId = PersistenceFacade.getInstance().createModuleId(serializedModuleId)
174+
175+
return when (foundImporter) {
176+
is MPSModelAsNode -> foundImporter.findDevKitDependency(moduleId)
177+
is MPSModuleAsNode -> foundImporter.findDevKitDependency(moduleId)
178+
else -> null
179+
}
180+
}
181+
182+
private fun resolveMPSJavaModuleFacetReference(ref: INodeReference): MPSJavaModuleFacetAsNode? {
183+
val moduleRef = if (ref is MPSJavaModuleFacetReference) {
184+
ref.moduleReference
185+
} else {
186+
val serialized = ref.serialize()
187+
val serializedModuleRef = serialized.substringAfter("${MPSJavaModuleFacetReference.PREFIX}:")
188+
ModuleReference.parseReference(serializedModuleRef)
189+
}
190+
191+
val facet = moduleRef.resolve(repository)?.getFacetOfType(JavaModuleFacet.FACET_TYPE)
192+
return facet?.let { MPSJavaModuleFacetAsNode(it as JavaModuleFacet) }
193+
}
194+
195+
private fun resolveMPSModelImportReference(ref: INodeReference): MPSModelImportAsNode? {
196+
val serialized = ref.serialize()
197+
val importedModelRef = if (ref is MPSModelImportReference) {
198+
ref.importedModel
199+
} else {
200+
val serializedModelRef = serialized
201+
.substringAfter("${MPSModelImportReference.PREFIX}:")
202+
.substringBefore(MPSModelImportReference.SEPARATOR)
203+
PersistenceFacade.getInstance().createModelReference(serializedModelRef)
204+
}
205+
206+
val importingModelRef = if (ref is MPSModelImportReference) {
207+
ref.importingModel
208+
} else {
209+
val serializedModelRef = serialized.substringAfter(MPSModelImportReference.SEPARATOR)
210+
PersistenceFacade.getInstance().createModelReference(serializedModelRef)
211+
}
212+
213+
val importedModel = importedModelRef.resolve(repository) ?: return null
214+
val importingModel = importingModelRef.resolve(repository) ?: return null
215+
216+
return MPSModelImportAsNode(importedModel = importedModel, importingModel = importingModel)
217+
}
218+
219+
private fun resolveMPSModuleDependencyReference(ref: INodeReference): MPSModuleDependencyAsNode? {
220+
val serialized = ref.serialize()
221+
val usedModuleId = if (ref is MPSModuleDependencyReference) {
222+
ref.usedModuleId
223+
} else {
224+
val serializedModuleId = serialized
225+
.substringAfter("${MPSModuleDependencyReference.PREFIX}:")
226+
.substringBefore(MPSModuleDependencyReference.SEPARATOR)
227+
PersistenceFacade.getInstance().createModuleId(serializedModuleId)
228+
}
229+
230+
val userModuleReference = if (ref is MPSModuleDependencyReference) {
231+
ref.userModuleReference
232+
} else {
233+
val serializedModuleRef = serialized.substringAfter(MPSModuleDependencyReference.SEPARATOR)
234+
ModuleReference.parseReference(serializedModuleRef)
235+
}
236+
237+
return userModuleReference.resolve(repository)
238+
?.let { MPSModuleAsNode(it) }
239+
?.findModuleDependency(usedModuleId)
240+
}
241+
242+
private fun resolveMPSProjectReference(ref: INodeReference): MPSProjectAsNode? {
243+
val projectName = if (ref is MPSProjectReference) {
244+
ref.projectName
245+
} else {
246+
ref.serialize().substringAfter("${MPSProjectReference.PREFIX}:")
247+
}
248+
249+
val project = ProjectManager.getInstance().openedProjects
250+
.filterIsInstance<ProjectBase>()
251+
.find { it.name == projectName }
252+
253+
return project?.let { MPSProjectAsNode(it) }
254+
}
255+
256+
private fun resolveMPSProjectModuleReference(ref: INodeReference): MPSProjectModuleAsNode? {
257+
val serialized = ref.serialize()
258+
val moduleRef = if (ref is MPSProjectModuleReference) {
259+
ref.moduleRef
260+
} else {
261+
val serializedModuleRef = serialized
262+
.substringAfter("${MPSProjectModuleReference.PREFIX}:")
263+
.substringBefore(MPSProjectModuleReference.SEPARATOR)
264+
ModuleReference.parseReference(serializedModuleRef)
265+
}
266+
267+
val projectRef = if (ref is MPSProjectModuleReference) {
268+
ref.projectRef
269+
} else {
270+
val projectRef = serialized.substringAfter(MPSProjectModuleReference.SEPARATOR)
271+
NodeReference(projectRef)
272+
}
273+
274+
return moduleRef.resolve(repository)?.let {
275+
MPSProjectModuleAsNode(
276+
project = (resolveNode(projectRef) as MPSProjectAsNode).project,
277+
module = it,
278+
)
279+
}
280+
}
281+
282+
private fun resolveMPSSingleLanguageDependencyReference(ref: INodeReference): MPSSingleLanguageDependencyAsNode? {
283+
if (ref is MPSSingleLanguageDependencyReference) {
284+
return when {
285+
ref.userModule != null -> ref.userModule.resolve(repository)
286+
?.let { MPSModuleAsNode(it).findSingleLanguageDependency(ref.usedModuleId) }
287+
ref.userModel != null -> ref.userModel.resolve(repository)
288+
?.let { MPSModelAsNode(it).findSingleLanguageDependency(ref.usedModuleId) }
289+
else -> error("No importer found.")
290+
}
291+
}
292+
val serialized = ref.serialize()
293+
val serializedModuleId = serialized.substringAfter("${MPSSingleLanguageDependencyReference.PREFIX}:")
294+
.substringBefore(MPSSingleLanguageDependencyReference.SEPARATOR)
295+
296+
val importer = serialized.substringAfter(MPSSingleLanguageDependencyReference.SEPARATOR)
297+
val foundImporter = resolveNode(NodeReference(importer))
298+
299+
val moduleId = PersistenceFacade.getInstance().createModuleId(serializedModuleId)
300+
301+
return when (foundImporter) {
302+
is MPSModelAsNode -> foundImporter.findSingleLanguageDependency(moduleId)
303+
is MPSModuleAsNode -> foundImporter.findSingleLanguageDependency(moduleId)
304+
else -> null
305+
}
306+
}
307+
308+
private fun resolveMPSRepositoryReference(): MPSRepositoryAsNode {
309+
return MPSRepositoryAsNode(repository)
310+
}
94311
}

mps-model-adapters/src/main/kotlin/org/modelix/model/mpsadapters/MPSConcept.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ data class MPSConcept(val concept: SAbstractConceptAdapter) : IConcept {
4040
val id: SConceptId = when (concept) {
4141
is SConceptAdapterById -> concept.id
4242
is SInterfaceConceptAdapterById -> concept.id
43-
else -> throw RuntimeException("Unknown concept type: $concept")
43+
else -> error("Unknown concept type: $concept")
4444
}
4545
return "mps:" + id.serialize()
4646
}

0 commit comments

Comments
 (0)