@@ -19,9 +19,18 @@ package org.modelix.mps.model.sync.bulk
19
19
import com.intellij.openapi.application.ApplicationManager
20
20
import com.intellij.openapi.project.ProjectManager
21
21
import jetbrains.mps.ide.project.ProjectHelper
22
+ import jetbrains.mps.smodel.SNodeUtil
23
+ import jetbrains.mps.smodel.adapter.ids.MetaIdHelper
24
+ import jetbrains.mps.smodel.adapter.ids.SConceptId
25
+ import jetbrains.mps.smodel.adapter.structure.concept.SConceptAdapterById
26
+ import jetbrains.mps.smodel.language.ConceptRegistry
27
+ import jetbrains.mps.smodel.language.StructureRegistry
28
+ import jetbrains.mps.smodel.runtime.ConceptDescriptor
29
+ import jetbrains.mps.smodel.runtime.illegal.IllegalConceptDescriptor
22
30
import kotlinx.serialization.ExperimentalSerializationApi
23
31
import kotlinx.serialization.json.Json
24
32
import kotlinx.serialization.json.decodeFromStream
33
+ import org.jetbrains.mps.openapi.model.EditableSModel
25
34
import org.jetbrains.mps.openapi.module.SModule
26
35
import org.jetbrains.mps.openapi.module.SRepository
27
36
import org.modelix.model.data.ModelData
@@ -125,12 +134,60 @@ object MPSBulkSynchronizer {
125
134
ApplicationManager .getApplication().invokeAndWait {
126
135
println (" Persisting changes..." )
127
136
repository.modelAccess.runWriteAction {
137
+ enableWorkaroundForFilePerRootPersistence(repository)
128
138
repository.saveAll()
129
139
}
130
140
println (" Changes persisted." )
131
141
}
132
142
}
133
143
144
+ /* *
145
+ * Workaround for MPS not being able to read the name property of the node during the save process
146
+ * in case FilePerRootPersistence is used.
147
+ * This is because the concept is not properly loaded and in the MPS code it checks if the concept is a subconcept
148
+ * of INamedConcept.
149
+ * Without this workaround the id of the root node will be used instead of the name, resulting in renamed files.
150
+ */
151
+ @JvmStatic
152
+ private fun enableWorkaroundForFilePerRootPersistence (repository : SRepository ) {
153
+ val structureRegistry: StructureRegistry = ConceptRegistry .getInstance().readField(" myStructureRegistry" )
154
+ val myConceptDescriptorsById: MutableMap <SConceptId , ConceptDescriptor > = structureRegistry.readField(" myConceptDescriptorsById" )
155
+
156
+ repository.modules
157
+ .asSequence()
158
+ .flatMap { it.models }
159
+ .mapNotNull { it as ? EditableSModel }
160
+ .filter { it.isChanged }
161
+ .flatMap { it.rootNodes }
162
+ .mapNotNull { (it.concept as ? SConceptAdapterById ) }
163
+ .forEach {
164
+ myConceptDescriptorsById.putIfAbsent(it.id, DummyNamedConceptDescriptor (it))
165
+ }
166
+ }
167
+
168
+ @Suppress(" UNCHECKED_CAST" )
169
+ private fun <R > Any.readField (name : String ): R {
170
+ return this ::class .java.getDeclaredField(name).also { it.isAccessible = true }.get(this ) as R
171
+ }
172
+
173
+ private class DummyNamedConceptDescriptor (concept : SConceptAdapterById ) : ConceptDescriptor by IllegalConceptDescriptor(concept.id, concept.qualifiedName) {
174
+ override fun isAssignableTo (other : SConceptId ? ): Boolean {
175
+ return MetaIdHelper .getConcept(SNodeUtil .concept_INamedConcept) == other
176
+ }
177
+
178
+ override fun getSuperConceptId (): SConceptId {
179
+ return MetaIdHelper .getConcept(SNodeUtil .concept_BaseConcept)
180
+ }
181
+
182
+ override fun getAncestorsIds (): MutableSet <SConceptId > {
183
+ return mutableSetOf (MetaIdHelper .getConcept(SNodeUtil .concept_INamedConcept))
184
+ }
185
+
186
+ override fun getParentsIds (): MutableList <SConceptId > {
187
+ return mutableListOf (MetaIdHelper .getConcept(SNodeUtil .concept_INamedConcept))
188
+ }
189
+ }
190
+
134
191
@JvmStatic
135
192
private fun parseRawPropertySet (rawProperty : String ): Set <String > {
136
193
return if (rawProperty.isEmpty()) {
0 commit comments