extract ProjectSaveSessionProducerManager

This commit is contained in:
Vladimir Krivosheev
2019-01-07 17:03:59 +01:00
parent e408cc35e8
commit 516d0efec9
5 changed files with 123 additions and 90 deletions
@@ -135,7 +135,7 @@ abstract class ComponentStoreImpl : IComponentStore {
beforeSaveComponents(errors, readonlyFiles)
val saveSessionProducerManager = if (components.isEmpty()) null else SaveSessionProducerManager()
val saveSessionProducerManager = if (components.isEmpty()) null else createSaveSessionProducerManager()
if (saveSessionProducerManager != null) {
saveComponents(isForce, saveSessionProducerManager, errors)
}
@@ -148,7 +148,7 @@ abstract class ComponentStoreImpl : IComponentStore {
}
if (saveSessionProducerManager != null) {
doSave(saveSessionProducerManager, readonlyFiles, errors)
saveSessionProducerManager.save(readonlyFiles, errors)
}
CompoundRuntimeException.throwIfNotEmpty(errors)
@@ -224,7 +224,7 @@ abstract class ComponentStoreImpl : IComponentStore {
override fun saveApplicationComponent(component: PersistentStateComponent<*>) {
val stateSpec = StoreUtil.getStateSpec(component)
LOG.info("saveApplicationComponent is called for ${stateSpec.name}")
val externalizationSession = SaveSessionProducerManager()
val externalizationSession = createSaveSessionProducerManager()
commitComponent(externalizationSession, ComponentInfoImpl(component, stateSpec), null)
val absolutePath = Paths.get(storageManager.expandMacros(findNonDeprecated(stateSpec.storages).path)).toAbsolutePath().toString()
runUndoTransparentWriteAction {
@@ -244,6 +244,8 @@ abstract class ComponentStoreImpl : IComponentStore {
}
}
internal open fun createSaveSessionProducerManager() = SaveSessionProducerManager()
private fun commitComponent(session: SaveSessionProducerManager, info: ComponentInfo, componentName: String?) {
val component = info.component
@Suppress("DEPRECATION")
@@ -280,10 +282,6 @@ abstract class ComponentStoreImpl : IComponentStore {
}
}
protected open fun doSave(saveSession: SaveExecutor, readonlyFiles: MutableList<SaveSessionAndFile> = arrayListOf(), errors: MutableList<Throwable>) {
saveSession.save(readonlyFiles, errors)
}
private fun initJdomExternalizable(@Suppress("DEPRECATION") component: JDOMExternalizable, componentName: String): String? {
doAddComponent(componentName, component, null)
@@ -601,37 +599,4 @@ private fun notifyUnknownMacros(store: IComponentStore, project: Project, compon
LOG.debug("Reporting unknown path macros $macros in component $componentName")
doNotify(macros, project, Collections.singletonMap(substitutor, store))
}, project.disposed)
}
interface SaveExecutor {
/**
* @return was something really saved
*/
fun save(readonlyFiles: MutableList<SaveSessionAndFile> = SmartList(), errors: MutableList<Throwable>): Boolean
}
private class SaveSessionProducerManager : SaveExecutor {
private val producers = LinkedHashMap<StateStorage, SaveSessionProducer>()
fun getProducer(storage: StateStorage): SaveSessionProducer? {
var producer = producers.get(storage)
if (producer == null) {
producer = storage.createSaveSessionProducer() ?: return null
producers.put(storage, producer)
}
return producer
}
override fun save(readonlyFiles: MutableList<SaveSessionAndFile>, errors: MutableList<Throwable>): Boolean {
if (producers.isEmpty()) {
return false
}
var changed = false
for (session in producers.values) {
executeSave(session.createSaveSession() ?: continue, readonlyFiles, errors)
changed = true
}
return changed
}
}
@@ -0,0 +1,71 @@
// Copyright 2000-2019 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.configurationStore
import com.intellij.notification.Notifications
import com.intellij.notification.NotificationsManager
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.components.impl.stores.IComponentStore
import com.intellij.openapi.components.impl.stores.SaveSessionAndFile
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.impl.ProjectManagerImpl
import com.intellij.openapi.vfs.ReadonlyStatusHandler
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.containers.mapSmart
import com.intellij.util.lang.CompoundRuntimeException
internal class ProjectSaveSessionProducerManager(private val project: Project) : SaveSessionProducerManager() {
override fun save(readonlyFiles: MutableList<SaveSessionAndFile>, errors: MutableList<Throwable>): Boolean {
val isChanged = super.save(readonlyFiles, errors)
val notifications = getUnableToSaveNotifications()
if (readonlyFiles.isEmpty()) {
notifications.forEach { it.expire() }
return isChanged
}
if (!notifications.isEmpty()) {
throw IComponentStore.SaveCancelledException()
}
val status = runReadAction {
ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(getFilesList(readonlyFiles))
}
if (status.hasReadonlyFiles()) {
dropUnableToSaveProjectNotification(project, status.readonlyFiles.toList())
throw IComponentStore.SaveCancelledException()
}
val oldList = readonlyFiles.toTypedArray()
readonlyFiles.clear()
for (entry in oldList) {
executeSave(entry.session, readonlyFiles, errors)
}
CompoundRuntimeException.throwIfNotEmpty(errors)
if (!readonlyFiles.isEmpty()) {
dropUnableToSaveProjectNotification(project, getFilesList(readonlyFiles))
throw IComponentStore.SaveCancelledException()
}
return isChanged
}
private fun dropUnableToSaveProjectNotification(project: Project, readOnlyFiles: List<VirtualFile>) {
val notifications = getUnableToSaveNotifications()
if (notifications.isEmpty()) {
Notifications.Bus.notify(
ProjectManagerImpl.UnableToSaveProjectNotification(project, readOnlyFiles), project)
}
else {
notifications[0].setFiles(readOnlyFiles)
}
}
private fun getUnableToSaveNotifications(): Array<out ProjectManagerImpl.UnableToSaveProjectNotification> {
return NotificationsManager.getNotificationsManager()
.getNotificationsOfType(ProjectManagerImpl.UnableToSaveProjectNotification::class.java, project)
}
}
private fun getFilesList(readonlyFiles: List<SaveSessionAndFile>) = readonlyFiles.mapSmart { it.file }
@@ -3,8 +3,6 @@ package com.intellij.configurationStore
import com.intellij.ide.highlighter.ProjectFileType
import com.intellij.ide.highlighter.WorkspaceFileType
import com.intellij.notification.Notifications
import com.intellij.notification.NotificationsManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.invokeAndWaitIfNeed
import com.intellij.openapi.application.runReadAction
@@ -20,7 +18,6 @@ import com.intellij.openapi.project.ProjectCoreUtil
import com.intellij.openapi.project.ex.ProjectNameProvider
import com.intellij.openapi.project.getProjectCachePath
import com.intellij.openapi.project.impl.ProjectImpl
import com.intellij.openapi.project.impl.ProjectManagerImpl.UnableToSaveProjectNotification
import com.intellij.openapi.project.impl.ProjectStoreClassProvider
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.FileUtilRt
@@ -33,9 +30,7 @@ import com.intellij.util.PathUtilRt
import com.intellij.util.SmartList
import com.intellij.util.containers.computeIfAny
import com.intellij.util.containers.isNullOrEmpty
import com.intellij.util.containers.mapSmart
import com.intellij.util.io.*
import com.intellij.util.lang.CompoundRuntimeException
import com.intellij.util.text.nullize
import java.nio.file.AccessDeniedException
import java.nio.file.Path
@@ -333,52 +328,9 @@ private open class ProjectStoreImpl(project: Project, private val pathMacroManag
super.beforeSaveComponents(errors, readonlyFiles)
}
override fun doSave(saveSession: SaveExecutor, readonlyFiles: MutableList<SaveSessionAndFile>, errors: MutableList<Throwable>) {
super.doSave(saveSession, readonlyFiles, errors)
val notifications = NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification::class.java, project)
if (readonlyFiles.isEmpty()) {
notifications.forEach { it.expire() }
return
}
if (!notifications.isEmpty()) {
throw IComponentStore.SaveCancelledException()
}
val status = runReadAction { ReadonlyStatusHandler.getInstance(project).ensureFilesWritable(getFilesList(readonlyFiles)) }
if (status.hasReadonlyFiles()) {
dropUnableToSaveProjectNotification(project, status.readonlyFiles.toList())
throw IComponentStore.SaveCancelledException()
}
val oldList = readonlyFiles.toTypedArray()
readonlyFiles.clear()
for (entry in oldList) {
executeSave(entry.session, readonlyFiles, errors)
}
CompoundRuntimeException.throwIfNotEmpty(errors)
if (!readonlyFiles.isEmpty()) {
dropUnableToSaveProjectNotification(project, getFilesList(readonlyFiles))
throw IComponentStore.SaveCancelledException()
}
}
final override fun createSaveSessionProducerManager() = ProjectSaveSessionProducerManager(project)
}
private fun dropUnableToSaveProjectNotification(project: Project, readOnlyFiles: List<VirtualFile>) {
val notifications = NotificationsManager.getNotificationsManager().getNotificationsOfType(UnableToSaveProjectNotification::class.java, project)
if (notifications.isEmpty()) {
Notifications.Bus.notify(UnableToSaveProjectNotification(project, readOnlyFiles), project)
}
else {
notifications[0].myFiles = readOnlyFiles
}
}
private fun getFilesList(readonlyFiles: List<SaveSessionAndFile>) = readonlyFiles.mapSmart { it.file }
private class ProjectWithModulesStoreImpl(project: Project, pathMacroManager: PathMacroManager) : ProjectStoreImpl(project, pathMacroManager) {
override fun beforeSaveComponents(errors: MutableList<Throwable>, readonlyFiles: MutableList<SaveSessionAndFile>) {
super.beforeSaveComponents(errors, readonlyFiles)
@@ -0,0 +1,40 @@
// Copyright 2000-2019 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.configurationStore
import com.intellij.openapi.components.StateStorage
import com.intellij.openapi.components.impl.stores.SaveSessionAndFile
import com.intellij.util.SmartList
import java.util.*
interface SaveExecutor {
/**
* @return was something really saved
*/
fun save(readonlyFiles: MutableList<SaveSessionAndFile> = SmartList(), errors: MutableList<Throwable>): Boolean
}
internal open class SaveSessionProducerManager : SaveExecutor {
private val producers = LinkedHashMap<StateStorage, SaveSessionProducer>()
fun getProducer(storage: StateStorage): SaveSessionProducer? {
var producer = producers.get(storage)
if (producer == null) {
producer = storage.createSaveSessionProducer() ?: return null
producers.put(storage, producer)
}
return producer
}
override fun save(readonlyFiles: MutableList<SaveSessionAndFile>, errors: MutableList<Throwable>): Boolean {
if (producers.isEmpty()) {
return false
}
var isChanged = false
for (session in producers.values) {
executeSave(session.createSaveSession() ?: continue, readonlyFiles, errors)
isChanged = true
}
return isChanged
}
}
@@ -887,7 +887,12 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable {
public static class UnableToSaveProjectNotification extends Notification {
private Project myProject;
public List<VirtualFile> myFiles;
private List<VirtualFile> myFiles;
public void setFiles(@NotNull List<VirtualFile> files) {
myFiles = files;
}
public UnableToSaveProjectNotification(@NotNull final Project project, @NotNull List<VirtualFile> readOnlyFiles) {
super("Project Settings", "Could not save project", "Unable to save project files. Please ensure project files are writable and you have permissions to modify them." +