mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IJPL-136 pass plugin id to PSC proxy
GitOrigin-RevId: ceacd58fb3bf6c8a76e68e1328814de5814d2826
This commit is contained in:
committed by
intellij-monorepo-bot
parent
26ec54adf8
commit
353d2e563f
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.components.*
|
||||
@@ -51,36 +52,45 @@ internal class AppStorageContentReader : JpsFileContentReader {
|
||||
}
|
||||
|
||||
internal class AppStorageContentWriter(private val session: SaveSessionProducerManager) : JpsAppFileContentWriter {
|
||||
override fun saveComponent(fileUrl: String, componentName: String, componentTag: Element?): Unit = saveComponentTimeMs.addMeasuredTime {
|
||||
val filePath = JpsPathUtil.urlToPath(fileUrl)
|
||||
if (isApplicationLevelFile(filePath)) {
|
||||
val storageSpec = FileStorageAnnotation(PathUtil.getFileName(filePath), false, StateSplitterEx::class.java)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val storage = ApplicationManager.getApplication().stateStore.storageManager.getStateStorage(storageSpec) as StateStorageBase<StateMap>
|
||||
session.getProducer(storage)?.setState(null, componentName, componentTag)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getReplacePathMacroMap(fileUrl: String): PathMacroMap =
|
||||
PathMacroManager.getInstance(ApplicationManager.getApplication()).replacePathMap
|
||||
|
||||
override suspend fun saveSession() {
|
||||
session.save(SaveResult())
|
||||
}
|
||||
|
||||
private fun isApplicationLevelFile(filePath: String): Boolean =
|
||||
Path.of(filePath).startsWith(Path.of(PathManager.getOptionsPath()))
|
||||
|
||||
companion object {
|
||||
private val saveComponentTimeMs = MillisecondsMeasurer()
|
||||
|
||||
init {
|
||||
setupOpenTelemetryReporting(jpsMetrics.meter)
|
||||
}
|
||||
|
||||
private fun setupOpenTelemetryReporting(meter: Meter) {
|
||||
val saveComponentTimeCounter = meter.counterBuilder("jps.app.storage.content.writer.save.component.ms").buildObserver()
|
||||
meter.batchCallback({ saveComponentTimeCounter.record(saveComponentTimeMs.asMilliseconds()) }, saveComponentTimeCounter)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setupOpenTelemetryReporting(jpsMetrics.meter)
|
||||
override fun saveComponent(fileUrl: String, componentName: String, componentTag: Element?) {
|
||||
saveComponentTimeMs.addMeasuredTime {
|
||||
val filePath = JpsPathUtil.urlToPath(fileUrl)
|
||||
if (isApplicationLevelFile(filePath)) {
|
||||
val storageSpec = FileStorageAnnotation(PathUtil.getFileName(filePath), false, StateSplitterEx::class.java)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val storage = ApplicationManager.getApplication().stateStore.storageManager.getStateStorage(storageSpec) as StateStorageBase<StateMap>
|
||||
session.getProducer(storage)?.setState(
|
||||
component = null,
|
||||
componentName = componentName,
|
||||
// doesn't matter for now
|
||||
pluginId = PluginManagerCore.CORE_ID,
|
||||
state = componentTag,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getReplacePathMacroMap(fileUrl: String): PathMacroMap {
|
||||
return PathMacroManager.getInstance(ApplicationManager.getApplication()).replacePathMap
|
||||
}
|
||||
|
||||
override suspend fun saveSession() {
|
||||
session.save(SaveResult())
|
||||
}
|
||||
|
||||
private fun isApplicationLevelFile(filePath: String): Boolean = Path.of(filePath).startsWith(Path.of(PathManager.getOptionsPath()))
|
||||
}
|
||||
|
||||
@@ -321,16 +321,19 @@ abstract class ComponentStoreImpl : IComponentStore {
|
||||
val stateSpec = getStateSpec(component)
|
||||
LOG.debug { "saveComponent is called for ${stateSpec.name}" }
|
||||
val saveManager = createSaveSessionProducerManager()
|
||||
val storages = getStorageSpecs(component, stateSpec, StateStorageOperation.WRITE)
|
||||
val storages = getStorageSpecs(component = component, stateSpec = stateSpec, operation = StateStorageOperation.WRITE)
|
||||
val storage = storages.firstOrNull { !it.deprecated } ?: throw AssertionError("All storages are deprecated")
|
||||
val absolutePath = storageManager.expandMacro(storage.path).toString()
|
||||
|
||||
val componentInfo = components.get(stateSpec.name)
|
||||
|
||||
Disposer.newDisposable().use {
|
||||
VfsRootAccess.allowRootAccess(it, absolutePath)
|
||||
@Suppress("DEPRECATION")
|
||||
runUnderModalProgressIfIsEdt {
|
||||
commitComponent(
|
||||
sessionManager = saveManager,
|
||||
info = ComponentInfoImpl(
|
||||
info = componentInfo ?: ComponentInfoImpl(
|
||||
pluginId = PluginManager.getPluginByClass(component::class.java)?.pluginId ?: PluginManagerCore.CORE_ID,
|
||||
component = component,
|
||||
stateSpec = stateSpec,
|
||||
@@ -371,7 +374,12 @@ abstract class ComponentStoreImpl : IComponentStore {
|
||||
if (component is com.intellij.openapi.util.JDOMExternalizable) {
|
||||
val effectiveComponentName = componentName ?: getComponentName(component)
|
||||
storageManager.getOldStorage(component, effectiveComponentName, StateStorageOperation.WRITE)?.let {
|
||||
sessionManager.getProducer(it)?.setState(component = component, componentName = effectiveComponentName, state = component)
|
||||
sessionManager.getProducer(it)?.setState(
|
||||
component = component,
|
||||
componentName = effectiveComponentName,
|
||||
pluginId = info.pluginId,
|
||||
state = component,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -385,7 +393,11 @@ abstract class ComponentStoreImpl : IComponentStore {
|
||||
val stateStorageChooser = component as? StateStorageChooserEx
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val storageSpecs = getStorageSpecs(component as PersistentStateComponent<Any>, stateSpec, StateStorageOperation.WRITE)
|
||||
val storageSpecs = getStorageSpecs(
|
||||
component = component as PersistentStateComponent<Any>,
|
||||
stateSpec = stateSpec,
|
||||
operation = StateStorageOperation.WRITE,
|
||||
)
|
||||
for (storageSpec in storageSpecs) {
|
||||
var resolution = stateStorageChooser?.getResolution(storageSpec, StateStorageOperation.WRITE) ?: Resolution.DO
|
||||
if (resolution == Resolution.SKIP) {
|
||||
@@ -404,7 +416,7 @@ abstract class ComponentStoreImpl : IComponentStore {
|
||||
val sessionProducer = sessionManager.getProducer(storage) ?: continue
|
||||
if (resolution == Resolution.CLEAR ||
|
||||
(storageSpec.deprecated && storageSpecs.none { !it.deprecated && it.value == storageSpec.value })) {
|
||||
sessionProducer.setState(component = component, componentName = effectiveComponentName, state = null)
|
||||
sessionProducer.setState(component = component, componentName = effectiveComponentName, pluginId = info.pluginId, state = null)
|
||||
}
|
||||
else {
|
||||
if (!stateRequested) {
|
||||
@@ -428,11 +440,13 @@ abstract class ComponentStoreImpl : IComponentStore {
|
||||
|
||||
// method is not called if storage is deprecated or clear was requested
|
||||
// (state in these cases is null), but called if state is null if returned so from a component
|
||||
protected open fun setStateToSaveSessionProducer(state: Any?,
|
||||
info: ComponentInfo,
|
||||
effectiveComponentName: String,
|
||||
sessionProducer: SaveSessionProducer) {
|
||||
sessionProducer.setState(component = info.component, componentName = effectiveComponentName, state = state)
|
||||
protected open fun setStateToSaveSessionProducer(
|
||||
state: Any?,
|
||||
info: ComponentInfo,
|
||||
effectiveComponentName: String,
|
||||
sessionProducer: SaveSessionProducer,
|
||||
) {
|
||||
sessionProducer.setState(component = info.component, componentName = effectiveComponentName, pluginId = info.pluginId, state = state)
|
||||
}
|
||||
|
||||
private fun registerComponent(name: String, info: ComponentInfo): ComponentInfo {
|
||||
|
||||
@@ -96,16 +96,21 @@ abstract class ComponentStoreWithExtraComponents : ComponentStoreImpl() {
|
||||
}
|
||||
|
||||
internal open fun commitObsoleteComponents(session: SaveSessionProducerManager, isProjectLevel: Boolean) {
|
||||
for (bean in ObsoleteStorageBean.EP_NAME.lazySequence()) {
|
||||
val storageManager = storageManager as? StateStorageManagerImpl ?: return
|
||||
for (item in ObsoleteStorageBean.EP_NAME.filterableLazySequence()) {
|
||||
val bean = item.instance ?: continue
|
||||
if (bean.isProjectLevel != isProjectLevel) {
|
||||
continue
|
||||
}
|
||||
|
||||
val storage = (storageManager as? StateStorageManagerImpl)?.getOrCreateStorage(bean.file ?: continue, RoamingType.DISABLED)
|
||||
if (storage != null) {
|
||||
for (componentName in bean.components) {
|
||||
session.getProducer(storage)?.setState(component = null, componentName = componentName, state = null)
|
||||
}
|
||||
val storage = storageManager.getOrCreateStorage(collapsedPath = bean.file ?: continue, roamingType = RoamingType.DISABLED)
|
||||
for (componentName in bean.components) {
|
||||
session.getProducer(storage)?.setState(
|
||||
component = null,
|
||||
componentName = componentName,
|
||||
pluginId = item.pluginDescriptor.pluginId,
|
||||
state = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:Suppress("ReplaceGetOrSet")
|
||||
@file:Suppress("ReplaceGetOrSet", "ReplacePutWithAssignment")
|
||||
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.concurrency.ConcurrentCollectionFactory
|
||||
import com.intellij.ide.impl.ProjectUtil
|
||||
import com.intellij.ide.impl.isTrusted
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.components.impl.ModulePathMacroManager
|
||||
import com.intellij.openapi.components.impl.ProjectPathMacroManager
|
||||
@@ -75,26 +76,35 @@ open class ProjectWithModuleStoreImpl(project: Project) : ProjectStoreImpl(proje
|
||||
StorageJpsConfigurationReader(project, getJpsProjectConfigLocation(project)!!)
|
||||
}
|
||||
|
||||
private class JpsStorageContentWriter(private val session: ProjectWithModulesSaveSessionProducerManager,
|
||||
private val store: IProjectStore,
|
||||
private val project: Project) : JpsFileContentWriter {
|
||||
private class JpsStorageContentWriter(
|
||||
private val session: ProjectWithModulesSaveSessionProducerManager,
|
||||
private val store: IProjectStore,
|
||||
private val project: Project,
|
||||
) : JpsFileContentWriter {
|
||||
override fun saveComponent(fileUrl: String, componentName: String, componentTag: Element?) {
|
||||
val filePath = JpsPathUtil.urlToPath(fileUrl)
|
||||
if (FileUtilRt.extensionEquals(filePath, "iml")) {
|
||||
session.setModuleComponentState(filePath, componentName, componentTag)
|
||||
session.setModuleComponentState(imlFilePath = filePath, componentName = componentName, componentTag = componentTag)
|
||||
}
|
||||
else if (isExternalModuleFile(filePath)) {
|
||||
session.setExternalModuleComponentState(FileUtilRt.getNameWithoutExtension(PathUtilRt.getFileName(filePath)), componentName,
|
||||
componentTag)
|
||||
session.setExternalModuleComponentState(
|
||||
moduleFileName = FileUtilRt.getNameWithoutExtension(PathUtilRt.getFileName(filePath)),
|
||||
componentName = componentName,
|
||||
componentTag = componentTag,
|
||||
)
|
||||
}
|
||||
else {
|
||||
val stateStorage = getProjectStateStorage(filePath, store, project)
|
||||
val stateStorage = getProjectStateStorage(filePath = filePath, store = store, project = project)
|
||||
val producer = session.getProducer(stateStorage)
|
||||
if (producer is DirectoryBasedSaveSessionProducer) {
|
||||
producer.setFileState(PathUtilRt.getFileName(filePath), componentName, componentTag?.children?.first())
|
||||
producer.setFileState(
|
||||
fileName = PathUtilRt.getFileName(filePath),
|
||||
componentName = componentName,
|
||||
element = componentTag?.children?.first(),
|
||||
)
|
||||
}
|
||||
else {
|
||||
producer?.setState(null, componentName, componentTag)
|
||||
producer?.setState(component = null, componentName = componentName, pluginId = PluginManagerCore.CORE_ID, state = componentTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,18 +127,22 @@ private class ProjectWithModulesSaveSessionProducerManager(project: Project, isU
|
||||
: ProjectSaveSessionProducerManager(project, isUseVfsForWrite)
|
||||
{
|
||||
private val internalModuleComponents: ConcurrentMap<String, ConcurrentHashMap<String, Element>> =
|
||||
if (!SystemInfoRt.isFileSystemCaseSensitive) ConcurrentCollectionFactory.createConcurrentMap(HashingStrategy.caseInsensitive())
|
||||
else ConcurrentCollectionFactory.createConcurrentMap()
|
||||
if (SystemInfoRt.isFileSystemCaseSensitive) {
|
||||
ConcurrentCollectionFactory.createConcurrentMap()
|
||||
}
|
||||
else {
|
||||
ConcurrentCollectionFactory.createConcurrentMap(HashingStrategy.caseInsensitive())
|
||||
}
|
||||
private val externalModuleComponents = ConcurrentHashMap<String, ConcurrentHashMap<String, Element>>()
|
||||
|
||||
fun setModuleComponentState(imlFilePath: String, componentName: String, componentTag: Element?) {
|
||||
val componentToElement = internalModuleComponents.computeIfAbsent(imlFilePath) { ConcurrentHashMap() }
|
||||
componentToElement[componentName] = componentTag ?: NULL_ELEMENT
|
||||
componentToElement.put(componentName, componentTag ?: NULL_ELEMENT)
|
||||
}
|
||||
|
||||
fun setExternalModuleComponentState(moduleFileName: String, componentName: String, componentTag: Element?) {
|
||||
val componentToElement = externalModuleComponents.computeIfAbsent(moduleFileName) { ConcurrentHashMap() }
|
||||
componentToElement[componentName] = componentTag ?: NULL_ELEMENT
|
||||
componentToElement.put(componentName, componentTag ?: NULL_ELEMENT)
|
||||
}
|
||||
|
||||
fun commitComponents(moduleStore: ComponentStoreImpl, moduleSaveSessionManager: SaveSessionProducerManager) {
|
||||
@@ -137,9 +151,12 @@ private class ProjectWithModulesSaveSessionProducerManager(project: Project, isU
|
||||
val producer = moduleSaveSessionManager.getProducer(storage)
|
||||
if (producer != null) {
|
||||
for ((componentName, componentTag) in componentToElement) {
|
||||
producer.setState(component = null,
|
||||
componentName = componentName,
|
||||
state = if (componentTag === NULL_ELEMENT) null else componentTag)
|
||||
producer.setState(
|
||||
component = null,
|
||||
componentName = componentName,
|
||||
pluginId = PluginManagerCore.CORE_ID,
|
||||
state = if (componentTag === NULL_ELEMENT) null else componentTag,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.components.impl.stores.ComponentStorageUtil
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
import com.intellij.openapi.util.WriteExternalException
|
||||
import com.intellij.openapi.vfs.LargeFileWriteRequestor
|
||||
import com.intellij.openapi.vfs.SafeWriteRequestor
|
||||
@@ -11,7 +12,7 @@ import com.intellij.util.xmlb.XmlSerializationException
|
||||
import org.jdom.Element
|
||||
|
||||
abstract class SaveSessionProducerBase : SaveSessionProducer, SafeWriteRequestor, LargeFileWriteRequestor {
|
||||
final override fun setState(component: Any?, componentName: String, state: Any?) {
|
||||
final override fun setState(component: Any?, componentName: String, pluginId: PluginId, state: Any?) {
|
||||
if (state == null) {
|
||||
setSerializedState(componentName = componentName, element = null)
|
||||
return
|
||||
|
||||
@@ -93,28 +93,34 @@ open class StateStorageManagerImpl(
|
||||
}
|
||||
|
||||
// storageCustomizer - to ensure that other threads will use fully constructed and configured storage (invoked under the same lock as created)
|
||||
fun getOrCreateStorage(collapsedPath: String,
|
||||
roamingType: RoamingType,
|
||||
storageClass: Class<out StateStorage> = StateStorage::class.java,
|
||||
@Suppress("DEPRECATION", "removal") stateSplitter: Class<out StateSplitter> = StateSplitterEx::class.java,
|
||||
exclusive: Boolean = false,
|
||||
storageCustomizer: (StateStorage.() -> Unit)? = null,
|
||||
storageCreator: StorageCreator? = null,
|
||||
usePathMacroManager: Boolean = true): StateStorage {
|
||||
fun getOrCreateStorage(
|
||||
collapsedPath: String,
|
||||
roamingType: RoamingType,
|
||||
storageClass: Class<out StateStorage> = StateStorage::class.java,
|
||||
@Suppress("DEPRECATION", "removal") stateSplitter: Class<out StateSplitter> = StateSplitterEx::class.java,
|
||||
exclusive: Boolean = false,
|
||||
storageCustomizer: (StateStorage.() -> Unit)? = null,
|
||||
storageCreator: StorageCreator? = null,
|
||||
usePathMacroManager: Boolean = true,
|
||||
): StateStorage {
|
||||
val normalizedCollapsedPath = normalizeFileSpec(collapsedPath)
|
||||
val key = computeStorageKey(storageClass = storageClass,
|
||||
normalizedCollapsedPath = normalizedCollapsedPath,
|
||||
collapsedPath = collapsedPath,
|
||||
storageCreator = storageCreator)
|
||||
val key = computeStorageKey(
|
||||
storageClass = storageClass,
|
||||
normalizedCollapsedPath = normalizedCollapsedPath,
|
||||
collapsedPath = collapsedPath,
|
||||
storageCreator = storageCreator,
|
||||
)
|
||||
val storage = storageLock.read { storages.get(key) } ?: return storageLock.write {
|
||||
storages.getOrPut(key) {
|
||||
val storage = when (storageCreator) {
|
||||
null -> createStateStorage(storageClass = storageClass,
|
||||
collapsedPath = normalizedCollapsedPath,
|
||||
roamingType = roamingType,
|
||||
stateSplitter = stateSplitter,
|
||||
usePathMacroManager = usePathMacroManager,
|
||||
exclusive = exclusive)
|
||||
null -> createStateStorage(
|
||||
storageClass = storageClass,
|
||||
collapsedPath = normalizedCollapsedPath,
|
||||
roamingType = roamingType,
|
||||
stateSplitter = stateSplitter,
|
||||
usePathMacroManager = usePathMacroManager,
|
||||
exclusive = exclusive,
|
||||
)
|
||||
else -> storageCreator.create(this)
|
||||
}
|
||||
storageCustomizer?.let { storage.it() }
|
||||
|
||||
@@ -104,7 +104,7 @@ internal fun <T : Any> deserializeStateWithController(
|
||||
componentName = componentName,
|
||||
pluginId = pluginId,
|
||||
stateElement = stateElement,
|
||||
)
|
||||
) as T? ?: mergeInto
|
||||
}
|
||||
else if (com.intellij.openapi.util.JDOMExternalizable::class.java.isAssignableFrom(stateClass)) {
|
||||
if (stateElement == null) {
|
||||
@@ -175,25 +175,23 @@ internal fun <T : Any> deserializeStateWithController(
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> deserializeAsJdomElement(
|
||||
private fun deserializeAsJdomElement(
|
||||
controller: SettingsController?,
|
||||
componentName: String,
|
||||
pluginId: PluginId,
|
||||
stateElement: Element?,
|
||||
): T? {
|
||||
): Element? {
|
||||
try {
|
||||
val item = controller?.doGetItem(createSettingDescriptor(key = componentName, pluginId = pluginId)) ?: GetResult.inapplicable()
|
||||
if (item.isResolved) {
|
||||
val xmlData = item.get() ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return buildNsUnawareJdom(xmlData) as T
|
||||
return buildNsUnawareJdom(xmlData)
|
||||
}
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
LOG.error("Cannot deserialize value for $componentName", e)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return stateElement as T?
|
||||
return stateElement
|
||||
}
|
||||
|
||||
private fun <T : Any> getXmlSerializationState(
|
||||
|
||||
@@ -149,10 +149,9 @@ abstract class XmlElementStorage protected constructor(
|
||||
|
||||
abstract class XmlElementStorageSaveSessionProducer<T : XmlElementStorage>(
|
||||
private val originalStates: StateMap,
|
||||
protected val storage: T
|
||||
@JvmField protected val storage: T
|
||||
) : SaveSessionProducerBase() {
|
||||
private var copiedStates: MutableMap<String, Any>? = null
|
||||
|
||||
private var newLiveStates: MutableMap<String, Element>? = HashMap()
|
||||
|
||||
protected open fun isSaveAllowed(): Boolean = !storage.checkIsSavingDisabled()
|
||||
@@ -269,19 +268,26 @@ abstract class XmlElementStorage protected constructor(
|
||||
|
||||
val normalized = element?.normalizeRootName()
|
||||
if (copiedStates == null) {
|
||||
copiedStates = setStateAndCloneIfNeeded(componentName, normalized, originalStates, newLiveStates)
|
||||
copiedStates = setStateAndCloneIfNeeded(
|
||||
key = componentName,
|
||||
newState = normalized,
|
||||
oldStates = originalStates,
|
||||
newLiveStates = newLiveStates,
|
||||
)
|
||||
}
|
||||
else {
|
||||
updateState(copiedStates!!, componentName, normalized, newLiveStates)
|
||||
updateState(states = copiedStates!!, key = componentName, newState = normalized, newLiveStates = newLiveStates)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun saveLocally(dataWriter: DataWriter?)
|
||||
}
|
||||
|
||||
protected open fun beforeElementLoaded(element: Element) { }
|
||||
protected open fun beforeElementLoaded(element: Element) {
|
||||
}
|
||||
|
||||
protected open fun beforeElementSaved(elements: MutableList<Element>, rootAttributes: MutableMap<String, String>) { }
|
||||
protected open fun beforeElementSaved(elements: MutableList<Element>, rootAttributes: MutableMap<String, String>) {
|
||||
}
|
||||
|
||||
fun updatedFromStreamProvider(changedComponentNames: MutableSet<String>, deleted: Boolean) {
|
||||
val newElement = if (deleted) null else loadElement()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.openapi.application.writeAction
|
||||
import com.intellij.openapi.components.MainConfigurationStateSplitter
|
||||
import com.intellij.openapi.util.JDOMUtil
|
||||
@@ -53,14 +54,22 @@ class DirectoryBasedStorageTest {
|
||||
assertThat(dir).doesNotExist()
|
||||
}
|
||||
|
||||
private fun generateData(name: String): String = """
|
||||
<component name="test">
|
||||
<${if (name == "test") "component" else "sub"} name="$name" />
|
||||
</component>""".trimIndent()
|
||||
private fun generateData(name: String): String {
|
||||
return """
|
||||
<component name="test">
|
||||
<${if (name == "test") "component" else "sub"} name="$name" />
|
||||
</component>
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
private suspend fun setStateAndSave(storage: StateStorageBase<*>, componentName: String, state: String?) {
|
||||
val saveSessionProducer = storage.createSaveSessionProducer()!!
|
||||
saveSessionProducer.setState(null, componentName, if (state == null) Element("state") else JDOMUtil.load(state))
|
||||
saveSessionProducer.setState(
|
||||
component = null,
|
||||
componentName = componentName,
|
||||
pluginId = PluginManagerCore.CORE_ID,
|
||||
state = if (state == null) Element("state") else JDOMUtil.load(state),
|
||||
)
|
||||
writeAction {
|
||||
saveSessionProducer.createSaveSession()!!.saveBlocking()
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class XmlElementStorageTest {
|
||||
val storage = MyXmlElementStorage(Element("root").addContent(Element("component").setAttribute("name", "test").addContent(Element("foo"))))
|
||||
val newState = Element("component").setAttribute("name", "test").addContent(Element("bar"))
|
||||
val externalizationSession = storage.createSaveSessionProducer()!!
|
||||
externalizationSession.setState(null, "test", newState)
|
||||
externalizationSession.setState(component = null, componentName = "test", pluginId = PluginManagerCore.CORE_ID, state = newState)
|
||||
externalizationSession.createSaveSession()!!.saveBlocking()
|
||||
assertThat(storage.savedElement).isNotNull
|
||||
assertThat(storage.savedElement!!.getChild("component").getChild("bar")).isNotNull
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.configurationStore
|
||||
|
||||
import com.intellij.openapi.extensions.PluginId
|
||||
|
||||
interface SaveSession : StorageManagerFileWriteRequestor {
|
||||
suspend fun save() {
|
||||
saveBlocking()
|
||||
@@ -10,7 +12,7 @@ interface SaveSession : StorageManagerFileWriteRequestor {
|
||||
}
|
||||
|
||||
interface SaveSessionProducer : StorageManagerFileWriteRequestor {
|
||||
fun setState(component: Any?, componentName: String, state: Any?)
|
||||
fun setState(component: Any?, componentName: String, pluginId: PluginId, state: Any?)
|
||||
|
||||
/**
|
||||
* Returns `null` if nothing to save.
|
||||
|
||||
@@ -31,8 +31,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
private val shimPluginId = PluginId.getId("__controller_shim__")
|
||||
|
||||
internal class StateStorageBackedByController(
|
||||
@JvmField val controller: SettingsControllerMediator,
|
||||
private val tags: List<SettingTag>,
|
||||
@@ -51,28 +49,33 @@ internal class StateStorageBackedByController(
|
||||
@Suppress("DEPRECATION", "UNCHECKED_CAST")
|
||||
when {
|
||||
stateClass === Element::class.java -> {
|
||||
getXmlData(createSettingDescriptor(componentName)).takeIf { it.isResolved }?.let {
|
||||
getXmlData(createSettingDescriptor(componentName, pluginId)).takeIf { it.isResolved }?.let {
|
||||
return it.get() as T?
|
||||
}
|
||||
return mergeInto
|
||||
}
|
||||
com.intellij.openapi.util.JDOMExternalizable::class.java.isAssignableFrom(stateClass) -> {
|
||||
return readDataForDeprecatedJdomExternalizable(componentName = componentName, mergeInto = mergeInto, stateClass = stateClass)
|
||||
return readDataForDeprecatedJdomExternalizable(
|
||||
componentName = componentName,
|
||||
mergeInto = mergeInto,
|
||||
stateClass = stateClass,
|
||||
pluginId = pluginId,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
val beanBinding = bindingProducer.getRootBinding(stateClass) as NotNullDeserializeBinding
|
||||
if (beanBinding is KotlinxSerializationBinding) {
|
||||
val data = controller.getItem(createSettingDescriptor(componentName))
|
||||
if (data != null) {
|
||||
return cborFormat.decodeFromByteArray(beanBinding.serializer, data) as T
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
val data = controller.getItem(createSettingDescriptor(componentName, pluginId)) ?: return null
|
||||
return cborFormat.decodeFromByteArray(beanBinding.serializer, data) as T
|
||||
}
|
||||
else {
|
||||
return getXmlSerializationState(mergeInto = mergeInto, beanBinding = beanBinding, componentName = componentName)
|
||||
return getXmlSerializationState(
|
||||
mergeInto = mergeInto,
|
||||
beanBinding = beanBinding,
|
||||
componentName = componentName,
|
||||
pluginId = pluginId,
|
||||
)
|
||||
}
|
||||
}
|
||||
catch (e: SerializationException) {
|
||||
@@ -85,9 +88,14 @@ internal class StateStorageBackedByController(
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> readDataForDeprecatedJdomExternalizable(componentName: String, mergeInto: T?, stateClass: Class<T>): T? {
|
||||
private fun <T : Any> readDataForDeprecatedJdomExternalizable(
|
||||
componentName: String,
|
||||
pluginId: PluginId,
|
||||
mergeInto: T?,
|
||||
stateClass: Class<T>,
|
||||
): T? {
|
||||
// we don't care about data from the old storage for deprecated JDOMExternalizable
|
||||
val data = getXmlData(createSettingDescriptor(componentName)).get() ?: return mergeInto
|
||||
val data = getXmlData(createSettingDescriptor(componentName, pluginId)).get() ?: return mergeInto
|
||||
if (mergeInto != null) {
|
||||
thisLogger().error("State is ${stateClass.name}, merge into is $mergeInto, state element text is $data")
|
||||
}
|
||||
@@ -101,12 +109,17 @@ internal class StateStorageBackedByController(
|
||||
return t as T
|
||||
}
|
||||
|
||||
private fun <T : Any> getXmlSerializationState(mergeInto: T?, beanBinding: NotNullDeserializeBinding, componentName: String): T? {
|
||||
private fun <T : Any> getXmlSerializationState(
|
||||
mergeInto: T?,
|
||||
beanBinding: NotNullDeserializeBinding,
|
||||
componentName: String,
|
||||
pluginId: PluginId,
|
||||
): T? {
|
||||
var result = mergeInto
|
||||
val bindings = (beanBinding as BeanBinding).bindings!!
|
||||
|
||||
for ((index, binding) in bindings.withIndex()) {
|
||||
val data = getXmlData(createSettingDescriptor("$componentName.${binding.accessor.name}"))
|
||||
val data = getXmlData(createSettingDescriptor("$componentName.${binding.accessor.name}", pluginId))
|
||||
if (!data.isResolved) {
|
||||
continue
|
||||
}
|
||||
@@ -153,10 +166,10 @@ internal class StateStorageBackedByController(
|
||||
// external change is not expected and not supported
|
||||
}
|
||||
|
||||
internal fun createSettingDescriptor(key: String): SettingDescriptor<ByteArray> {
|
||||
internal fun createSettingDescriptor(key: String, pluginId: PluginId): SettingDescriptor<ByteArray> {
|
||||
return SettingDescriptor(
|
||||
key = key,
|
||||
pluginId = shimPluginId,
|
||||
pluginId = pluginId,
|
||||
tags = tags,
|
||||
serializer = RawSettingSerializerDescriptor,
|
||||
)
|
||||
@@ -171,8 +184,8 @@ private class ControllerBackedSaveSessionProducer(
|
||||
storageController.controller.setItem(key, value)
|
||||
}
|
||||
|
||||
override fun setState(component: Any?, componentName: String, state: Any?) {
|
||||
val settingDescriptor = storageController.createSettingDescriptor(componentName)
|
||||
override fun setState(component: Any?, componentName: String, pluginId: PluginId, state: Any?) {
|
||||
val settingDescriptor = storageController.createSettingDescriptor(componentName, pluginId)
|
||||
if (state == null) {
|
||||
put(key = settingDescriptor, value = null)
|
||||
return
|
||||
@@ -200,11 +213,13 @@ private class ControllerBackedSaveSessionProducer(
|
||||
continue
|
||||
}
|
||||
|
||||
val element = beanBinding.serializePropertyInto(/* binding = */ binding,
|
||||
/* o = */ state,
|
||||
/* element = */ null,
|
||||
/* filter = */ filter,
|
||||
/* isFilterPropertyItself = */ true)
|
||||
val element = beanBinding.serializePropertyInto(
|
||||
binding = binding,
|
||||
o = state,
|
||||
preCreatedElement = null,
|
||||
filter = filter,
|
||||
isFilterPropertyItself = true,
|
||||
)
|
||||
putJdomElement(settingDescriptor.withSubName(binding.accessor.name), element)
|
||||
}
|
||||
}
|
||||
@@ -230,7 +245,7 @@ private class ControllerBackedSaveSessionProducer(
|
||||
}
|
||||
|
||||
private class BindingProducer : XmlSerializerImpl.XmlSerializerBase() {
|
||||
private val cache: MutableMap<Class<*>, Binding> = HashMap()
|
||||
private val cache = HashMap<Class<*>, Binding>()
|
||||
private val cacheLock = ReentrantReadWriteLock()
|
||||
|
||||
override fun getRootBinding(aClass: Class<*>): Binding {
|
||||
|
||||
@@ -910,7 +910,7 @@ class JpsProjectSerializersImpl(directorySerializersFactories: List<JpsDirectory
|
||||
unloadedEntityStorage: EntityStorage,
|
||||
writer: JpsFileContentWriter) {
|
||||
LOG.trace("saving modules list")
|
||||
it.saveEntitiesList(storage.entities(ModuleEntity::class.java) + unloadedEntityStorage.entities(ModuleEntity::class.java), writer)
|
||||
it.saveEntityList(storage.entities(ModuleEntity::class.java) + unloadedEntityStorage.entities(ModuleEntity::class.java), writer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.workspace.jps.serialization.impl
|
||||
|
||||
import com.intellij.java.workspace.entities.*
|
||||
@@ -1031,7 +1031,7 @@ internal open class ModuleListSerializerImpl(override val fileUrl: String,
|
||||
}
|
||||
}
|
||||
|
||||
override fun saveEntitiesList(entities: Sequence<ModuleEntity>, writer: JpsFileContentWriter) {
|
||||
override fun saveEntityList(entities: Sequence<ModuleEntity>, writer: JpsFileContentWriter) {
|
||||
val entitiesToSave = entities
|
||||
.filter { moduleEntity ->
|
||||
entitySourceFilter(moduleEntity.entitySource)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.workspace.jps.serialization.impl
|
||||
|
||||
import com.intellij.openapi.components.ExpandMacroToPathMap
|
||||
@@ -29,6 +29,7 @@ interface JpsFileContentReader {
|
||||
|
||||
interface JpsFileContentWriter {
|
||||
fun saveComponent(fileUrl: String, componentName: String, componentTag: Element?)
|
||||
|
||||
fun getReplacePathMacroMap(fileUrl: String): PathMacroMap
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ interface JpsFileEntitiesSerializer<E : WorkspaceEntity> {
|
||||
* This method reads configuration files and creates entities that are not added to any builder.
|
||||
*
|
||||
* These entities can be just added to builder, but it's suggested to do it using [checkAndAddToBuilder] because this method
|
||||
* implements additional actions on adding (e.g. reports error when trying to add a library that already exists).
|
||||
* implements additional actions on adding (e.g., reports error when trying to add a library that already exists).
|
||||
*/
|
||||
fun loadEntities(reader: JpsFileContentReader,
|
||||
errorReporter: ErrorReporter,
|
||||
@@ -65,7 +66,7 @@ interface JpsFileEntitiesSerializer<E : WorkspaceEntity> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a serializer which is responsible for serializing all entities of the given type (e.g. libraries in *.ipr file).
|
||||
* Represents a serializer which is responsible for serializing all entities of the given type (e.g., libraries in *.ipr file).
|
||||
*/
|
||||
interface JpsFileEntityTypeSerializer<E : WorkspaceEntity> : JpsFileEntitiesSerializer<E> {
|
||||
val isExternalStorage: Boolean
|
||||
@@ -75,7 +76,7 @@ interface JpsFileEntityTypeSerializer<E : WorkspaceEntity> : JpsFileEntitiesSeri
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a directory containing configuration files (e.g. .idea/libraries).
|
||||
* Represents a directory containing configuration files (e.g. `.idea/libraries`).
|
||||
*/
|
||||
interface JpsDirectoryEntitiesSerializerFactory<E : WorkspaceEntity> {
|
||||
val directoryUrl: String
|
||||
@@ -103,14 +104,18 @@ interface JpsModuleListSerializer {
|
||||
get() = { true }
|
||||
|
||||
fun loadFileList(reader: JpsFileContentReader, virtualFileManager: VirtualFileUrlManager): List<Pair<VirtualFileUrl, String?>>
|
||||
|
||||
fun createSerializer(internalSource: JpsFileEntitySource, fileUrl: VirtualFileUrl, moduleGroup: String?): JpsFileEntitiesSerializer<ModuleEntity>
|
||||
fun saveEntitiesList(entities: Sequence<ModuleEntity>, writer: JpsFileContentWriter)
|
||||
|
||||
fun saveEntityList(entities: Sequence<ModuleEntity>, writer: JpsFileContentWriter)
|
||||
|
||||
fun getFileName(entity: ModuleEntity): String
|
||||
|
||||
fun deleteObsoleteFile(fileUrl: String, writer: JpsFileContentWriter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents set of serializers for some project.
|
||||
* Represents a set of serializers for some project.
|
||||
*/
|
||||
interface JpsProjectSerializers {
|
||||
companion object {
|
||||
|
||||
Reference in New Issue
Block a user