-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathMPSGenericNodeAdapter.kt
165 lines (132 loc) · 7.57 KB
/
MPSGenericNodeAdapter.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package org.modelix.model.mpsadapters
import jetbrains.mps.smodel.MPSModuleRepository
import org.jetbrains.mps.openapi.language.SConcept
import org.jetbrains.mps.openapi.module.SRepository
import org.modelix.model.api.ConceptReference
import org.modelix.model.api.IChildLinkReference
import org.modelix.model.api.IMutableModel
import org.modelix.model.api.INodeReference
import org.modelix.model.api.IPropertyReference
import org.modelix.model.api.IReadableNode
import org.modelix.model.api.IReferenceLinkReference
import org.modelix.model.api.ISyncTargetNode
import org.modelix.model.api.IWritableNode
import org.modelix.model.api.NewNodeSpec
import org.modelix.model.api.upcast
abstract class MPSGenericNodeAdapter<E> : IWritableNode, ISyncTargetNode {
protected abstract fun getElement(): E
abstract fun getRepository(): SRepository?
protected abstract fun getPropertyAccessors(): List<Pair<IPropertyReference, IPropertyAccessor<E>>>
protected abstract fun getReferenceAccessors(): List<Pair<IReferenceLinkReference, IReferenceAccessor<E>>>
protected abstract fun getChildAccessors(): List<Pair<IChildLinkReference, IChildAccessor<E>>>
protected fun tryGetPropertyAccessor(role: IPropertyReference): IPropertyAccessor<E>? = getPropertyAccessors().find { it.first.matches(role) }?.second
protected fun tryGetReferenceAccessor(role: IReferenceLinkReference): IReferenceAccessor<E>? = getReferenceAccessors().find { it.first.matches(role) }?.second
protected fun tryGetChildAccessor(role: IChildLinkReference): IChildAccessor<E>? = getChildAccessors().find { it.first.matches(role) }?.second
protected fun getPropertyAccessor(role: IPropertyReference) = requireNotNull(tryGetPropertyAccessor(role)) { "Unknown property [role = $role, node = $this]" }
protected fun getReferenceAccessor(role: IReferenceLinkReference) = requireNotNull(tryGetReferenceAccessor(role)) { "Unknown reference link [role = $role, node = $this]" }
protected fun getChildAccessor(role: IChildLinkReference) = requireNotNull(tryGetChildAccessor(role)) { "Unknown child link [role = $role, node = $this]" }
override fun getModel(): IMutableModel {
return MPSArea(getRepository() ?: MPSModuleRepository.getInstance()).asModel()
}
override fun getAllChildren(): List<IWritableNode> {
return getChildAccessors().flatMap { it.second.read(getElement()) }
}
override fun getChildren(role: IChildLinkReference): List<IWritableNode> {
return tryGetChildAccessor(role)?.read(getElement()) ?: emptyList()
}
override fun getReferenceTarget(role: IReferenceLinkReference): IWritableNode? {
return tryGetReferenceAccessor(role)?.read(getElement())
}
override fun getAllReferenceTargets(): List<Pair<IReferenceLinkReference, IWritableNode>> {
return getReferenceAccessors().mapNotNull { it.first to (it.second.read(getElement()) ?: return@mapNotNull null) }
}
override fun changeConcept(newConcept: ConceptReference): IWritableNode {
throw UnsupportedOperationException("Concept is immutable [node = $this, newConcept = $newConcept]")
}
override fun setPropertyValue(property: IPropertyReference, value: String?) {
getPropertyAccessor(property).write(getElement(), value)
}
override fun moveChild(role: IChildLinkReference, index: Int, child: IWritableNode) {
getChildAccessor(role).move(getElement(), index, child)
}
override fun removeChild(child: IWritableNode) {
getChildAccessor(child.getContainmentLink()).remove(getElement(), child)
}
override fun addNewChild(role: IChildLinkReference, index: Int, concept: ConceptReference): IWritableNode {
return addNewChildren(role, index, listOf(concept)).single()
}
override fun addNewChildren(role: IChildLinkReference, index: Int, concepts: List<ConceptReference>): List<IWritableNode> {
return doSyncNewChildren(role, index, concepts.map { it to null })
}
override fun syncNewChildren(role: IChildLinkReference, index: Int, sourceNodes: List<NewNodeSpec>): List<IWritableNode> {
return doSyncNewChildren(role, index, sourceNodes.map { it.conceptRef to it })
}
private fun doSyncNewChildren(role: IChildLinkReference, index: Int, sourceNodes: List<Pair<ConceptReference, NewNodeSpec?>>): List<IWritableNode> {
val accessor = getChildAccessor(role)
val repo = getRepository() ?: MPSModuleRepository.getInstance()
val resolvedConcepts = sourceNodes.map { it.first }.distinct().associateWith { concept ->
repo.resolveConcept(concept)
}
return sourceNodes.map { sourceNode ->
accessor.addNew(getElement(), index, SpecWithResolvedConcept(resolvedConcepts[sourceNode.first]!!, sourceNode.second))
}
}
override fun setReferenceTarget(role: IReferenceLinkReference, target: IWritableNode?) {
getReferenceAccessor(role).write(getElement(), target)
}
override fun setReferenceTargetRef(role: IReferenceLinkReference, target: INodeReference?) {
setReferenceTarget(role, target?.let { checkNotNull(getModel().resolveNode(it)) { "Target not found: $target" } })
}
override fun isValid(): Boolean {
return true
}
override fun getConceptReference(): ConceptReference {
return getConcept().getReference().upcast()
}
override fun getPropertyValue(property: IPropertyReference): String? {
return tryGetPropertyAccessor(property)?.read(getElement())
}
override fun getPropertyLinks(): List<IPropertyReference> {
return getPropertyAccessors().map { it.first }
}
override fun getAllProperties(): List<Pair<IPropertyReference, String>> {
return getPropertyAccessors().mapNotNull { it.first to (it.second.read(getElement()) ?: return@mapNotNull null) }
}
override fun getReferenceTargetRef(role: IReferenceLinkReference): INodeReference? {
return getReferenceTarget(role)?.getNodeReference()
}
override fun getReferenceLinks(): List<IReferenceLinkReference> {
return getReferenceAccessors().map { it.first }
}
override fun getAllReferenceTargetRefs(): List<Pair<IReferenceLinkReference, INodeReference>> {
return getAllReferenceTargets().map { it.first to it.second.getNodeReference() }
}
interface IPropertyAccessor<in E> {
fun read(element: E): String?
fun write(element: E, value: String?): Unit = throw UnsupportedOperationException("$this, $value")
}
interface IReferenceAccessor<in E> {
fun read(element: E): IWritableNode?
fun write(element: E, value: IWritableNode?) {
throw UnsupportedOperationException()
}
fun write(element: E, value: INodeReference?): Unit = throw UnsupportedOperationException()
}
interface IChildAccessor<in E> {
fun read(element: E): List<IWritableNode>
fun addNew(element: E, index: Int, sourceNode: SpecWithResolvedConcept): IWritableNode {
throw UnsupportedOperationException("$this, $element, $sourceNode")
}
fun move(element: E, index: Int, child: IWritableNode) {
throw UnsupportedOperationException("$this, $element, $child")
}
fun remove(element: E, child: IWritableNode): Unit = throw UnsupportedOperationException()
}
class SpecWithResolvedConcept(val concept: SConcept, val spec: NewNodeSpec?) {
fun getNode(): IReadableNode = spec?.node!!
fun getConceptReference(): ConceptReference = MPSConcept(concept).getReference()
override fun toString(): String {
return "SourceNodeAndConcept[$concept, $spec]"
}
}
}