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