[workspace model] provide implementation of FacetManager which stores data in workspa...
[idea/community.git] / platform / workspaceModel-ide / src / com / intellij / workspace / legacyBridge / intellij / LegacyBridgeModuleManagerComponent.kt
1 package com.intellij.workspace.legacyBridge.intellij
2
3 import com.intellij.ProjectTopics
4 import com.intellij.concurrency.JobSchedulerImpl
5 import com.intellij.configurationStore.ModuleStoreBase
6 import com.intellij.configurationStore.saveComponentManager
7 import com.intellij.openapi.Disposable
8 import com.intellij.openapi.application.ApplicationManager
9 import com.intellij.openapi.application.PathManager
10 import com.intellij.openapi.application.runWriteAction
11 import com.intellij.openapi.components.ProjectComponent
12 import com.intellij.openapi.components.stateStore
13 import com.intellij.openapi.diagnostic.Logger
14 import com.intellij.openapi.diagnostic.logger
15 import com.intellij.openapi.diagnostic.runAndLogException
16 import com.intellij.openapi.module.*
17 import com.intellij.openapi.module.impl.*
18 import com.intellij.openapi.module.impl.UnloadedModuleDescriptionImpl.Companion.createFromPaths
19 import com.intellij.openapi.project.Project
20 import com.intellij.openapi.project.impl.ProjectLifecycleListener
21 import com.intellij.openapi.roots.ModuleRootManager
22 import com.intellij.openapi.roots.ex.ProjectRootManagerEx
23 import com.intellij.openapi.util.Disposer
24 import com.intellij.openapi.util.io.FileUtilRt
25 import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager
26 import com.intellij.util.concurrency.AppExecutorUtil
27 import com.intellij.util.graph.*
28 import com.intellij.workspace.api.*
29 import com.intellij.workspace.bracket
30 import com.intellij.workspace.executeOrQueueOnDispatchThread
31 import com.intellij.workspace.ide.*
32 import com.intellij.workspace.jps.JpsProjectEntitiesLoader
33 import com.intellij.workspace.legacyBridge.facet.FacetEntityChangeListener
34 import org.jetbrains.annotations.ApiStatus
35 import java.io.File
36 import java.util.*
37 import java.util.concurrent.Callable
38 import java.util.concurrent.ConcurrentHashMap
39 import java.util.concurrent.ConcurrentMap
40
41 @Suppress("ComponentNotRegistered")
42 class LegacyBridgeModuleManagerComponent(private val project: Project) : ModuleManagerEx(), ProjectComponent, Disposable {
43
44   val outOfTreeModulesPath: String =
45     FileUtilRt.toSystemIndependentName(File(PathManager.getTempPath(), "outOfTreeProjectModules-${project.locationHash}").path)
46
47   private val LOG = Logger.getInstance(javaClass)
48
49   override fun dispose() {
50     val modules = modulesMap.values.toList()
51     modulesMap.clear()
52
53     for (module in modules) {
54       Disposer.dispose(module)
55     }
56   }
57
58   private val modulesMap: ConcurrentMap<ModuleId, LegacyBridgeModule> = ConcurrentHashMap()
59   private val unloadedModules: MutableMap<String, UnloadedModuleDescriptionImpl> = mutableMapOf()
60   private val newModuleInstances = mutableMapOf<ModuleId, LegacyBridgeModule>()
61
62   @ApiStatus.Internal
63   internal fun setNewModuleInstances(addedInstances: List<LegacyBridgeModule>) {
64     if (newModuleInstances.isNotEmpty()) error("newModuleInstances are not empty")
65     for (instance in addedInstances) {
66       newModuleInstances[instance.moduleEntityId] = instance
67     }
68   }
69
70   private fun getModuleRootComponentByLibrary(entity: LibraryEntity): LegacyBridgeModuleRootComponent {
71     val tableId = entity.tableId as LibraryTableId.ModuleLibraryTableId
72     val module = modulesMap[tableId.moduleId] ?: error("Could not find module for module library: ${entity.persistentId()}")
73     return LegacyBridgeModuleRootComponent.getInstance(module)
74   }
75
76   init {
77     // default project doesn't have modules
78     if (!project.isDefault) {
79       val myMessageBusConnection = project.messageBus.connect(this)
80       myMessageBusConnection.subscribe(ProjectLifecycleListener.TOPIC, object : ProjectLifecycleListener {
81         override fun projectComponentsInitialized(listenedProject: Project) {
82           if (project !== listenedProject) return
83
84           val unloadedNames = UnloadedModulesListStorage.getInstance(project).unloadedModuleNames.toSet()
85
86           val entities = entityStore.current.entities(ModuleEntity::class.java)
87             .filter { !unloadedNames.contains(it.name) }
88             .toList()
89           loadModules(entities)
90         }
91       })
92
93       myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
94         override fun changed(event: EntityStoreChanged) = LOG.bracket("ModuleManagerComponent.EntityStoreChange") {
95           val moduleLibraryChanges = event.getChanges(LibraryEntity::class.java).filterModuleLibraryChanges()
96           val changes = event.getChanges(ModuleEntity::class.java)
97           if (changes.isEmpty() && moduleLibraryChanges.isEmpty()) return@bracket
98
99           executeOrQueueOnDispatchThread {
100             LOG.bracket("ModuleManagerComponent.handleModulesChange") {
101               incModificationCount()
102
103               for (change in moduleLibraryChanges) when (change) {
104                 is EntityChange.Removed -> {
105                   val moduleRootComponent = getModuleRootComponentByLibrary(change.entity)
106                   val persistentId = change.entity.persistentId()
107                   val moduleLibrary = moduleRootComponent.moduleLibraries.firstOrNull { it.entityId == persistentId }
108                                       ?: error("Could not find library '${change.entity.name}' in module ${moduleRootComponent.module.name}")
109                   moduleRootComponent.moduleLibraries.remove(moduleLibrary)
110                   Disposer.dispose(moduleLibrary)
111                 }
112                 is EntityChange.Replaced -> {
113                   val idBefore = change.oldEntity.persistentId()
114                   val idAfter = change.newEntity.persistentId()
115
116                   if (idBefore != idAfter) {
117                     if (idBefore.tableId != idAfter.tableId) {
118                       throw UnsupportedOperationException(
119                         "Changing library table id is not allowed by replace entity operation." +
120                         "old library id: '$idBefore' new library id: '$idAfter'")
121                     }
122
123                     val moduleRootComponent = getModuleRootComponentByLibrary(change.oldEntity)
124                     val moduleLibrary = moduleRootComponent.moduleLibraries.firstOrNull { it.entityId == idBefore }
125                                         ?: error("Could not find library '${idBefore.name}' in module ${moduleRootComponent.module.name}")
126                     moduleLibrary.entityId = idAfter
127                   }
128
129                   Unit
130                 }
131                 is EntityChange.Added -> Unit // Add events are handled after adding new modules
132               }.let {  } // exhaustive when
133
134
135               loadStateOfUnloadedModules(changes)
136               val unloadedModulesSetOriginal = unloadedModules.keys.toList()
137               val unloadedModulesSet = unloadedModulesSetOriginal.toMutableSet()
138               val oldModuleNames = mutableMapOf<Module, String>()
139
140               nextChange@ for (change in changes) when (change) {
141                 is EntityChange.Removed -> {
142                   fireBeforeModuleRemoved(change.entity.persistentId())
143                   removeModuleAndFireEvent(change.entity.persistentId())
144                   unloadedModulesSet.remove(change.entity.name)
145                 }
146
147                 is EntityChange.Added -> {
148                   val moduleId = change.entity.persistentId()
149                   val alreadyCreatedModule = newModuleInstances.remove(moduleId)
150                   val module = if (alreadyCreatedModule != null) {
151                     unloadedModulesSet.remove(change.entity.name)
152
153                     (alreadyCreatedModule as LegacyBridgeModuleImpl).entityStore = entityStore
154                     alreadyCreatedModule.diff = null
155                     addModule(alreadyCreatedModule)
156                     alreadyCreatedModule
157                   } else {
158                     if (change.entity.name in unloadedModules.keys) {
159                       // Skip unloaded modules if it was not added via API
160                       continue@nextChange
161                     }
162
163                     addModule(change.entity)
164                   }
165
166                   if (project.isOpen) {
167                     fireModuleAddedInWriteAction(module)
168                   }
169                 }
170
171                 is EntityChange.Replaced -> {
172                   val oldId = change.oldEntity.persistentId()
173                   val newId = change.newEntity.persistentId()
174
175                   if (oldId != newId) {
176                     unloadedModulesSet.remove(change.newEntity.name)
177                     renameModule(oldId, newId)
178                     oldModuleNames[modulesMap.getValue(newId)] = oldId.name
179                   }
180                 }
181               }
182
183               val modulesToCheck = mutableSetOf<Module>()
184               for (change in moduleLibraryChanges) when (change) {
185                 is EntityChange.Added -> {
186                   val moduleRootComponent = getModuleRootComponentByLibrary(change.entity)
187                   modulesToCheck.add(moduleRootComponent.module)
188
189                   val addedLibraryId = change.entity.persistentId()
190                   val alreadyCreatedLibrary = moduleRootComponent.newModuleLibraries.firstOrNull { it.entityId == addedLibraryId }
191                   val libraryImpl = if (alreadyCreatedLibrary != null) {
192                     moduleRootComponent.newModuleLibraries.remove(alreadyCreatedLibrary)
193                     alreadyCreatedLibrary.entityStore = entityStore
194                     alreadyCreatedLibrary.modifiableModelFactory = null
195                     alreadyCreatedLibrary
196                   }
197                   else {
198                     moduleRootComponent.createModuleLibrary(addedLibraryId)
199                   }
200
201                   moduleRootComponent.moduleLibraries.add(libraryImpl)
202
203                   Unit
204                 }
205                 is EntityChange.Replaced -> Unit
206                 is EntityChange.Removed -> Unit
207               }.let {  } // exhaustive when
208
209               if (oldModuleNames.isNotEmpty()) {
210                 project.messageBus
211                   .syncPublisher(ProjectTopics.MODULES)
212                   .modulesRenamed(project, oldModuleNames.keys.toList()) { module -> oldModuleNames[module] }
213               }
214
215               if (unloadedModulesSetOriginal != unloadedModulesSet) {
216                 setUnloadedModules(unloadedModulesSet.toList())
217               }
218
219               if (unloadedModulesSet.isNotEmpty()) {
220                 val loadedModules = modules.map { it.name }.toMutableList()
221                 loadedModules.removeAll(unloadedModulesSet)
222                 AutomaticModuleUnloader.getInstance(project).setLoadedModules(loadedModules)
223               }
224
225               for (module in modulesToCheck) {
226                 val newModuleLibraries = LegacyBridgeModuleRootComponent.getInstance(module).newModuleLibraries
227                 if (newModuleLibraries.isNotEmpty()) {
228                   LOG.error("Not all module library instances were handled in change event. Leftovers:\n" +
229                             newModuleLibraries.joinToString(separator = "\n"))
230                   newModuleLibraries.clear()
231                 }
232               }
233
234               if (newModuleInstances.isNotEmpty()) {
235                 LOG.error("Not all module instances were handled in change event. Leftovers:\n" +
236                           newModuleInstances.keys.joinToString(separator = "\n"))
237                 newModuleInstances.clear()
238               }
239
240               incModificationCount()
241             }
242           }
243         }
244       })
245       myMessageBusConnection.subscribe(WorkspaceModelTopics.CHANGED, FacetEntityChangeListener(project))
246     }
247   }
248
249   private fun loadStateOfUnloadedModules(changes: List<EntityChange<ModuleEntity>>) {
250     val moduleEntitiesToLoad = changes.filterIsInstance<EntityChange.Added<ModuleEntity>>().map { it.entity }.toMutableList()
251     if (moduleEntitiesToLoad.isEmpty()) return
252     val unloadedModuleNames = UnloadedModulesListStorage.getInstance(project).unloadedModuleNames.toSet()
253
254     val modulePathsToLoad = mutableSetOf<ModulePath>()
255     val iterator = moduleEntitiesToLoad.listIterator()
256     val unloadedModulePaths: MutableList<ModulePath> = mutableListOf()
257     while (iterator.hasNext()) {
258       val element = getModulePath(iterator.next())
259       if (element.moduleName in unloadedModuleNames) {
260         unloadedModulePaths += element
261         iterator.remove()
262         continue
263       }
264       else {
265         modulePathsToLoad += element
266       }
267     }
268
269     val unloaded = createFromPaths(unloadedModulePaths, project).toMutableList()
270
271     if (unloaded.isNotEmpty()) {
272       val changeUnloaded = AutomaticModuleUnloader.getInstance(project).processNewModules(modulePathsToLoad, unloaded)
273       unloaded.addAll(changeUnloaded.toUnloadDescriptions)
274       unloadedModulePaths += changeUnloaded.toUnload
275     }
276
277     unloadedModules.clear()
278     unloaded.associateByTo(unloadedModules) { it.name }
279   }
280
281   internal fun addModule(moduleEntity: ModuleEntity): LegacyBridgeModule {
282     if (modulesMap.containsKey(moduleEntity.persistentId())) {
283       error("Module ${moduleEntity.name} (id:'${moduleEntity.persistentId()}') is already added")
284     }
285
286     val module = createModuleInstance(moduleEntity, entityStore, diff = null, isNew = false)
287     addModule(module)
288     return module
289   }
290
291   internal fun addModule(module: LegacyBridgeModule) {
292     val oldValue = modulesMap.put(module.moduleEntityId, module)
293     if (oldValue != null) {
294       LOG.warn("Duplicate module name: ${module.name}")
295     }
296   }
297
298   internal fun removeModuleAndFireEvent(moduleEntityId: ModuleId) {
299     val moduleImpl = modulesMap.remove(moduleEntityId) ?: error("Module $moduleEntityId does not exist")
300     project.messageBus.syncPublisher(ProjectTopics.MODULES).moduleRemoved(project, moduleImpl)
301     Disposer.dispose(moduleImpl)
302   }
303
304   internal fun fireBeforeModuleRemoved(moduleEntityId: ModuleId) {
305     val moduleImpl = modulesMap[moduleEntityId] ?: error("Module $moduleEntityId does not exist")
306     project.messageBus.syncPublisher(ProjectTopics.MODULES).beforeModuleRemoved(project, moduleImpl)
307   }
308
309   internal fun renameModule(oldId: ModuleId, newId: ModuleId) {
310     val moduleImpl = modulesMap.remove(oldId) ?: error("Module $oldId does not exist")
311
312     val replacedModuleImplById = modulesMap.put(newId, moduleImpl)
313     if (replacedModuleImplById != null) {
314       error("ModuleId $newId already exists")
315     }
316
317     // TODO Set notifyStorage to `true` after fixing module storages
318     moduleImpl.rename(newId.name, true)
319   }
320
321   override fun moduleDependencyComparator(): Comparator<Module> {
322     val builder = DFSTBuilder(moduleGraph(true))
323     return builder.comparator()
324   }
325
326   override fun moduleGraph(): Graph<Module> = moduleGraph(includeTests = true)
327   override fun moduleGraph(includeTests: Boolean): Graph<Module> {
328     return GraphGenerator.generate(CachingSemiGraph.cache(object : InboundSemiGraph<Module> {
329       override fun getNodes(): Collection<Module> = this@LegacyBridgeModuleManagerComponent.modulesMap.values.toMutableList()
330
331       override fun getIn(m: Module): Iterator<Module> {
332         val dependentModules = ModuleRootManager.getInstance(m).getDependencies(includeTests)
333         return dependentModules.toList().iterator()
334       }
335     }))
336   }
337
338   private val entityStore by lazy { WorkspaceModel.getInstance(project).entityStore }
339
340   private fun loadModules(entities: List<ModuleEntity>) {
341     val service = AppExecutorUtil.createBoundedApplicationPoolExecutor("ModuleManager Loader", JobSchedulerImpl.getCPUCoresCount())
342     try {
343       val tasks = entities
344         .map { moduleEntity ->
345           Callable {
346             LOG.runAndLogException {
347               addModule(moduleEntity)
348             }
349           }
350         }
351
352       service.invokeAll(tasks)
353     }
354     finally {
355       service.shutdownNow()
356     }
357   }
358
359   override fun projectOpened() {
360     fireModulesAdded()
361
362     for (module in modulesMap.values) {
363       (module as ModuleEx).projectOpened()
364     }
365   }
366
367   override fun projectClosed() {
368     for (module in modulesMap.values) {
369       (module as ModuleEx).projectClosed()
370     }
371   }
372
373   private fun fireModulesAdded() {
374     for (module in modulesMap.values) {
375       fireModuleAddedInWriteAction(module as ModuleEx)
376     }
377   }
378
379   private fun fireModuleAddedInWriteAction(module: ModuleEx) {
380     ApplicationManager.getApplication().runWriteAction {
381       if (!module.isLoaded) {
382         module.moduleAdded()
383         fireModuleAdded(module)
384       }
385     }
386   }
387
388   private fun fireModuleAdded(module: Module) {
389     project.messageBus.syncPublisher(ProjectTopics.MODULES).moduleAdded(project, module)
390   }
391
392   override fun getModifiableModel(): ModifiableModuleModel =
393     LegacyBridgeModifiableModuleModel(project, this, TypedEntityStorageBuilder.from(entityStore.current))
394
395   fun getModifiableModel(diff: TypedEntityStorageBuilder): ModifiableModuleModel =
396     LegacyBridgeModifiableModuleModel(project, this, diff)
397
398   override fun newModule(filePath: String, moduleTypeId: String): Module {
399     incModificationCount()
400     val modifiableModel = modifiableModel
401     val module = modifiableModel.newModule(filePath, moduleTypeId)
402     modifiableModel.commit()
403     return module
404   }
405
406   override fun getModuleDependentModules(module: Module): MutableList<Module> =
407     ModuleRootManager.getInstance(module).dependencies.toMutableList()
408
409   override fun getUnloadedModuleDescriptions(): Collection<UnloadedModuleDescription> = unloadedModules.values
410
411   override fun getFailedModulePaths(): Collection<ModulePath> = emptyList()
412
413   override fun hasModuleGroups(): Boolean = hasModuleGroups(entityStore)
414
415   override fun isModuleDependent(module: Module, onModule: Module): Boolean =
416     ModuleRootManager.getInstance(module).isDependsOn(onModule)
417
418   override fun getAllModuleDescriptions(): MutableCollection<ModuleDescription> =
419     (modulesMap.values.map { module ->
420       object : ModuleDescription {
421         override fun getName(): String = module.name
422
423         override fun getDependencyModuleNames(): MutableList<String> =
424           this@LegacyBridgeModuleManagerComponent
425             .getModuleDependentModules(module)
426             .map { it.name }
427             .toMutableList()
428       }
429     } + unloadedModuleDescriptions).toMutableList()
430
431   override fun getModuleGroupPath(module: Module): Array<String>? = getModuleGroupPath(module, entityStore)
432
433   override fun getModuleGrouper(model: ModifiableModuleModel?): ModuleGrouper = createGrouper(project, model)
434
435   override fun loadModule(filePath: String): Module {
436     val moduleName = getModuleNameByFilePath(filePath)
437     if (findModuleByName(moduleName) != null) {
438       error("Module name '$moduleName' already exists. Trying to load module: $filePath")
439     }
440
441     val moduleFile = File(filePath)
442
443     WorkspaceModel.getInstance(project).updateProjectModel { builder ->
444       JpsProjectEntitiesLoader.loadModule(moduleFile, project.storagePlace!!, builder)
445     }
446
447     return findModuleByName(moduleName)
448            ?: error("Module '$moduleName' was not found after loading: $filePath")
449   }
450
451   override fun getUnloadedModuleDescription(moduleName: String): UnloadedModuleDescription? {
452     if (moduleName !in unloadedModules) return null
453
454     // TODO Optimize?
455     val moduleEntity = entityStore.current.entities(ModuleEntity::class.java).filter { it.name == moduleName }.firstOrNull()
456                        ?: return null
457     return getUnloadedModuleDescription(moduleEntity)
458   }
459
460   override fun getModules(): Array<Module> = modulesMap.values.toTypedArray()
461
462   private val sortedModulesValue = CachedValueWithParameter<Set<ModuleId>, Array<Module>> { _, _ ->
463     val allModules = modulesMap.values.toTypedArray<Module>()
464     Arrays.sort(allModules, moduleDependencyComparator())
465     return@CachedValueWithParameter allModules
466   }
467
468   override fun getSortedModules(): Array<Module> = entityStore.cachedValue(sortedModulesValue, modulesMap.keys.toSet())
469
470   override fun findModuleByName(name: String): Module? = modulesMap[ModuleId(name)]
471
472   override fun disposeModule(module: Module) = ApplicationManager.getApplication().runWriteAction {
473     val modifiableModel = modifiableModel
474     modifiableModel.disposeModule(module)
475     modifiableModel.commit()
476   }
477
478   override fun setUnloadedModules(unloadedModuleNames: List<String>) {
479     if (unloadedModules.keys == unloadedModuleNames.toSet()) {
480       //optimization
481       return
482     }
483
484     UnloadedModulesListStorage.getInstance(project).unloadedModuleNames = unloadedModuleNames
485
486     if (unloadedModuleNames.isNotEmpty()) {
487       val loadedModules = modules.map { it.name }.toMutableList()
488       loadedModules.removeAll(unloadedModuleNames)
489       AutomaticModuleUnloader.getInstance(project).setLoadedModules(loadedModules)
490     }
491
492     val names = unloadedModuleNames.toSet()
493
494     val modulesToUnload = mutableSetOf<ModuleId>()
495     for (module in modules) {
496       if (module.name in names) {
497         modulesToUnload.add((module as LegacyBridgeModule).moduleEntityId)
498       }
499     }
500
501     val moduleEntities = entityStore.current.entities(ModuleEntity::class.java).toList()
502     val moduleEntitiesToLoad = moduleEntities.filter { findModuleByName(it.name) == null && it.name !in names }
503
504     if (moduleEntitiesToLoad.isEmpty() && modulesToUnload.isEmpty()) return
505
506     unloadedModules.keys.removeAll { it !in names }
507     runWriteAction {
508       ProjectRootManagerEx.getInstanceEx(project).makeRootsChange(
509         {
510           for (moduleId in modulesToUnload) {
511             fireBeforeModuleRemoved(moduleId)
512
513             val module = findModuleByName(moduleId.name)
514             if (module != null) {
515               val description = LoadedModuleDescriptionImpl(module)
516               val modulePath = getModulePath(module, entityStore)
517               val pointerManager = VirtualFilePointerManager.getInstance()
518               val contentRoots = ModuleRootManager.getInstance(module).contentRootUrls.map {
519                 url -> pointerManager.create(url, this, null)
520               }
521               val unloadedModuleDescription = UnloadedModuleDescriptionImpl(modulePath, description.dependencyModuleNames, contentRoots)
522               unloadedModules[moduleId.name] = unloadedModuleDescription
523               // we need to save module configuration before unloading, otherwise its settings will be lost
524               saveComponentManager(module)
525             }
526
527             removeModuleAndFireEvent(moduleId)
528           }
529
530           loadModules(moduleEntitiesToLoad)
531         }, false, true)
532     }
533   }
534
535   override fun removeUnloadedModules(unloadedModules: MutableCollection<out UnloadedModuleDescription>) {
536     ApplicationManager.getApplication().assertWriteAccessAllowed()
537
538     unloadedModules.forEach { this.unloadedModules.remove(it.name) }
539
540     UnloadedModulesListStorage.getInstance(project).unloadedModuleNames = this.unloadedModules.keys.toList()
541   }
542
543   private fun getUnloadedModuleDescription(moduleEntity: ModuleEntity): UnloadedModuleDescriptionImpl {
544     val provider = LegacyBridgeFilePointerProvider.getInstance(project)
545
546     val modulePath = getModulePath(moduleEntity)
547     val dependencyModuleNames = moduleEntity.dependencies.filterIsInstance<ModuleDependencyItem.Exportable.ModuleDependency>().map { it.module.name }
548     val contentRoots = moduleEntity.contentRoots.map { it.url }.map { provider.getAndCacheFilePointer(it) }.toList()
549
550     return UnloadedModuleDescriptionImpl(
551       modulePath = modulePath,
552       dependencyModuleNames = dependencyModuleNames,
553       contentRoots = contentRoots
554     )
555   }
556
557   internal fun getModuleFilePath(moduleEntity: ModuleEntity): String {
558     val jpsFileEntitySource = moduleEntity.entitySource as? JpsFileEntitySource.FileInDirectory
559
560     // TODO Is this fallback fake path ok?
561     val directoryPath = jpsFileEntitySource?.directory?.filePath ?: outOfTreeModulesPath
562     return "$directoryPath/${moduleEntity.name}.iml"
563   }
564
565   fun createModuleInstance(moduleEntity: ModuleEntity,
566                            entityStore: TypedEntityStore,
567                            diff: TypedEntityStorageDiffBuilder?,
568                            isNew: Boolean): LegacyBridgeModule {
569
570     val modulePath = getModuleFilePath(moduleEntity)
571
572     val module = LegacyBridgeModuleImpl(
573       name = moduleEntity.name,
574       project = project,
575       filePath = modulePath,
576       moduleEntityId = moduleEntity.persistentId(),
577       entityStore = entityStore,
578       diff = diff
579     )
580
581     module.init {
582       try {
583         val moduleStore = module.stateStore as ModuleStoreBase
584         moduleStore.setPath(modulePath, isNew)
585         moduleStore.storageManager.addMacro("MODULE_FILE", modulePath)
586       }
587       catch (t: Throwable) {
588         logger<LegacyBridgeModuleManagerComponent>().error(t)
589       }
590     }
591
592     return module
593   }
594
595   internal fun getModulePath(moduleEntity: ModuleEntity): ModulePath = ModulePath(
596     path = getModuleFilePath(moduleEntity),
597     group = moduleEntity.groupPath?.path?.joinToString(separator = ModuleManagerImpl.MODULE_GROUP_SEPARATOR)
598   )
599
600   companion object {
601     @JvmStatic
602     fun getInstance(project: Project): LegacyBridgeModuleManagerComponent =
603       ModuleManagerComponent.getInstance(project) as LegacyBridgeModuleManagerComponent
604
605     private fun List<EntityChange<LibraryEntity>>.filterModuleLibraryChanges() =
606       filter {
607         when (it) {
608           is EntityChange.Added -> it.entity.tableId is LibraryTableId.ModuleLibraryTableId
609           is EntityChange.Removed -> it.entity.tableId is LibraryTableId.ModuleLibraryTableId
610           is EntityChange.Replaced -> it.oldEntity.tableId is LibraryTableId.ModuleLibraryTableId
611         }
612       }
613
614     internal fun getModuleGroupPath(module: Module, entityStore: TypedEntityStore): Array<String>? {
615       val moduleId = (module as LegacyBridgeModule).moduleEntityId
616
617       val storage = entityStore.current
618       val moduleEntity = storage.resolve(moduleId) ?: return null
619
620       return moduleEntity.groupPath?.path?.toTypedArray()
621     }
622
623     internal fun getModulePath(module: Module, entityStore: TypedEntityStore): ModulePath = ModulePath(
624       path = module.moduleFilePath,
625       group = getModuleGroupPath(module, entityStore)?.joinToString(separator = ModuleManagerImpl.MODULE_GROUP_SEPARATOR)
626     )
627
628     internal fun hasModuleGroups(entityStore: TypedEntityStore) =
629       entityStore.current.entities(ModuleGroupPathEntity::class.java).firstOrNull() != null
630   }
631 }