mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[workspace model] IJPL-169 Migration of custom libraries to WSM
Prior to this change custom libraries stored in `LibraryTableBase` and it was the latest peace of libraries that was not stored in WSM. Custom libraries also very similar to the global one, so some common code was extracted GitOrigin-RevId: 147f5f24047b7e3820918592bfb3500ab34b97a8
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7bc51722c6
commit
400ae4e157
+5
-4
@@ -7,9 +7,10 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.impl.OrderRootsCache
|
||||
import com.intellij.openapi.roots.impl.ProjectRootManagerComponent
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
|
||||
import com.intellij.openapi.util.EmptyRunnable
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryTableId
|
||||
import com.intellij.util.indexing.BuildableRootsChangeRescanningInfo
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.LibraryBridge
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.module.roots.OrderRootsCacheBridge
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.ModuleDependencyIndex
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.ModuleDependencyListener
|
||||
@@ -77,9 +78,9 @@ class ProjectRootManagerBridge(project: Project, coroutineScope: CoroutineScope)
|
||||
}
|
||||
|
||||
private fun shouldListen(library: Library): Boolean {
|
||||
//project and global level libraries are stored in WorkspaceModel, and changes in their roots are handled by RootsChangeWatcher
|
||||
return library.table?.tableLevel != LibraryTablesRegistrar.PROJECT_LEVEL &&
|
||||
library.table?.tableLevel != LibraryTablesRegistrar.APPLICATION_LEVEL
|
||||
//project, global and custom level libraries are stored in WorkspaceModel, and changes in their roots are handled by RootsChangeWatcher
|
||||
val libraryTableId = (library as? LibraryBridge)?.libraryId?.tableId ?: return true
|
||||
return libraryTableId !is LibraryTableId.ProjectLibraryTableId && libraryTableId !is LibraryTableId.GlobalLibraryTableId
|
||||
}
|
||||
|
||||
override fun referencedSdkAdded(sdk: Sdk) {
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ class GlobalSdkBridgeInitializer : BridgeInitializer {
|
||||
|
||||
class GlobalSdkBridgesLoader: GlobalSdkTableBridge {
|
||||
|
||||
override fun initializeSdkBridgesAfterLoading(mutableStorage: MutableEntityStorage,
|
||||
override fun initializeBridgesAfterLoading(mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage): () -> Unit {
|
||||
val sdks = mutableStorage
|
||||
.entities(SdkEntity::class.java)
|
||||
@@ -51,7 +51,7 @@ class GlobalSdkBridgesLoader: GlobalSdkTableBridge {
|
||||
return {}
|
||||
}
|
||||
|
||||
override fun initializeSdkBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage) {
|
||||
override fun initializeBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val sdkChanges = (changes[SdkEntity::class.java] as? List<EntityChange<SdkEntity>>) ?: emptyList()
|
||||
val addChanges = sdkChanges.filterIsInstance<EntityChange.Added<SdkEntity>>()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.intellij.openapi.roots
|
||||
|
||||
import com.intellij.openapi.application.runWriteActionAndWait
|
||||
import com.intellij.openapi.roots.impl.libraries.CustomLibraryTableImpl
|
||||
import com.intellij.openapi.roots.impl.libraries.LibraryEx
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
@@ -17,7 +16,6 @@ import com.intellij.testFramework.rules.ProjectModelRule
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.LegacyBridgeModifiableBase
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.LibraryBridgeImpl
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Assume
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -190,8 +188,6 @@ abstract class LibraryTableTestCase {
|
||||
|
||||
@Test
|
||||
fun `check events count at library update`() {
|
||||
Assume.assumeFalse("Test isn't applicable for CustomLibraryTable", libraryTable is CustomLibraryTableImpl)
|
||||
|
||||
var eventsCount = 0
|
||||
val libraryNames = listOf("a", "b", "c")
|
||||
projectModel.project.messageBus.connect().subscribe(WorkspaceModelTopics.CHANGED, object : WorkspaceModelChangeListener {
|
||||
@@ -222,8 +218,6 @@ abstract class LibraryTableTestCase {
|
||||
|
||||
@Test
|
||||
fun `use single builder at library update`() {
|
||||
Assume.assumeFalse("Test isn't applicable for CustomLibraryTable", libraryTable is CustomLibraryTableImpl)
|
||||
|
||||
val libraryNames = listOf("a", "b", "c")
|
||||
edit { model ->
|
||||
val mutableStorage = (model as LegacyBridgeModifiableBase).diff
|
||||
|
||||
+4
-8
@@ -17,8 +17,7 @@ import com.intellij.workspaceModel.ide.*
|
||||
import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.impl.jpsMetrics
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.sdk.SdkBridgeImpl.Companion.sdkMap
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalSdkTableBridge
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalEntityBridgeAndEventHandler
|
||||
import io.opentelemetry.api.metrics.Meter
|
||||
import kotlinx.coroutines.*
|
||||
import org.jdom.Element
|
||||
@@ -156,13 +155,10 @@ class JpsGlobalModelSynchronizerImpl(private val coroutineScope: CoroutineScope)
|
||||
|
||||
private fun bridgesInitializationCallback(mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage): () -> Unit {
|
||||
val sdkCallback = GlobalSdkTableBridge.getInstance().initializeSdkBridgesAfterLoading(mutableStorage,
|
||||
initialEntityStorage)
|
||||
val librariesCallback = GlobalLibraryTableBridge.getInstance().initializeLibraryBridgesAfterLoading(mutableStorage,
|
||||
initialEntityStorage)
|
||||
val callbacks = GlobalEntityBridgeAndEventHandler.getAllGlobalEntityHandlers()
|
||||
.map { it.initializeBridgesAfterLoading(mutableStorage, initialEntityStorage) }
|
||||
return {
|
||||
sdkCallback.invoke()
|
||||
librariesCallback.invoke()
|
||||
callbacks.forEach { it.invoke() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.roots.libraries;
|
||||
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.WriteExternalException;
|
||||
import org.jdom.Element;
|
||||
|
||||
/**
|
||||
* The CustomLibraryTable interface extends the LibraryTable interface and represents a custom library table.
|
||||
* It provides methods to read and write the library table from/to an XML element.
|
||||
* <p>
|
||||
* The direct usage of these methods has to be provided by the client, not by the platform,
|
||||
* so client's code is responsible for serialization of custom libraries to/from disk
|
||||
*/
|
||||
public interface CustomLibraryTable extends LibraryTable {
|
||||
void readExternal(final Element element) throws InvalidDataException;
|
||||
void writeExternal(final Element element) throws WriteExternalException;
|
||||
}
|
||||
@@ -28,13 +28,19 @@
|
||||
<applicationService serviceInterface="com.intellij.openapi.roots.libraries.LibraryTablesRegistrar"
|
||||
serviceImplementation="com.intellij.openapi.roots.impl.libraries.LibraryTablesRegistrarImpl"/>
|
||||
<!-- Global Workspace Model -->
|
||||
<applicationService serviceInterface="com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge"
|
||||
serviceImplementation="com.intellij.workspaceModel.ide.impl.legacyBridge.library.GlobalLibraryTableBridgeImpl"/>
|
||||
<workspace.bridgeInitializer
|
||||
implementation="com.intellij.workspaceModel.ide.impl.legacyBridge.library.GlobalLibraryTableBridgeInitializer"/>
|
||||
<applicationService serviceImplementation="com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel"/>
|
||||
<applicationService serviceInterface="com.intellij.workspaceModel.ide.JpsGlobalModelSynchronizer"
|
||||
serviceImplementation="com.intellij.workspaceModel.ide.impl.jps.serialization.JpsGlobalModelSynchronizerImpl"/>
|
||||
<applicationService serviceImplementation="com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel"/>
|
||||
<!-- Global Workspace Model Libraries -->
|
||||
<applicationService serviceInterface="com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge"
|
||||
serviceImplementation="com.intellij.workspaceModel.ide.impl.legacyBridge.library.GlobalLibraryTableBridgeImpl"/>
|
||||
<!-- Custom Workspace Model Libraries -->
|
||||
<registryKey key="workspace.model.custom.library.bridge" defaultValue="true"
|
||||
description="Enables new implementation for Custom libraries which works via Workspace Model"
|
||||
restartRequired="true"/>
|
||||
<!-- Global or Custom Workspace Model Libraries -->
|
||||
<workspace.bridgeInitializer
|
||||
implementation="com.intellij.workspaceModel.ide.impl.legacyBridge.library.GlobalAndCustomLibraryTableBridgeInitializer"/>
|
||||
|
||||
<projectService serviceInterface="com.intellij.openapi.roots.FileIndexFacade"
|
||||
serviceImplementation="com.intellij.openapi.roots.impl.ProjectFileIndexFacade"/>
|
||||
|
||||
+62
-2
@@ -1,12 +1,72 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.roots.impl.libraries
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.roots.libraries.CustomLibraryTable
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablePresentation
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.CustomLibraryTableBridgeImpl
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.CustomLibraryTableBridge
|
||||
import org.jdom.Element
|
||||
|
||||
class CustomLibraryTableImpl(private val level: String, private val presentation: LibraryTablePresentation) : LibraryTableBase() {
|
||||
class CustomLibraryTableImpl(level: String, presentation: LibraryTablePresentation) : CustomLibraryTable, Disposable {
|
||||
private val delegate: CustomLibraryTable = if (CustomLibraryTableBridge.isEnabled()) {
|
||||
CustomLibraryTableBridgeImpl(level, presentation)
|
||||
}
|
||||
else {
|
||||
LegacyCustomLibraryTable(level, presentation)
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<Library> = delegate.libraries
|
||||
|
||||
override fun createLibrary(): Library = delegate.createLibrary()
|
||||
|
||||
override fun createLibrary(name: String?): Library = delegate.createLibrary(name)
|
||||
|
||||
override fun removeLibrary(library: Library) = delegate.removeLibrary(library)
|
||||
|
||||
override fun getLibraryIterator(): MutableIterator<Library> = delegate.getLibraryIterator()
|
||||
|
||||
override fun getLibraryByName(name: String): Library? = delegate.getLibraryByName(name)
|
||||
|
||||
override fun getTableLevel(): String = delegate.tableLevel
|
||||
|
||||
override fun getPresentation(): LibraryTablePresentation = delegate.presentation
|
||||
|
||||
override fun isEditable(): Boolean = false
|
||||
|
||||
override fun getModifiableModel(): LibraryTable.ModifiableModel = delegate.modifiableModel
|
||||
|
||||
override fun addListener(listener: LibraryTable.Listener) = delegate.addListener(listener)
|
||||
|
||||
override fun addListener(listener: LibraryTable.Listener, parentDisposable: Disposable) = delegate.addListener(listener, parentDisposable)
|
||||
|
||||
override fun removeListener(listener: LibraryTable.Listener) = delegate.removeListener(listener)
|
||||
|
||||
override fun readExternal(element: Element): Unit = delegate.readExternal(element)
|
||||
|
||||
override fun writeExternal(element: Element): Unit = delegate.writeExternal(element)
|
||||
|
||||
internal fun getDelegate(): LibraryTable = delegate
|
||||
|
||||
override fun dispose() {
|
||||
if (delegate is Disposable) Disposer.dispose(delegate)
|
||||
}
|
||||
}
|
||||
|
||||
private class LegacyCustomLibraryTable(private val level: String, private val presentation: LibraryTablePresentation)
|
||||
: LibraryTableBase(), CustomLibraryTable {
|
||||
override fun getTableLevel(): String = level
|
||||
|
||||
override fun getPresentation(): LibraryTablePresentation = presentation
|
||||
|
||||
override fun isEditable(): Boolean = false
|
||||
override fun readExternal(element: Element) {
|
||||
super.readExternal(element)
|
||||
}
|
||||
|
||||
override fun writeExternal(element: Element) {
|
||||
super.writeExternal(element)
|
||||
}
|
||||
}
|
||||
+10
-8
@@ -21,7 +21,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
final class LibraryTablesRegistrarImpl extends LibraryTablesRegistrar implements Disposable {
|
||||
private static final ExtensionPointName<CustomLibraryTableDescription> CUSTOM_TABLES_EP = new ExtensionPointName<>("com.intellij.customLibraryTable");
|
||||
private final Map<String, LibraryTableBase> customLibraryTables = new ConcurrentHashMap<>();
|
||||
private final Map<String, LibraryTable> customLibraryTables = new ConcurrentHashMap<>();
|
||||
private volatile boolean extensionLoaded = false;
|
||||
private final Object extensionLoadingLock = new Object();
|
||||
|
||||
@@ -55,7 +55,7 @@ final class LibraryTablesRegistrarImpl extends LibraryTablesRegistrar implements
|
||||
return getCustomLibrariesMap().get(level);
|
||||
}
|
||||
|
||||
public @NotNull Map<String, LibraryTableBase> getCustomLibrariesMap() {
|
||||
private @NotNull Map<String, LibraryTable> getCustomLibrariesMap() {
|
||||
if (extensionLoaded) {
|
||||
return customLibraryTables;
|
||||
}
|
||||
@@ -65,15 +65,15 @@ final class LibraryTablesRegistrarImpl extends LibraryTablesRegistrar implements
|
||||
CUSTOM_TABLES_EP.getPoint().addExtensionPointListener(new ExtensionPointListener<>() {
|
||||
@Override
|
||||
public void extensionAdded(@NotNull CustomLibraryTableDescription extension, @NotNull PluginDescriptor pluginDescriptor) {
|
||||
LibraryTableBase table = new CustomLibraryTableImpl(extension.getTableLevel(), extension.getPresentation());
|
||||
LibraryTable table = new CustomLibraryTableImpl(extension.getTableLevel(), extension.getPresentation());
|
||||
customLibraryTables.put(extension.getTableLevel(), table);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extensionRemoved(@NotNull CustomLibraryTableDescription extension, @NotNull PluginDescriptor pluginDescriptor) {
|
||||
LibraryTableBase table = customLibraryTables.remove(extension.getTableLevel());
|
||||
if (table != null) {
|
||||
Disposer.dispose(table);
|
||||
LibraryTable table = customLibraryTables.remove(extension.getTableLevel());
|
||||
if (table instanceof Disposable disposable) {
|
||||
Disposer.dispose(disposable);
|
||||
}
|
||||
}
|
||||
}, true, null);
|
||||
@@ -90,8 +90,10 @@ final class LibraryTablesRegistrarImpl extends LibraryTablesRegistrar implements
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
for (LibraryTableBase value : customLibraryTables.values()) {
|
||||
Disposer.dispose(value);
|
||||
for (LibraryTable table : customLibraryTables.values()) {
|
||||
if (table instanceof Disposable disposable) {
|
||||
Disposer.dispose(disposable);
|
||||
}
|
||||
}
|
||||
customLibraryTables.clear();
|
||||
}
|
||||
|
||||
+6
-4
@@ -9,6 +9,7 @@ import com.intellij.openapi.vfs.newvfs.events.*
|
||||
import com.intellij.platform.backend.workspace.WorkspaceModel
|
||||
import com.intellij.platform.backend.workspace.virtualFile
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryEntity
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryTableId
|
||||
import com.intellij.platform.workspace.storage.EntityReference
|
||||
import com.intellij.platform.workspace.storage.EntityStorage
|
||||
import com.intellij.platform.workspace.storage.WorkspaceEntity
|
||||
@@ -20,6 +21,8 @@ import com.intellij.util.io.URLUtil
|
||||
import com.intellij.workspaceModel.core.fileIndex.EntityStorageKind
|
||||
import com.intellij.workspaceModel.ide.getInstance
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.GlobalLibraryTableBridgeImpl
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.LibraryBridgeImpl
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.libraryMap
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge
|
||||
import java.util.*
|
||||
|
||||
@@ -190,15 +193,14 @@ private class VfsChangeApplierImpl(
|
||||
}
|
||||
indexData.updateDirtyEntities()
|
||||
|
||||
// Keep old behaviour for global libraries
|
||||
// Keep old behaviour for global and custom libraries
|
||||
if (affectedEntities.isNotEmpty()) {
|
||||
val entityStorage = WorkspaceModel.getInstance(project).currentSnapshot
|
||||
val globalLibraryTableBridge = GlobalLibraryTableBridge.getInstance() as GlobalLibraryTableBridgeImpl
|
||||
|
||||
affectedEntities.forEach { entityRef ->
|
||||
val libraryEntity = (entityRef.resolve(entityStorage) as? LibraryEntity) ?: return@forEach
|
||||
if (libraryEntity.tableId.level != LibraryTablesRegistrar.APPLICATION_LEVEL) return@forEach
|
||||
globalLibraryTableBridge.fireRootSetChanged(libraryEntity, entityStorage)
|
||||
if (libraryEntity.tableId !is LibraryTableId.GlobalLibraryTableId) return@forEach
|
||||
(entityStorage.libraryMap.getDataByEntity(libraryEntity) as? LibraryBridgeImpl)?.fireRootSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-16
@@ -11,23 +11,23 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.backend.workspace.GlobalWorkspaceModelCache
|
||||
import com.intellij.workspaceModel.ide.JpsGlobalModelSynchronizer
|
||||
import com.intellij.platform.backend.workspace.WorkspaceModel
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.addElapsedTimeMillis
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.addMeasuredTimeMillis
|
||||
import com.intellij.platform.workspace.jps.JpsGlobalFileEntitySource
|
||||
import com.intellij.platform.workspace.jps.entities.*
|
||||
import com.intellij.util.concurrency.annotations.RequiresWriteLock
|
||||
import com.intellij.workspaceModel.ide.*
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.libraryMap
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.mutableLibraryMap
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.impl.VersionedEntityStorageImpl
|
||||
import com.intellij.platform.workspace.storage.impl.assertConsistency
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrl
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import com.intellij.util.concurrency.annotations.RequiresWriteLock
|
||||
import com.intellij.workspaceModel.ide.JpsGlobalModelSynchronizer
|
||||
import com.intellij.workspaceModel.ide.getGlobalInstance
|
||||
import com.intellij.workspaceModel.ide.getInstance
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.libraryMap
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.mutableLibraryMap
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalEntityBridgeAndEventHandler
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalSdkTableBridge
|
||||
import io.opentelemetry.api.metrics.Meter
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -143,27 +143,25 @@ class GlobalWorkspaceModel : Disposable {
|
||||
@RequiresWriteLock
|
||||
private fun initializeBridges(change: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage) {
|
||||
ThreadingAssertions.assertWriteAccess()
|
||||
logErrorOnEventHandling {
|
||||
GlobalLibraryTableBridge.getInstance().initializeLibraryBridges(change, builder)
|
||||
}
|
||||
logErrorOnEventHandling {
|
||||
GlobalSdkTableBridge.getInstance().initializeSdkBridges(change, builder)
|
||||
|
||||
GlobalEntityBridgeAndEventHandler.getAllGlobalEntityHandlers().forEach {
|
||||
logErrorOnEventHandling {
|
||||
it.initializeBridges(change, builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBeforeChanged(change: VersionedStorageChange) {
|
||||
ThreadingAssertions.assertWriteAccess()
|
||||
|
||||
GlobalSdkTableBridge.getInstance().handleBeforeChangeEvents(change)
|
||||
GlobalLibraryTableBridge.getInstance().handleBeforeChangeEvents(change)
|
||||
GlobalEntityBridgeAndEventHandler.getAllGlobalEntityHandlers().forEach { it.handleBeforeChangeEvents(change) }
|
||||
}
|
||||
|
||||
@RequiresWriteLock
|
||||
private fun onChanged(change: VersionedStorageChange) {
|
||||
ThreadingAssertions.assertWriteAccess()
|
||||
|
||||
GlobalSdkTableBridge.getInstance().handleChangedEvents(change)
|
||||
GlobalLibraryTableBridge.getInstance().handleChangedEvents(change)
|
||||
GlobalEntityBridgeAndEventHandler.getAllGlobalEntityHandlers().forEach { it.handleChangedEvents(change) }
|
||||
|
||||
globalWorkspaceModelCache?.scheduleCacheSave()
|
||||
isFromGlobalWorkspaceModel = true
|
||||
@@ -212,6 +210,7 @@ class GlobalWorkspaceModel : Disposable {
|
||||
private fun copyEntitiesToEmptyStorage(storage: EntityStorage, vfuManager: VirtualFileUrlManager): MutableEntityStorage {
|
||||
val mutableEntityStorage = MutableEntityStorage.create()
|
||||
// Copying global libraries
|
||||
// TODO:: Check heree that we also copying custom libraries
|
||||
storage.entities(LibraryEntity::class.java).forEach { libraryEntity ->
|
||||
if (!globalEntitiesFilter.invoke(libraryEntity.entitySource)) return@forEach
|
||||
val libraryRootsCopy = libraryEntity.roots.map { root ->
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.workspaceModel.ide.impl.legacyBridge.library
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.roots.libraries.CustomLibraryTable
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablePresentation
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryEntity
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryTableId
|
||||
import com.intellij.platform.workspace.jps.serialization.impl.JpsLibraryEntitiesSerializer
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.url.VirtualFileUrlManager
|
||||
import com.intellij.workspaceModel.ide.getGlobalInstance
|
||||
import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.CustomLibraryTableBridge
|
||||
import org.jdom.Element
|
||||
import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer
|
||||
import org.jetbrains.jps.model.serialization.module.JpsModuleRootModelSerializer
|
||||
|
||||
internal class CustomLibraryTableBridgeImpl(private val level: String, private val presentation: LibraryTablePresentation)
|
||||
: CustomLibraryTableBridge, CustomLibraryTable, Disposable {
|
||||
private val libraryTableId = LibraryTableId.GlobalLibraryTableId(tableLevel)
|
||||
private val libraryTableDelegate = GlobalLibraryTableDelegate(this, libraryTableId)
|
||||
|
||||
override fun initializeBridgesAfterLoading(mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage): () -> Unit {
|
||||
return libraryTableDelegate.initializeLibraryBridgesAfterLoading(mutableStorage, initialEntityStorage)
|
||||
}
|
||||
|
||||
override fun initializeBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage) {
|
||||
libraryTableDelegate.initializeLibraryBridges(changes, builder)
|
||||
}
|
||||
|
||||
override fun handleBeforeChangeEvents(event: VersionedStorageChange) {
|
||||
libraryTableDelegate.handleBeforeChangeEvents(event)
|
||||
}
|
||||
|
||||
override fun handleChangedEvents(event: VersionedStorageChange) {
|
||||
libraryTableDelegate.handleChangedEvents(event)
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<Library> = libraryTableDelegate.getLibraries()
|
||||
|
||||
override fun getLibraryIterator(): Iterator<Library> = getLibraries().iterator()
|
||||
|
||||
override fun getLibraryByName(name: String): Library? = libraryTableDelegate.getLibraryByName(name)
|
||||
|
||||
override fun createLibrary(): Library = createLibrary(null)
|
||||
|
||||
override fun createLibrary(name: String?): Library = libraryTableDelegate.createLibrary(name)
|
||||
|
||||
override fun removeLibrary(library: Library): Unit = libraryTableDelegate.removeLibrary(library)
|
||||
|
||||
override fun getTableLevel(): String = level
|
||||
|
||||
override fun getPresentation(): LibraryTablePresentation = presentation
|
||||
|
||||
override fun getModifiableModel(): LibraryTable.ModifiableModel = GlobalOrCustomModifiableLibraryTableBridgeImpl(this)
|
||||
|
||||
override fun isEditable(): Boolean = false
|
||||
|
||||
override fun dispose() {
|
||||
if (libraries.isEmpty()) {
|
||||
Disposer.dispose(libraryTableDelegate)
|
||||
return
|
||||
}
|
||||
|
||||
val runnable: () -> Unit = {
|
||||
// We need to remove all related libraries from the [GlobalWorkspaceModel] e.g. extension point and related [CustomLibraryTable] can be unloaded
|
||||
val modifiableModel = modifiableModel
|
||||
libraries.forEach { modifiableModel.removeLibrary(it) }
|
||||
modifiableModel.commit()
|
||||
Disposer.dispose(libraryTableDelegate)
|
||||
}
|
||||
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isWriteAccessAllowed) {
|
||||
runnable.invoke()
|
||||
}
|
||||
else {
|
||||
application.invokeLater {
|
||||
application.runWriteAction {
|
||||
runnable.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun readExternal(libraryTableTag: Element) {
|
||||
val mutableEntityStorage = MutableEntityStorage.create()
|
||||
libraryTableTag.getChildren(JpsLibraryTableSerializer.LIBRARY_TAG).forEach { libraryTag ->
|
||||
val name = libraryTag.getAttributeValue(JpsModuleRootModelSerializer.NAME_ATTRIBUTE)
|
||||
val libraryEntity = JpsLibraryEntitiesSerializer.loadLibrary(name, libraryTag, libraryTableId, LegacyCustomLibraryEntitySource,
|
||||
VirtualFileUrlManager.getGlobalInstance())
|
||||
mutableEntityStorage.addEntity(libraryEntity)
|
||||
}
|
||||
|
||||
if (!mutableEntityStorage.hasChanges()) return
|
||||
|
||||
val runnable: () -> Unit = {
|
||||
GlobalWorkspaceModel.getInstance().updateModel("Custom library table ${libraryTableId.level} update") { builder ->
|
||||
builder.replaceBySource({ it is LegacyCustomLibraryEntitySource }, mutableEntityStorage)
|
||||
}
|
||||
}
|
||||
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isWriteAccessAllowed) {
|
||||
runnable.invoke()
|
||||
}
|
||||
else {
|
||||
application.invokeLater {
|
||||
application.runWriteAction {
|
||||
runnable.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeExternal(element: Element) {
|
||||
GlobalWorkspaceModel.getInstance().currentSnapshot.entities(LibraryEntity::class.java)
|
||||
.filter { it.tableId == libraryTableId }
|
||||
.sortedBy { it.name }
|
||||
.forEach { libraryEntity ->
|
||||
val libraryTag = JpsLibraryEntitiesSerializer.saveLibrary(libraryEntity, null, false)
|
||||
element.addContent(libraryTag)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: LibraryTable.Listener) = libraryTableDelegate.addListener(listener)
|
||||
|
||||
override fun addListener(listener: LibraryTable.Listener, parentDisposable: Disposable): Unit = libraryTableDelegate.addListener(listener, parentDisposable)
|
||||
|
||||
override fun removeListener(listener: LibraryTable.Listener) = libraryTableDelegate.removeListener(listener)
|
||||
}
|
||||
|
||||
object LegacyCustomLibraryEntitySource: EntitySource
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.workspaceModel.ide.impl.legacyBridge.library
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
|
||||
import com.intellij.platform.backend.workspace.BridgeInitializer
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.addMeasuredTimeMillis
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryEntity
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryTableId
|
||||
import com.intellij.platform.workspace.storage.EntityChange
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.mutableLibraryMap
|
||||
|
||||
class GlobalAndCustomLibraryTableBridgeInitializer : BridgeInitializer {
|
||||
override fun isEnabled(): Boolean = true
|
||||
|
||||
// Handle the initialization of all global and custom libraries
|
||||
override fun initializeBridges(project: Project,
|
||||
changes: Map<Class<*>, List<EntityChange<*>>>,
|
||||
builder: MutableEntityStorage) = GlobalLibraryTableBridgeImpl.initializeLibraryBridgesTimeMs.addMeasuredTimeMillis {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val libraryChanges = (changes[LibraryEntity::class.java] as? List<EntityChange<LibraryEntity>>) ?: emptyList()
|
||||
val addChanges = libraryChanges.filterGlobalOrCustomLibraryChanges().filterIsInstance<EntityChange.Added<LibraryEntity>>()
|
||||
|
||||
for (addChange in addChanges) {
|
||||
// Will initialize the bridge if missing
|
||||
builder.mutableLibraryMap.getOrPutDataByEntity(addChange.entity) {
|
||||
LibraryBridgeImpl(
|
||||
libraryTable = getGlobalOrCustomLibraryTable(addChange.entity.symbolicId.tableId.level),
|
||||
project = null,
|
||||
initialId = addChange.entity.symbolicId,
|
||||
initialEntityStorage = entityStorage,
|
||||
targetBuilder = builder
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGlobalOrCustomLibraryTable(tableId: String): LibraryTable {
|
||||
val libraryTablesRegistrar = LibraryTablesRegistrar.getInstance()
|
||||
return when (tableId) {
|
||||
LibraryTablesRegistrar.APPLICATION_LEVEL -> libraryTablesRegistrar.libraryTable
|
||||
else -> libraryTablesRegistrar.getCustomLibraryTableByLevel(tableId)!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<EntityChange<LibraryEntity>>.filterGlobalOrCustomLibraryChanges(): List<EntityChange<LibraryEntity>> {
|
||||
return filter {
|
||||
when (it) {
|
||||
is EntityChange.Added -> it.entity.tableId is LibraryTableId.GlobalLibraryTableId
|
||||
is EntityChange.Removed -> it.entity.tableId is LibraryTableId.GlobalLibraryTableId
|
||||
is EntityChange.Replaced -> it.oldEntity.tableId is LibraryTableId.GlobalLibraryTableId
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
-240
@@ -2,271 +2,76 @@
|
||||
package com.intellij.workspaceModel.ide.impl.legacyBridge.library
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablePresentation
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.backend.workspace.BridgeInitializer
|
||||
import com.intellij.platform.diagnostic.telemetry.helpers.addMeasuredTimeMillis
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryEntity
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryId
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryTableId
|
||||
import com.intellij.platform.workspace.jps.serialization.impl.LibraryNameGenerator
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.platform.workspace.storage.EntityChange
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedStorageChange
|
||||
import com.intellij.projectModel.ProjectModelBundle
|
||||
import com.intellij.util.EventDispatcher
|
||||
import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.impl.jpsMetrics
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.GlobalLibraryTableBridgeImpl.Companion.initializeLibraryBridgesTimeMs
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.libraryMap
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.mutableLibraryMap
|
||||
import com.intellij.workspaceModel.ide.legacyBridge.GlobalLibraryTableBridge
|
||||
import io.opentelemetry.api.metrics.Meter
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
class GlobalLibraryTableBridgeInitializer : BridgeInitializer {
|
||||
override fun isEnabled(): Boolean = true
|
||||
|
||||
override fun initializeBridges(project: Project,
|
||||
changes: Map<Class<*>,
|
||||
List<EntityChange<*>>>,
|
||||
builder: MutableEntityStorage) = initializeLibraryBridgesTimeMs.addMeasuredTimeMillis {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val libraryChanges = (changes[LibraryEntity::class.java] as? List<EntityChange<LibraryEntity>>) ?: emptyList()
|
||||
val addChanges = libraryChanges.filterGlobalLibraryChanges().filterIsInstance<EntityChange.Added<LibraryEntity>>()
|
||||
|
||||
for (addChange in addChanges) {
|
||||
// Will initialize the bridge if missing
|
||||
builder.mutableLibraryMap.getOrPutDataByEntity(addChange.entity) {
|
||||
LibraryBridgeImpl(
|
||||
libraryTable = GlobalLibraryTableBridge.getInstance(),
|
||||
project = null,
|
||||
initialId = addChange.entity.symbolicId,
|
||||
initialEntityStorage = entityStorage,
|
||||
targetBuilder = builder
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GlobalLibraryTableBridgeImpl : GlobalLibraryTableBridge, Disposable {
|
||||
private val dispatcher = EventDispatcher.create(LibraryTable.Listener::class.java)
|
||||
private val libraryTableDelegate = GlobalLibraryTableDelegate(this, LibraryTableId.GlobalLibraryTableId(LibraryTablesRegistrar.APPLICATION_LEVEL))
|
||||
|
||||
override fun initializeLibraryBridges(
|
||||
changes: Map<Class<*>,
|
||||
List<EntityChange<*>>>,
|
||||
builder: MutableEntityStorage) = initializeLibraryBridgesTimeMs.addMeasuredTimeMillis {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val libraryChanges = (changes[LibraryEntity::class.java] as? List<EntityChange<LibraryEntity>>) ?: emptyList()
|
||||
val addChanges = libraryChanges.filterGlobalLibraryChanges().filterIsInstance<EntityChange.Added<LibraryEntity>>()
|
||||
|
||||
for (addChange in addChanges) {
|
||||
// Will initialize the bridge if missing
|
||||
builder.mutableLibraryMap.getOrPutDataByEntity(addChange.entity) {
|
||||
LibraryBridgeImpl(
|
||||
libraryTable = this@GlobalLibraryTableBridgeImpl,
|
||||
project = null,
|
||||
initialId = addChange.entity.symbolicId,
|
||||
initialEntityStorage = entityStorage,
|
||||
targetBuilder = builder
|
||||
)
|
||||
}
|
||||
}
|
||||
override fun initializeBridges(changes: Map<Class<*>, List<EntityChange<*>>>,
|
||||
builder: MutableEntityStorage) = initializeLibraryBridgesTimeMs.addMeasuredTimeMillis {
|
||||
libraryTableDelegate.initializeLibraryBridges(changes, builder)
|
||||
}
|
||||
|
||||
override fun initializeLibraryBridgesAfterLoading(
|
||||
mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage
|
||||
): () -> Unit = initializeLibraryBridgesAfterLoadingTimeMs.addMeasuredTimeMillis {
|
||||
|
||||
val libraries = mutableStorage
|
||||
.entities(LibraryEntity::class.java)
|
||||
.filter { it.tableId is LibraryTableId.GlobalLibraryTableId }
|
||||
.filter { mutableStorage.libraryMap.getDataByEntity(it) == null }
|
||||
.map { libraryEntity ->
|
||||
Pair(libraryEntity, LibraryBridgeImpl(
|
||||
libraryTable = this@GlobalLibraryTableBridgeImpl,
|
||||
project = null,
|
||||
initialId = libraryEntity.symbolicId,
|
||||
initialEntityStorage = initialEntityStorage,
|
||||
targetBuilder = null
|
||||
))
|
||||
}
|
||||
.toList()
|
||||
LOG.debug("Initial load of application-level libraries")
|
||||
if (libraries.isEmpty()) {
|
||||
return@addMeasuredTimeMillis {}
|
||||
}
|
||||
|
||||
for ((entity, library) in libraries) {
|
||||
mutableStorage.mutableLibraryMap.addIfAbsent(entity, library)
|
||||
}
|
||||
|
||||
val action: () -> Unit = {
|
||||
// TODO:: Check should we fire this event
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isWriteAccessAllowed) {
|
||||
for ((_, library) in libraries) {
|
||||
dispatcher.multicaster.afterLibraryAdded(library)
|
||||
}
|
||||
}
|
||||
else {
|
||||
application.invokeLater {
|
||||
runWriteAction {
|
||||
for ((_, library) in libraries) {
|
||||
dispatcher.multicaster.afterLibraryAdded(library)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return@addMeasuredTimeMillis action
|
||||
override fun initializeBridgesAfterLoading(mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage): () -> Unit
|
||||
= initializeLibraryBridgesAfterLoadingTimeMs.addMeasuredTimeMillis {
|
||||
return@addMeasuredTimeMillis libraryTableDelegate.initializeLibraryBridgesAfterLoading(mutableStorage, initialEntityStorage)
|
||||
}
|
||||
|
||||
override fun handleBeforeChangeEvents(event: VersionedStorageChange) = handleBeforeChangeEventsTimeMs.addMeasuredTimeMillis {
|
||||
val removeChanges = event.getChanges(LibraryEntity::class.java).filterGlobalLibraryChanges()
|
||||
.filterIsInstance<EntityChange.Removed<LibraryEntity>>()
|
||||
if (removeChanges.isEmpty()) return@addMeasuredTimeMillis
|
||||
|
||||
for (change in removeChanges) {
|
||||
val library = event.storageBefore.libraryMap.getDataByEntity(change.entity)
|
||||
LOG.debug { "Fire 'beforeLibraryRemoved' event for ${change.entity.name}, library = $library" }
|
||||
if (library != null) {
|
||||
dispatcher.multicaster.beforeLibraryRemoved(library)
|
||||
}
|
||||
}
|
||||
libraryTableDelegate.handleBeforeChangeEvents(event)
|
||||
}
|
||||
|
||||
override fun handleChangedEvents(event: VersionedStorageChange) = handleChangedEventsTimeMs.addMeasuredTimeMillis {
|
||||
val changes = event.getChanges(LibraryEntity::class.java)
|
||||
.filterGlobalLibraryChanges()
|
||||
// Since the listener is not deprecated, it will be better to keep the order of events as remove -> replace -> add
|
||||
.orderToRemoveReplaceAdd()
|
||||
if (changes.isEmpty()) return@addMeasuredTimeMillis
|
||||
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
for (change in changes) {
|
||||
LOG.debug { "Process global library change $change" }
|
||||
when (change) {
|
||||
is EntityChange.Added -> {
|
||||
val alreadyCreatedLibrary = event.storageAfter.libraryMap.getDataByEntity(change.entity) as? LibraryBridgeImpl
|
||||
?: error("Library bridge should be created in `before` method")
|
||||
alreadyCreatedLibrary.entityStorage = entityStorage
|
||||
alreadyCreatedLibrary.clearTargetBuilder()
|
||||
|
||||
dispatcher.multicaster.afterLibraryAdded(alreadyCreatedLibrary)
|
||||
}
|
||||
is EntityChange.Removed -> {
|
||||
val library = event.storageBefore.libraryMap.getDataByEntity(change.entity)
|
||||
|
||||
if (library != null) {
|
||||
// TODO There won't be any content in libraryImpl as EntityStore's current was already changed
|
||||
dispatcher.multicaster.afterLibraryRemoved(library)
|
||||
Disposer.dispose(library)
|
||||
}
|
||||
}
|
||||
is EntityChange.Replaced -> {
|
||||
val idBefore = change.oldEntity.symbolicId
|
||||
val idAfter = change.newEntity.symbolicId
|
||||
|
||||
if (idBefore != idAfter) {
|
||||
val library = event.storageBefore.libraryMap.getDataByEntity(change.oldEntity) as? LibraryBridgeImpl
|
||||
if (library != null) {
|
||||
library.entityId = idAfter
|
||||
dispatcher.multicaster.afterLibraryRenamed(library, LibraryNameGenerator.getLegacyLibraryName(idBefore))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fireRootSetChanged(libraryEntity: LibraryEntity, entityStorage: EntityStorage) {
|
||||
(entityStorage.libraryMap.getDataByEntity(libraryEntity) as? LibraryBridgeImpl)?.fireRootSetChanged()
|
||||
libraryTableDelegate.handleChangedEvents(event)
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<Library> = getLibrariesTimeMs.addMeasuredTimeMillis {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
val storage = entityStorage.current
|
||||
val libraryEntitySequence = storage.entities(
|
||||
LibraryEntity::class.java).filter { it.tableId::class == LibraryTableId.GlobalLibraryTableId::class }.toList()
|
||||
val libs: Array<Library> = libraryEntitySequence
|
||||
.mapNotNull { storage.libraryMap.getDataByEntity(it) }
|
||||
.toList().toTypedArray()
|
||||
|
||||
return@addMeasuredTimeMillis libs
|
||||
return@addMeasuredTimeMillis libraryTableDelegate.getLibraries()
|
||||
}
|
||||
|
||||
override fun createLibrary(): Library {
|
||||
return createLibrary(null)
|
||||
}
|
||||
|
||||
override fun createLibrary(name: String?): Library = createLibraryTimeMs.addMeasuredTimeMillis {
|
||||
if (name == null) error("Creating unnamed global libraries is unsupported")
|
||||
|
||||
if (getLibraryByName(name) != null) {
|
||||
error("Application library named $name already exists")
|
||||
}
|
||||
|
||||
val modifiableModel = modifiableModel
|
||||
modifiableModel.createLibrary(name)
|
||||
modifiableModel.commit()
|
||||
|
||||
val newLibrary = getLibraryByName(name)
|
||||
if (newLibrary == null) {
|
||||
error("Library $name was not created")
|
||||
}
|
||||
|
||||
return@addMeasuredTimeMillis newLibrary
|
||||
}
|
||||
|
||||
override fun removeLibrary(library: Library) {
|
||||
val modifiableModel = modifiableModel
|
||||
modifiableModel.removeLibrary(library)
|
||||
modifiableModel.commit()
|
||||
}
|
||||
|
||||
override fun getLibraryIterator(): Iterator<Library> {
|
||||
return libraries.iterator()
|
||||
}
|
||||
override fun getLibraryIterator(): Iterator<Library> = libraries.iterator()
|
||||
|
||||
override fun getLibraryByName(name: String): Library? = getLibraryByNameTimeMs.addMeasuredTimeMillis {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
val libraryId = LibraryId(name, LibraryTableId.GlobalLibraryTableId(LibraryTablesRegistrar.APPLICATION_LEVEL))
|
||||
val library = entityStorage.current.resolve(libraryId)?.let { entity ->
|
||||
entityStorage.current.libraryMap.getDataByEntity(entity)
|
||||
}
|
||||
|
||||
return@addMeasuredTimeMillis library
|
||||
return@addMeasuredTimeMillis libraryTableDelegate.getLibraryByName(name)
|
||||
}
|
||||
|
||||
override fun createLibrary(): Library = createLibrary(null)
|
||||
|
||||
override fun createLibrary(name: String?): Library = createLibraryTimeMs.addMeasuredTimeMillis {
|
||||
return@addMeasuredTimeMillis libraryTableDelegate.createLibrary(name)
|
||||
}
|
||||
|
||||
override fun removeLibrary(library: Library): Unit = libraryTableDelegate.removeLibrary(library)
|
||||
|
||||
override fun getTableLevel(): String = LibraryTablesRegistrar.APPLICATION_LEVEL
|
||||
|
||||
override fun getPresentation(): LibraryTablePresentation = GLOBAL_LIBRARY_TABLE_PRESENTATION
|
||||
override fun getModifiableModel(): LibraryTable.ModifiableModel = GlobalModifiableLibraryTableBridgeImpl(this)
|
||||
|
||||
override fun dispose() {
|
||||
for (library in libraries) {
|
||||
Disposer.dispose(library)
|
||||
}
|
||||
}
|
||||
override fun getModifiableModel(): LibraryTable.ModifiableModel = GlobalOrCustomModifiableLibraryTableBridgeImpl(this)
|
||||
|
||||
override fun addListener(listener: LibraryTable.Listener) = dispatcher.addListener(listener)
|
||||
override fun addListener(listener: LibraryTable.Listener, parentDisposable: Disposable) {
|
||||
dispatcher.addListener(listener, parentDisposable)
|
||||
}
|
||||
override fun dispose(): Unit = Disposer.dispose(libraryTableDelegate)
|
||||
|
||||
override fun removeListener(listener: LibraryTable.Listener) = dispatcher.removeListener(listener)
|
||||
override fun addListener(listener: LibraryTable.Listener) = libraryTableDelegate.addListener(listener)
|
||||
|
||||
override fun addListener(listener: LibraryTable.Listener, parentDisposable: Disposable): Unit = libraryTableDelegate.addListener(listener, parentDisposable)
|
||||
|
||||
override fun removeListener(listener: LibraryTable.Listener) = libraryTableDelegate.removeListener(listener)
|
||||
|
||||
companion object {
|
||||
private val GLOBAL_LIBRARY_TABLE_PRESENTATION: LibraryTablePresentation = object : LibraryTablePresentation() {
|
||||
@@ -283,8 +88,6 @@ class GlobalLibraryTableBridgeImpl : GlobalLibraryTableBridge, Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = logger<GlobalLibraryTableBridgeImpl>()
|
||||
|
||||
internal val initializeLibraryBridgesTimeMs: AtomicLong = AtomicLong()
|
||||
private val initializeLibraryBridgesAfterLoadingTimeMs: AtomicLong = AtomicLong()
|
||||
private val handleBeforeChangeEventsTimeMs: AtomicLong = AtomicLong()
|
||||
@@ -322,13 +125,3 @@ class GlobalLibraryTableBridgeImpl : GlobalLibraryTableBridge, Disposable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<EntityChange<LibraryEntity>>.filterGlobalLibraryChanges(): List<EntityChange<LibraryEntity>> {
|
||||
return filter {
|
||||
when (it) {
|
||||
is EntityChange.Added -> it.entity.tableId is LibraryTableId.GlobalLibraryTableId
|
||||
is EntityChange.Removed -> it.entity.tableId is LibraryTableId.GlobalLibraryTableId
|
||||
is EntityChange.Replaced -> it.oldEntity.tableId is LibraryTableId.GlobalLibraryTableId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.workspaceModel.ide.impl.legacyBridge.library
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryEntity
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryId
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryTableId
|
||||
import com.intellij.platform.workspace.jps.serialization.impl.LibraryNameGenerator
|
||||
import com.intellij.platform.workspace.storage.*
|
||||
import com.intellij.util.EventDispatcher
|
||||
import com.intellij.workspaceModel.ide.impl.GlobalWorkspaceModel
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.libraryMap
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.mutableLibraryMap
|
||||
|
||||
internal class GlobalLibraryTableDelegate(private val libraryTable: LibraryTable, private val libraryTableId: LibraryTableId): Disposable {
|
||||
private val dispatcher = EventDispatcher.create(LibraryTable.Listener::class.java)
|
||||
|
||||
internal fun initializeLibraryBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage) {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val libraryChanges = (changes[LibraryEntity::class.java] as? List<EntityChange<LibraryEntity>>) ?: emptyList()
|
||||
val addChanges = libraryChanges.filterLibraryChanges(libraryTableId).filterIsInstance<EntityChange.Added<LibraryEntity>>()
|
||||
|
||||
for (addChange in addChanges) {
|
||||
// Will initialize the bridge if missing
|
||||
builder.mutableLibraryMap.getOrPutDataByEntity(addChange.entity) {
|
||||
LibraryBridgeImpl(
|
||||
libraryTable = libraryTable,
|
||||
project = null,
|
||||
initialId = addChange.entity.symbolicId,
|
||||
initialEntityStorage = entityStorage,
|
||||
targetBuilder = builder
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun initializeLibraryBridgesAfterLoading(mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage): () -> Unit {
|
||||
val libraries = mutableStorage
|
||||
.entities(LibraryEntity::class.java)
|
||||
.filter { it.tableId == libraryTableId }
|
||||
.filter { mutableStorage.libraryMap.getDataByEntity(it) == null }
|
||||
.map { libraryEntity ->
|
||||
Pair(libraryEntity, LibraryBridgeImpl(
|
||||
libraryTable = libraryTable,
|
||||
project = null,
|
||||
initialId = libraryEntity.symbolicId,
|
||||
initialEntityStorage = initialEntityStorage,
|
||||
targetBuilder = null
|
||||
))
|
||||
}
|
||||
.toList()
|
||||
LOG.debug("Initial load of ${libraryTableId.level}-level libraries")
|
||||
if (libraries.isEmpty()) {
|
||||
return {}
|
||||
}
|
||||
|
||||
for ((entity, library) in libraries) {
|
||||
mutableStorage.mutableLibraryMap.addIfAbsent(entity, library)
|
||||
}
|
||||
|
||||
val action: () -> Unit = {
|
||||
// TODO:: Check should we fire this event
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isWriteAccessAllowed) {
|
||||
for ((_, library) in libraries) {
|
||||
dispatcher.multicaster.afterLibraryAdded(library)
|
||||
}
|
||||
}
|
||||
else {
|
||||
application.invokeLater {
|
||||
runWriteAction {
|
||||
for ((_, library) in libraries) {
|
||||
dispatcher.multicaster.afterLibraryAdded(library)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
internal fun handleBeforeChangeEvents(event: VersionedStorageChange) {
|
||||
val removeChanges = event.getChanges(LibraryEntity::class.java).filterLibraryChanges(libraryTableId)
|
||||
.filterIsInstance<EntityChange.Removed<LibraryEntity>>()
|
||||
if (removeChanges.isEmpty()) return
|
||||
|
||||
for (change in removeChanges) {
|
||||
val library = event.storageBefore.libraryMap.getDataByEntity(change.entity)
|
||||
LOG.debug { "Fire 'beforeLibraryRemoved' event for ${change.entity.name}, library = $library" }
|
||||
if (library != null) {
|
||||
dispatcher.multicaster.beforeLibraryRemoved(library)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun handleChangedEvents(event: VersionedStorageChange) {
|
||||
val changes = event.getChanges(LibraryEntity::class.java).filterLibraryChanges(libraryTableId)
|
||||
// Since the listener is not deprecated, it will be better to keep the order of events as remove -> replace -> add
|
||||
.orderToRemoveReplaceAdd()
|
||||
if (changes.isEmpty()) return
|
||||
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
for (change in changes) {
|
||||
LOG.debug { "Process ${libraryTableId.level} library change $change" }
|
||||
when (change) {
|
||||
is EntityChange.Added -> {
|
||||
val alreadyCreatedLibrary = event.storageAfter.libraryMap.getDataByEntity(change.entity) as? LibraryBridgeImpl
|
||||
?: error("Library bridge should be created in `before` method")
|
||||
alreadyCreatedLibrary.entityStorage = entityStorage
|
||||
alreadyCreatedLibrary.clearTargetBuilder()
|
||||
|
||||
dispatcher.multicaster.afterLibraryAdded(alreadyCreatedLibrary)
|
||||
}
|
||||
is EntityChange.Removed -> {
|
||||
val library = event.storageBefore.libraryMap.getDataByEntity(change.entity)
|
||||
|
||||
if (library != null) {
|
||||
// TODO There won't be any content in libraryImpl as EntityStore's current was already changed
|
||||
dispatcher.multicaster.afterLibraryRemoved(library)
|
||||
Disposer.dispose(library)
|
||||
}
|
||||
}
|
||||
is EntityChange.Replaced -> {
|
||||
val idBefore = change.oldEntity.symbolicId
|
||||
val idAfter = change.newEntity.symbolicId
|
||||
|
||||
if (idBefore != idAfter) {
|
||||
val library = event.storageBefore.libraryMap.getDataByEntity(change.oldEntity) as? LibraryBridgeImpl
|
||||
if (library != null) {
|
||||
library.entityId = idAfter
|
||||
dispatcher.multicaster.afterLibraryRenamed(library, LibraryNameGenerator.getLegacyLibraryName(idBefore))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fireRootSetChanged(libraryEntity: LibraryEntity, entityStorage: EntityStorage) {
|
||||
(entityStorage.libraryMap.getDataByEntity(libraryEntity) as? LibraryBridgeImpl)?.fireRootSetChanged()
|
||||
}
|
||||
|
||||
internal fun createLibrary(name: String?): Library {
|
||||
if (name == null) error("Creating unnamed ${libraryTableId.level} libraries is unsupported")
|
||||
|
||||
if (getLibraryByName(name) != null) {
|
||||
error("${libraryTableId.level} library named $name already exists")
|
||||
}
|
||||
|
||||
val modifiableModel = libraryTable.modifiableModel
|
||||
modifiableModel.createLibrary(name)
|
||||
modifiableModel.commit()
|
||||
|
||||
val newLibrary = getLibraryByName(name)
|
||||
if (newLibrary == null) {
|
||||
error("Library $name was not created")
|
||||
}
|
||||
|
||||
return newLibrary
|
||||
}
|
||||
|
||||
internal fun removeLibrary(library: Library) {
|
||||
val modifiableModel = libraryTable.modifiableModel
|
||||
modifiableModel.removeLibrary(library)
|
||||
modifiableModel.commit()
|
||||
}
|
||||
|
||||
internal fun getLibraries(): Array<Library> {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
val storage = entityStorage.current
|
||||
val libraryEntitySequence = storage.entities(LibraryEntity::class.java).filter { it.tableId == libraryTableId }.toList()
|
||||
val libs: Array<Library> = libraryEntitySequence.mapNotNull { storage.libraryMap.getDataByEntity(it) }
|
||||
.toList().toTypedArray()
|
||||
return libs
|
||||
}
|
||||
|
||||
internal fun getLibraryByName(name: String): Library? {
|
||||
val entityStorage = GlobalWorkspaceModel.getInstance().entityStorage
|
||||
val libraryId = LibraryId(name, libraryTableId)
|
||||
val library = entityStorage.current.resolve(libraryId)?.let { entity ->
|
||||
entityStorage.current.libraryMap.getDataByEntity(entity)
|
||||
}
|
||||
return library
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
for (library in getLibraries()) {
|
||||
Disposer.dispose(library)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun addListener(listener: LibraryTable.Listener) = dispatcher.addListener(listener)
|
||||
|
||||
internal fun addListener(listener: LibraryTable.Listener, parentDisposable: Disposable) {
|
||||
dispatcher.addListener(listener, parentDisposable)
|
||||
}
|
||||
|
||||
internal fun removeListener(listener: LibraryTable.Listener) = dispatcher.removeListener(listener)
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<GlobalLibraryTableDelegate>()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<EntityChange<LibraryEntity>>.filterLibraryChanges(libraryTableId: LibraryTableId): List<EntityChange<LibraryEntity>> {
|
||||
return filter {
|
||||
val tableId = when (it) {
|
||||
is EntityChange.Added -> it.entity.tableId
|
||||
is EntityChange.Removed -> it.entity.tableId
|
||||
is EntityChange.Replaced -> it.oldEntity.tableId
|
||||
}
|
||||
tableId == libraryTableId
|
||||
}
|
||||
}
|
||||
+7
-9
@@ -4,7 +4,6 @@ package com.intellij.workspaceModel.ide.impl.legacyBridge.library
|
||||
import com.intellij.openapi.roots.ProjectModelExternalSource
|
||||
import com.intellij.openapi.roots.libraries.Library
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
|
||||
import com.intellij.openapi.roots.libraries.PersistentLibraryKind
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.workspace.jps.entities.LibraryEntity
|
||||
@@ -20,10 +19,12 @@ import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryT
|
||||
import com.intellij.workspaceModel.ide.impl.legacyBridge.library.ProjectLibraryTableBridgeImpl.Companion.mutableLibraryMap
|
||||
import org.jetbrains.jps.model.serialization.library.JpsLibraryTableSerializer
|
||||
|
||||
internal class GlobalModifiableLibraryTableBridgeImpl(private val libraryTable: LibraryTable):
|
||||
internal class GlobalOrCustomModifiableLibraryTableBridgeImpl(private val libraryTable: LibraryTable):
|
||||
LegacyBridgeModifiableBase(MutableEntityStorage.from(GlobalWorkspaceModel.getInstance().currentSnapshot), true),
|
||||
LibraryTable.ModifiableModel {
|
||||
|
||||
private val myAddedLibraries = mutableListOf<LibraryBridgeImpl>()
|
||||
private val libraryTableId = LibraryTableId.GlobalLibraryTableId(libraryTable.tableLevel)
|
||||
|
||||
override fun createLibrary(name: String?): Library {
|
||||
return createLibrary(name = name, type = null)
|
||||
@@ -34,11 +35,9 @@ internal class GlobalModifiableLibraryTableBridgeImpl(private val libraryTable:
|
||||
}
|
||||
|
||||
override fun createLibrary(name: String?, type: PersistentLibraryKind<*>?, externalSource: ProjectModelExternalSource?): Library {
|
||||
if (name.isNullOrBlank()) error("Application Library must have a name")
|
||||
if (name.isNullOrBlank()) error("${libraryTableId.level} library must have a name")
|
||||
assertModelIsLive()
|
||||
|
||||
val libraryTableId = LibraryTableId.GlobalLibraryTableId(LibraryTablesRegistrar.APPLICATION_LEVEL)
|
||||
|
||||
val libraryEntity = diff addEntity LibraryEntity(name, libraryTableId, emptyList(),
|
||||
LegacyBridgeJpsEntitySourceFactory.createEntitySourceForGlobalLibrary())
|
||||
|
||||
@@ -77,7 +76,7 @@ internal class GlobalModifiableLibraryTableBridgeImpl(private val libraryTable:
|
||||
}
|
||||
|
||||
override fun commit() {
|
||||
GlobalWorkspaceModel.getInstance().updateModel("Global library table commit") {
|
||||
GlobalWorkspaceModel.getInstance().updateModel("${libraryTableId.level} library table commit") {
|
||||
it.addDiff(diff)
|
||||
}
|
||||
libraries.forEach { library -> (library as LibraryBridgeImpl).clearTargetBuilder() }
|
||||
@@ -86,19 +85,18 @@ internal class GlobalModifiableLibraryTableBridgeImpl(private val libraryTable:
|
||||
override fun getLibraryIterator(): Iterator<Library> = libraries.iterator()
|
||||
|
||||
override fun getLibraryByName(name: String): Library? {
|
||||
val libraryEntity = diff.resolve(LibraryId(name, LibraryTableId.GlobalLibraryTableId(LibraryTablesRegistrar.APPLICATION_LEVEL))) ?: return null
|
||||
val libraryEntity = diff.resolve(LibraryId(name, libraryTableId)) ?: return null
|
||||
return diff.libraryMap.getDataByEntity(libraryEntity)
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<Library> {
|
||||
return diff.entities(LibraryEntity::class.java).filter { it.tableId::class == LibraryTableId.GlobalLibraryTableId::class }
|
||||
return diff.entities(LibraryEntity::class.java).filter { it.tableId == libraryTableId }
|
||||
.mapNotNull { diff.libraryMap.getDataByEntity(it) }
|
||||
.toList().toTypedArray()
|
||||
}
|
||||
|
||||
override fun isChanged(): Boolean = diff.hasChanges()
|
||||
|
||||
|
||||
override fun dispose() {
|
||||
modelIsCommittedOrDisposed = true
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.workspaceModel.ide.legacyBridge
|
||||
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface CustomLibraryTableBridge: GlobalEntityBridgeAndEventHandler, LibraryTable {
|
||||
companion object {
|
||||
fun isEnabled(): Boolean = Registry.`is`("workspace.model.custom.library.bridge", true)
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.workspaceModel.ide.legacyBridge
|
||||
|
||||
import com.intellij.openapi.roots.impl.libraries.CustomLibraryTableImpl
|
||||
import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar
|
||||
import com.intellij.platform.workspace.storage.EntityChange
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedStorageChange
|
||||
|
||||
interface GlobalEntityBridgeAndEventHandler {
|
||||
fun initializeBridgesAfterLoading(mutableStorage: MutableEntityStorage, initialEntityStorage: VersionedEntityStorage): () -> Unit
|
||||
fun initializeBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage)
|
||||
fun handleBeforeChangeEvents(event: VersionedStorageChange)
|
||||
fun handleChangedEvents(event: VersionedStorageChange)
|
||||
|
||||
companion object {
|
||||
fun getAllGlobalEntityHandlers(): List<GlobalEntityBridgeAndEventHandler> {
|
||||
val result = mutableListOf<GlobalEntityBridgeAndEventHandler>()
|
||||
result.add(GlobalLibraryTableBridge.getInstance())
|
||||
result.add(GlobalSdkTableBridge.getInstance())
|
||||
if (CustomLibraryTableBridge.isEnabled()) {
|
||||
LibraryTablesRegistrar.getInstance().customLibraryTables.forEach { customLibraryTable ->
|
||||
customLibraryTable as CustomLibraryTableImpl
|
||||
result.add(customLibraryTable.getDelegate() as CustomLibraryTableBridge)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-9
@@ -4,21 +4,13 @@ package com.intellij.workspaceModel.ide.legacyBridge
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.roots.libraries.LibraryTable
|
||||
import com.intellij.platform.workspace.storage.EntityChange
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedStorageChange
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
/**
|
||||
* Utility interface to provide bridge behaviour from entities to [com.intellij.openapi.roots.libraries.Library]
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
interface GlobalLibraryTableBridge : LibraryTable {
|
||||
fun initializeLibraryBridgesAfterLoading(mutableStorage: MutableEntityStorage, initialEntityStorage: VersionedEntityStorage): () -> Unit
|
||||
fun initializeLibraryBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage)
|
||||
fun handleBeforeChangeEvents(event: VersionedStorageChange)
|
||||
fun handleChangedEvents(event: VersionedStorageChange)
|
||||
interface GlobalLibraryTableBridge : GlobalEntityBridgeAndEventHandler, LibraryTable {
|
||||
companion object {
|
||||
fun getInstance(): GlobalLibraryTableBridge = ApplicationManager.getApplication().service()
|
||||
}
|
||||
|
||||
+3
-11
@@ -4,18 +4,10 @@ package com.intellij.workspaceModel.ide.legacyBridge
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.platform.workspace.storage.EntityChange
|
||||
import com.intellij.platform.workspace.storage.MutableEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedEntityStorage
|
||||
import com.intellij.platform.workspace.storage.VersionedStorageChange
|
||||
|
||||
interface GlobalSdkTableBridge {
|
||||
fun initializeSdkBridgesAfterLoading(mutableStorage: MutableEntityStorage,
|
||||
initialEntityStorage: VersionedEntityStorage): () -> Unit
|
||||
fun initializeSdkBridges(changes: Map<Class<*>, List<EntityChange<*>>>, builder: MutableEntityStorage)
|
||||
fun handleBeforeChangeEvents(event: VersionedStorageChange)
|
||||
fun handleChangedEvents(event: VersionedStorageChange)
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface GlobalSdkTableBridge: GlobalEntityBridgeAndEventHandler {
|
||||
companion object {
|
||||
fun getInstance(): GlobalSdkTableBridge = ApplicationManager.getApplication().service()
|
||||
fun isEnabled(): Boolean = Registry.`is`("workspace.model.global.sdk.bridge", true)
|
||||
|
||||
+115
-118
@@ -72,8 +72,10 @@ internal class JpsGlobalLibrariesFileSerializer(entitySource: JpsGlobalFileEntit
|
||||
JpsFileEntityTypeSerializer<LibraryEntity> {
|
||||
override val isExternalStorage: Boolean
|
||||
get() = false
|
||||
|
||||
/* Working only with global libraries omitting the custom one */
|
||||
override val entityFilter: (LibraryEntity) -> Boolean
|
||||
get() = { it.tableId is LibraryTableId.GlobalLibraryTableId }
|
||||
get() = { it.tableId == libraryTableId }
|
||||
|
||||
override fun deleteObsoleteFile(fileUrl: String, writer: JpsFileContentWriter) {
|
||||
writer.saveComponent(fileUrl, LIBRARY_TABLE_COMPONENT_NAME, null)
|
||||
@@ -122,7 +124,7 @@ internal class JpsLibrariesExternalFileSerializer(private val externalFile: JpsP
|
||||
}
|
||||
}
|
||||
|
||||
internal open class JpsLibraryEntitiesSerializer(override val fileUrl: VirtualFileUrl,
|
||||
open class JpsLibraryEntitiesSerializer(override val fileUrl: VirtualFileUrl,
|
||||
override val internalEntitySource: JpsFileEntitySource,
|
||||
protected val libraryTableId: LibraryTableId) : JpsFileEntitiesSerializer<LibraryEntity> {
|
||||
open val isExternalStorage: Boolean
|
||||
@@ -198,6 +200,117 @@ internal open class JpsLibraryEntitiesSerializer(override val fileUrl: VirtualFi
|
||||
override fun toString(): String = "${javaClass.simpleName.substringAfterLast('.')}($fileUrl)"
|
||||
|
||||
companion object {
|
||||
fun saveLibrary(library: LibraryEntity, externalSystemId: String?, isExternalStorage: Boolean): Element {
|
||||
val libraryTag = Element(LIBRARY_TAG)
|
||||
val legacyName = LibraryNameGenerator.getLegacyLibraryName(library.symbolicId)
|
||||
if (legacyName != null) {
|
||||
libraryTag.setAttribute(NAME_ATTRIBUTE, legacyName)
|
||||
}
|
||||
val customProperties = library.libraryProperties
|
||||
if (customProperties != null) {
|
||||
libraryTag.setAttribute(TYPE_ATTRIBUTE, customProperties.libraryType)
|
||||
val propertiesXmlTag = customProperties.propertiesXmlTag
|
||||
if (propertiesXmlTag != null) {
|
||||
libraryTag.addContent(JDOMUtil.load(propertiesXmlTag))
|
||||
}
|
||||
}
|
||||
if (externalSystemId != null) {
|
||||
val attributeName =
|
||||
if (isExternalStorage) SerializationConstants.EXTERNAL_SYSTEM_ID_ATTRIBUTE
|
||||
else SerializationConstants.EXTERNAL_SYSTEM_ID_IN_INTERNAL_STORAGE_ATTRIBUTE
|
||||
libraryTag.setAttribute(attributeName, externalSystemId)
|
||||
}
|
||||
val rootsMap = library.roots.groupByTo(HashMap()) { it.type }
|
||||
ROOT_TYPES_TO_WRITE_EMPTY_TAG.forEach {
|
||||
rootsMap.putIfAbsent(it, ArrayList())
|
||||
}
|
||||
val jarDirectoriesTags = ArrayList<Element>()
|
||||
rootsMap.entries.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.key.name }).forEach { (rootType, roots) ->
|
||||
val rootTypeTag = Element(rootType.name)
|
||||
roots.forEach {
|
||||
rootTypeTag.addContent(Element(ROOT_TAG).setAttribute(JpsModuleRootModelSerializer.URL_ATTRIBUTE, it.url.url))
|
||||
}
|
||||
roots.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.url.url }).forEach {
|
||||
if (it.inclusionOptions != LibraryRoot.InclusionOptions.ROOT_ITSELF) {
|
||||
val jarDirectoryTag = Element(JAR_DIRECTORY_TAG)
|
||||
jarDirectoryTag.setAttribute(JpsModuleRootModelSerializer.URL_ATTRIBUTE, it.url.url)
|
||||
jarDirectoryTag.setAttribute(RECURSIVE_ATTRIBUTE,
|
||||
(it.inclusionOptions == LibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT_RECURSIVELY).toString())
|
||||
if (rootType.name != DEFAULT_JAR_DIRECTORY_TYPE) {
|
||||
jarDirectoryTag.setAttribute(TYPE_ATTRIBUTE, rootType.name)
|
||||
}
|
||||
jarDirectoriesTags.add(jarDirectoryTag)
|
||||
}
|
||||
}
|
||||
libraryTag.addContent(rootTypeTag)
|
||||
}
|
||||
val excludedRoots = library.excludedRoots
|
||||
if (excludedRoots.isNotEmpty()) {
|
||||
val excludedTag = Element("excluded")
|
||||
excludedRoots.forEach {
|
||||
excludedTag.addContent(Element(ROOT_TAG).setAttribute(JpsModuleRootModelSerializer.URL_ATTRIBUTE, it.url.url))
|
||||
}
|
||||
libraryTag.addContent(excludedTag)
|
||||
}
|
||||
jarDirectoriesTags.forEach {
|
||||
libraryTag.addContent(it)
|
||||
}
|
||||
return libraryTag
|
||||
}
|
||||
|
||||
fun loadLibrary(name: String, libraryElement: Element, libraryTableId: LibraryTableId, source: EntitySource,
|
||||
virtualFileManager: VirtualFileUrlManager): LibraryEntity {
|
||||
val roots = ArrayList<LibraryRoot>()
|
||||
val excludedRoots = ArrayList<VirtualFileUrl>()
|
||||
val jarDirectories = libraryElement.getChildren(JAR_DIRECTORY_TAG).associateBy(
|
||||
{
|
||||
Pair(it.getAttributeValue(JpsModuleRootModelSerializer.TYPE_ATTRIBUTE) ?: DEFAULT_JAR_DIRECTORY_TYPE,
|
||||
it.getAttributeValueStrict(JpsModuleRootModelSerializer.URL_ATTRIBUTE))
|
||||
},
|
||||
{
|
||||
if (it.getAttributeValue(RECURSIVE_ATTRIBUTE)?.toBoolean() == true) LibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT_RECURSIVELY
|
||||
else LibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT
|
||||
}
|
||||
)
|
||||
|
||||
val type = libraryElement.getAttributeValue("type")
|
||||
var properties: String? = null
|
||||
for (childElement in libraryElement.children) {
|
||||
when (childElement.name) {
|
||||
"excluded" -> excludedRoots.addAll(
|
||||
childElement.getChildren(JpsJavaModelSerializerExtension.ROOT_TAG)
|
||||
.map { it.getAttributeValueStrict(JpsModuleRootModelSerializer.URL_ATTRIBUTE) }
|
||||
.map { virtualFileManager.fromUrl(it) }
|
||||
)
|
||||
PROPERTIES_TAG -> {
|
||||
properties = JDOMUtil.write(childElement)
|
||||
}
|
||||
JAR_DIRECTORY_TAG -> {
|
||||
}
|
||||
else -> {
|
||||
val rootType = childElement.name
|
||||
for (rootTag in childElement.getChildren(JpsJavaModelSerializerExtension.ROOT_TAG)) {
|
||||
val url = rootTag.getAttributeValueStrict(JpsModuleRootModelSerializer.URL_ATTRIBUTE)
|
||||
val inclusionOptions = jarDirectories[Pair(rootType, url)] ?: LibraryRoot.InclusionOptions.ROOT_ITSELF
|
||||
roots.add(LibraryRoot(virtualFileManager.fromUrl(url), libraryRootTypes[rootType]!!, inclusionOptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val libProperties = type?.let {
|
||||
LibraryPropertiesEntity(type, source) {
|
||||
this.propertiesXmlTag = properties
|
||||
}
|
||||
}
|
||||
val excludes = excludedRoots.map { ExcludeUrlEntity(it, source) }
|
||||
val libraryEntity = LibraryEntity(name, libraryTableId, roots, source) {
|
||||
this.excludedRoots = excludes
|
||||
this.libraryProperties = libProperties
|
||||
}
|
||||
|
||||
return libraryEntity
|
||||
}
|
||||
|
||||
private val loadEntitiesTimeMs: AtomicLong = AtomicLong()
|
||||
private val saveEntitiesTimeMs: AtomicLong = AtomicLong()
|
||||
|
||||
@@ -221,122 +334,6 @@ internal open class JpsLibraryEntitiesSerializer(override val fileUrl: VirtualFi
|
||||
}
|
||||
|
||||
private const val DEFAULT_JAR_DIRECTORY_TYPE = "CLASSES"
|
||||
|
||||
internal fun loadLibrary(name: String,
|
||||
libraryElement: Element,
|
||||
libraryTableId: LibraryTableId,
|
||||
source: EntitySource,
|
||||
virtualFileManager: VirtualFileUrlManager): LibraryEntity {
|
||||
val roots = ArrayList<LibraryRoot>()
|
||||
val excludedRoots = ArrayList<VirtualFileUrl>()
|
||||
val jarDirectories = libraryElement.getChildren(JAR_DIRECTORY_TAG).associateBy(
|
||||
{
|
||||
Pair(it.getAttributeValue(JpsModuleRootModelSerializer.TYPE_ATTRIBUTE) ?: DEFAULT_JAR_DIRECTORY_TYPE,
|
||||
it.getAttributeValueStrict(JpsModuleRootModelSerializer.URL_ATTRIBUTE))
|
||||
},
|
||||
{
|
||||
if (it.getAttributeValue(RECURSIVE_ATTRIBUTE)?.toBoolean() == true) LibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT_RECURSIVELY
|
||||
else LibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT
|
||||
}
|
||||
)
|
||||
|
||||
val type = libraryElement.getAttributeValue("type")
|
||||
var properties: String? = null
|
||||
for (childElement in libraryElement.children) {
|
||||
when (childElement.name) {
|
||||
"excluded" -> excludedRoots.addAll(
|
||||
childElement.getChildren(JpsJavaModelSerializerExtension.ROOT_TAG)
|
||||
.map { it.getAttributeValueStrict(JpsModuleRootModelSerializer.URL_ATTRIBUTE) }
|
||||
.map { virtualFileManager.fromUrl(it) }
|
||||
)
|
||||
PROPERTIES_TAG -> {
|
||||
properties = JDOMUtil.write(childElement)
|
||||
}
|
||||
JAR_DIRECTORY_TAG -> {
|
||||
}
|
||||
else -> {
|
||||
val rootType = childElement.name
|
||||
for (rootTag in childElement.getChildren(JpsJavaModelSerializerExtension.ROOT_TAG)) {
|
||||
val url = rootTag.getAttributeValueStrict(JpsModuleRootModelSerializer.URL_ATTRIBUTE)
|
||||
val inclusionOptions = jarDirectories[Pair(rootType, url)] ?: LibraryRoot.InclusionOptions.ROOT_ITSELF
|
||||
roots.add(LibraryRoot(virtualFileManager.fromUrl(url), libraryRootTypes[rootType]!!, inclusionOptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val libProperties = type?.let {
|
||||
LibraryPropertiesEntity(type, source) {
|
||||
this.propertiesXmlTag = properties
|
||||
}
|
||||
}
|
||||
val excludes = excludedRoots.map { ExcludeUrlEntity(it, source) }
|
||||
val libraryEntity = LibraryEntity(name, libraryTableId, roots, source) {
|
||||
this.excludedRoots = excludes
|
||||
this.libraryProperties = libProperties
|
||||
}
|
||||
|
||||
return libraryEntity
|
||||
}
|
||||
|
||||
private val libraryRootTypes = ConcurrentFactoryMap.createMap<String, LibraryRootTypeId> { LibraryRootTypeId(it) }
|
||||
|
||||
internal fun saveLibrary(library: LibraryEntity, externalSystemId: String?, isExternalStorage: Boolean): Element {
|
||||
val libraryTag = Element(LIBRARY_TAG)
|
||||
val legacyName = LibraryNameGenerator.getLegacyLibraryName(library.symbolicId)
|
||||
if (legacyName != null) {
|
||||
libraryTag.setAttribute(NAME_ATTRIBUTE, legacyName)
|
||||
}
|
||||
val customProperties = library.libraryProperties
|
||||
if (customProperties != null) {
|
||||
libraryTag.setAttribute(TYPE_ATTRIBUTE, customProperties.libraryType)
|
||||
val propertiesXmlTag = customProperties.propertiesXmlTag
|
||||
if (propertiesXmlTag != null) {
|
||||
libraryTag.addContent(JDOMUtil.load(propertiesXmlTag))
|
||||
}
|
||||
}
|
||||
if (externalSystemId != null) {
|
||||
val attributeName =
|
||||
if (isExternalStorage) SerializationConstants.EXTERNAL_SYSTEM_ID_ATTRIBUTE
|
||||
else SerializationConstants.EXTERNAL_SYSTEM_ID_IN_INTERNAL_STORAGE_ATTRIBUTE
|
||||
libraryTag.setAttribute(attributeName, externalSystemId)
|
||||
}
|
||||
val rootsMap = library.roots.groupByTo(HashMap()) { it.type }
|
||||
ROOT_TYPES_TO_WRITE_EMPTY_TAG.forEach {
|
||||
rootsMap.putIfAbsent(it, ArrayList())
|
||||
}
|
||||
val jarDirectoriesTags = ArrayList<Element>()
|
||||
rootsMap.entries.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.key.name }).forEach { (rootType, roots) ->
|
||||
val rootTypeTag = Element(rootType.name)
|
||||
roots.forEach {
|
||||
rootTypeTag.addContent(Element(ROOT_TAG).setAttribute(JpsModuleRootModelSerializer.URL_ATTRIBUTE, it.url.url))
|
||||
}
|
||||
roots.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.url.url }).forEach {
|
||||
if (it.inclusionOptions != LibraryRoot.InclusionOptions.ROOT_ITSELF) {
|
||||
val jarDirectoryTag = Element(JAR_DIRECTORY_TAG)
|
||||
jarDirectoryTag.setAttribute(JpsModuleRootModelSerializer.URL_ATTRIBUTE, it.url.url)
|
||||
jarDirectoryTag.setAttribute(RECURSIVE_ATTRIBUTE,
|
||||
(it.inclusionOptions == LibraryRoot.InclusionOptions.ARCHIVES_UNDER_ROOT_RECURSIVELY).toString())
|
||||
if (rootType.name != DEFAULT_JAR_DIRECTORY_TYPE) {
|
||||
jarDirectoryTag.setAttribute(TYPE_ATTRIBUTE, rootType.name)
|
||||
}
|
||||
jarDirectoriesTags.add(jarDirectoryTag)
|
||||
}
|
||||
}
|
||||
libraryTag.addContent(rootTypeTag)
|
||||
}
|
||||
val excludedRoots = library.excludedRoots
|
||||
if (excludedRoots.isNotEmpty()) {
|
||||
val excludedTag = Element("excluded")
|
||||
excludedRoots.forEach {
|
||||
excludedTag.addContent(Element(ROOT_TAG).setAttribute(JpsModuleRootModelSerializer.URL_ATTRIBUTE, it.url.url))
|
||||
}
|
||||
libraryTag.addContent(excludedTag)
|
||||
}
|
||||
jarDirectoriesTags.forEach {
|
||||
libraryTag.addContent(it)
|
||||
}
|
||||
return libraryTag
|
||||
}
|
||||
|
||||
private val ROOT_TYPES_TO_WRITE_EMPTY_TAG = listOf("CLASSES", "SOURCES", "JAVADOC").map { libraryRootTypes[it]!! }
|
||||
|
||||
|
||||
+2
-2
@@ -442,7 +442,7 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod
|
||||
val name = LibraryNameGenerator.generateUniqueLibraryName(originalName) { it in moduleLibraryNames }
|
||||
moduleLibraryNames.add(name)
|
||||
val tableId = LibraryTableId.ModuleLibraryTableId(moduleId)
|
||||
val library = loadLibrary(name, libraryElement, tableId, contentRootEntitySource, virtualFileManager)
|
||||
val library = JpsLibraryEntitiesSerializer.loadLibrary(name, libraryElement, tableId, contentRootEntitySource, virtualFileManager)
|
||||
val libraryId = LibraryId(name, tableId)
|
||||
moduleLibrariesCollector[libraryId] = library
|
||||
ModuleDependencyItem.Exportable.LibraryDependency(libraryId, dependencyElement.isExported(), dependencyElement.readScope())
|
||||
@@ -816,7 +816,7 @@ internal open class ModuleImlFileEntitiesSerializer(internal val modulePath: Mod
|
||||
if (moduleLibrary != null) {
|
||||
createOrderEntryTag(MODULE_LIBRARY_TYPE).apply {
|
||||
setExportedAndScopeAttributes(dependencyItem)
|
||||
addContent(saveLibrary(moduleLibrary, null, false))
|
||||
addContent(JpsLibraryEntitiesSerializer.saveLibrary(moduleLibrary, null, false))
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
Reference in New Issue
Block a user