diff --git a/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java b/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java index 303387e7bf9e..1cf0e078bfd3 100644 --- a/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java +++ b/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java @@ -19,7 +19,10 @@ import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.JDOMExternalizable; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.NamedJDOMExternalizable; +import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -27,7 +30,6 @@ import com.intellij.util.SystemProperties; import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import org.jetbrains.jps.model.serialization.JDomSerializationUtil; import org.junit.Assert; @@ -37,7 +39,6 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import java.util.Set; /** * @author nik @@ -151,16 +152,5 @@ public class CompilerTestUtil { @Override public void save() { } - - @Nullable - @Override - public Set analyzeExternalChanges(@NotNull Set> changedFiles) { - return null; - } - - @Override - public void collectAllStorageFiles(@NotNull List files) { - - } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStateStorageManager.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStateStorageManager.java index 38558196e6ff..516c89f7a109 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStateStorageManager.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStateStorageManager.java @@ -17,6 +17,7 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageOperation; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.module.Module; @@ -53,4 +54,9 @@ class ModuleStateStorageManager extends StateStorageManagerImpl { return myModule.getName() + Integer.toHexString(myModule.getModuleFilePath().hashCode()); } + @NotNull + @Override + protected StateStorage.Listener createStorageTopicListener() { + return myModule.getProject().getMessageBus().syncPublisher(StateStorage.PROJECT_STORAGE_TOPIC); + } } diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java index ec07d50986bd..7386fe0e9d41 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java @@ -29,6 +29,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.PathUtilRt; +import com.intellij.util.messages.MessageBus; import gnu.trove.THashMap; import org.jdom.Attribute; import org.jdom.Element; @@ -131,8 +132,8 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM @Override @NotNull - protected Element save() { - Element root = super.save(); + protected Element save(@NotNull Map newLiveStates) { + Element root = super.save(newLiveStates); myOptions.put(VERSION_OPTION, Integer.toString(myVersion)); String[] options = ArrayUtil.toStringArray(myOptions.keySet()); Arrays.sort(options); @@ -152,11 +153,6 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM return new ModuleFileData(this); } - @Override - protected int computeHash() { - return super.computeHash() * 31 + myOptions.hashCode(); - } - @Nullable @Override public Set getChangedComponentNames(@NotNull StorageData newStorageData, @Nullable PathMacroSubstitutor substitutor) { @@ -168,12 +164,10 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM } public void setOption(final String optionName, final String optionValue) { - clearHash(); myOptions.put(optionName, optionValue); } public void clearOption(final String optionName) { - clearHash(); myOptions.remove(optionName); } @@ -245,6 +239,12 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM return ((ProjectEx)myModule.getProject()).isOptimiseTestLoadSpeed(); } + @NotNull + @Override + protected MessageBus getMessageBus() { + return myModule.getMessageBus(); + } + @NotNull @Override protected StateStorageManager createStateStorageManager() { diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java index 4931a32d64b3..3e63072599f1 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java @@ -15,8 +15,6 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.openapi.components.StateStorage; -import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.components.store.ComponentSaveSession; import com.intellij.openapi.module.Module; @@ -27,12 +25,8 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.SmartHashSet; -import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -45,12 +39,17 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { } @Override - public void reinitComponents(@NotNull Set componentNames, boolean reloadData) { - super.reinitComponents(componentNames, reloadData); + protected boolean reinitComponent(@NotNull String componentName, boolean reloadData) { + if (super.reinitComponent(componentName, reloadData)) { + return true; + } for (Module module : getPersistentModules()) { - ((ModuleImpl)module).getStateStore().reinitComponents(componentNames, reloadData); + if (((ModuleStoreImpl)((ModuleImpl)module).getStateStore()).reinitComponent(componentName, reloadData)) { + return true; + } } + return false; } @Override @@ -87,54 +86,19 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { } @Override - protected SaveSessionImpl createSaveSession() throws StateStorageException { + protected SaveSessionImpl createSaveSession() { return new ProjectWithModulesSaveSession(); } private class ProjectWithModulesSaveSession extends ProjectSaveSession { - List myModuleSaveSessions = new SmartList(); + final List myModuleSaveSessions = new SmartList(); public ProjectWithModulesSaveSession() { for (Module module : getPersistentModules()) { - myModuleSaveSessions.add(((ModuleImpl)module).getStateStore().startSave()); + ContainerUtil.addIfNotNull(myModuleSaveSessions, ((ModuleImpl)module).getStateStore().startSave()); } } - @Override - public void collectAllStorageFiles(boolean includingSubStructures, @NotNull List files) { - super.collectAllStorageFiles(includingSubStructures, files); - - if (includingSubStructures) { - for (ComponentSaveSession moduleSaveSession : myModuleSaveSessions) { - moduleSaveSession.collectAllStorageFiles(true, files); - } - } - } - - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull Set> changedFiles) { - Set superResult = super.analyzeExternalChanges(changedFiles); - if (superResult == null) { - return null; - } - - Set result = superResult.isEmpty() ? null : new THashSet(superResult); - for (ComponentSaveSession moduleSaveSession : myModuleSaveSessions) { - Set s = moduleSaveSession.analyzeExternalChanges(changedFiles); - if (s == null) { - return null; - } - if (!s.isEmpty()) { - if (result == null) { - result = new SmartHashSet(); - } - result.addAll(s); - } - } - return result == null ? Collections.emptySet() : result; - } - @Override public void finishSave() { try { @@ -159,18 +123,6 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { } } - @Override - public void reset() { - try { - for (ComponentSaveSession moduleSaveSession : myModuleSaveSessions) { - moduleSaveSession.reset(); - } - } - finally { - super.reset(); - } - } - @Override protected void beforeSave(@NotNull List> readonlyFiles) { super.beforeSave(readonlyFiles); diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java index 386ec9b3bebb..8b91cec8bac1 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java @@ -17,11 +17,7 @@ package com.intellij.openapi.roots.impl.storage; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.PathMacroManager; -import com.intellij.openapi.components.StateStorage; -import com.intellij.openapi.components.StateStorageException; -import com.intellij.openapi.components.Storage; -import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.components.*; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; @@ -42,7 +38,6 @@ import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.messages.MessageBus; import org.jdom.Element; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; @@ -63,8 +58,6 @@ import java.util.Set; * Time: 1:42:06 PM */ public class ClasspathStorage implements StateStorage { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.storage.ClasspathStorage"); - @NonNls public static final String SPECIAL_STORAGE = "special"; public static final String DEFAULT_STORAGE_DESCR = ProjectBundle.message("project.roots.classpath.format.default.descr"); @@ -72,21 +65,17 @@ public class ClasspathStorage implements StateStorage { @NonNls public static final String CLASSPATH_DIR_OPTION = JpsProjectLoader.CLASSPATH_DIR_ATTRIBUTE; @NonNls private static final String COMPONENT_TAG = "component"; - private Object mySession; private final ClasspathStorageProvider.ClasspathConverter myConverter; - public ClasspathStorage(Module module) { myConverter = getProvider(ClassPathStorageUtil.getStorageType(module)).createConverter(module); - final MessageBus messageBus = module.getMessageBus(); - final VirtualFileTracker virtualFileTracker = - (VirtualFileTracker)module.getPicoContainer().getComponentInstanceOfType(VirtualFileTracker.class); + final VirtualFileTracker virtualFileTracker = ServiceManager.getService(VirtualFileTracker.class); if (virtualFileTracker != null) { final ArrayList files = new ArrayList(); try { myConverter.getFileSet().listFiles(files); for (VirtualFile file : files) { - final Listener listener = messageBus.syncPublisher(STORAGE_TOPIC); + final Listener listener = module.getProject().getMessageBus().syncPublisher(PROJECT_STORAGE_TOPIC); virtualFileTracker.addTracker(file.getUrl(), new VirtualFileAdapter() { @Override public void contentsChanged(@NotNull final VirtualFileEvent event) { @@ -107,7 +96,7 @@ public class ClasspathStorage implements StateStorage { @Override @Nullable - public T getState(final Object component, @NotNull final String componentName, Class stateClass, @Nullable T mergeInto) + public T getState(final Object component, @NotNull final String componentName, @NotNull Class stateClass, @Nullable T mergeInto) throws StateStorageException { assert component instanceof ModuleRootManager; assert componentName.equals("NewModuleRootManager"); @@ -147,8 +136,7 @@ public class ClasspathStorage implements StateStorage { } @Override - public boolean hasState(final Object component, @NotNull final String componentName, final Class aClass, final boolean reloadData) - throws StateStorageException { + public boolean hasState(@Nullable final Object component, @NotNull final String componentName, final Class aClass, final boolean reloadData) { return true; } @@ -171,42 +159,23 @@ public class ClasspathStorage implements StateStorage { @Override @NotNull public ExternalizationSession startExternalization() { - final ExternalizationSession session = new ExternalizationSession() { + + return new ExternalizationSession() { @Override public void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, Storage storageSpec) { - assert mySession == this; ClasspathStorage.this.setState(component, componentName, state); } }; + } - mySession = session; - return session; + @Nullable + @Override + public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { + return new MySaveSession(); } @Override - @NotNull - public SaveSession startSave(@NotNull final ExternalizationSession externalizationSession) { - assert mySession == externalizationSession; - - final SaveSession session = new MySaveSession(); - - mySession = session; - return session; - } - - - @Override - public void finishSave(@NotNull final SaveSession saveSession) { - try { - LOG.assertTrue(mySession == saveSession); - } - finally { - mySession = null; - } - } - - @Override - public void reload(@NotNull Set changedComponents) { + public void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set> changedFiles, @NotNull Set result) { } public void save() throws StateStorageException { @@ -402,19 +371,7 @@ public class ClasspathStorage implements StateStorage { private class MySaveSession implements SaveSession, SafeWriteRequestor { @Override public void save() { - assert mySession == this; ClasspathStorage.this.save(); } - - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { - return null; - } - - @Override - public void collectAllStorageFiles(@NotNull List files) { - getFileSet().listFiles(files); - } } } diff --git a/platform/platform-impl/src/com/intellij/internal/ReloadProjectAction.java b/platform/platform-impl/src/com/intellij/internal/ReloadProjectAction.java index c807c0631f9f..efa341b30aec 100644 --- a/platform/platform-impl/src/com/intellij/internal/ReloadProjectAction.java +++ b/platform/platform-impl/src/com/intellij/internal/ReloadProjectAction.java @@ -33,7 +33,7 @@ public class ReloadProjectAction extends AnAction implements DumbAware { public void actionPerformed(@NotNull AnActionEvent e) { Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); if (project != null) { - ((ProjectManagerImpl)ProjectManager.getInstance()).reloadProjectImpl(project, false); + ((ProjectManagerImpl)ProjectManager.getInstance()).reloadProjectImpl(project, null); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java index 3424aea2e4d9..2552c7d0f89d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java @@ -23,6 +23,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.NamedJDOMExternalizable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.*; +import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,7 +52,7 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto private boolean myConfigDirectoryRefreshed; @Override - protected StorageData createStorageData(String storageSpec) { + protected StorageData createStorageData(@NotNull String storageSpec) { return new FileBasedStorage.FileStorageData(ROOT_ELEMENT_NAME); } @@ -77,6 +78,11 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto return super.getMacroSubstitutor(fileSpec); } + @Override + protected boolean isUseXmlProlog() { + return false; + } + @Override protected void beforeFileBasedStorageCreate() { if (!myConfigDirectoryRefreshed && (application.isUnitTestMode() || application.isDispatchThread())) { @@ -132,8 +138,14 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto } @Override - public Collection reload(@NotNull Set> changedFiles) throws IOException { - return reload(changedFiles, myApplication.getMessageBus()); + public Collection reload(@NotNull Set> changedFiles) { + return doReload(changedFiles); + } + + @Override + @NotNull + protected MessageBus getMessageBus() { + return myApplication.getMessageBus(); } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java index d43d446de757..9759a237b6ef 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.List; +import java.util.Map; import java.util.Set; abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { @@ -75,8 +76,8 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { @Override @NotNull - protected Element save() { - Element root = super.save(); + protected Element save(@NotNull Map newLiveStates) { + Element root = super.save(newLiveStates); if (root == null) { root = new Element(myRootElementName); } @@ -90,21 +91,14 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { return new BaseStorageData(this); } - @Override - protected int computeHash() { - int result = super.computeHash(); - result = result * 31 + myVersion; - return result; - } - @Nullable @Override - public Set getChangedComponentNames(@NotNull StorageData storageData, @Nullable PathMacroSubstitutor substitutor) { - BaseStorageData data = (BaseStorageData)storageData; + public Set getChangedComponentNames(@NotNull StorageData newStorageData, @Nullable PathMacroSubstitutor substitutor) { + BaseStorageData data = (BaseStorageData)newStorageData; if (myVersion != data.myVersion) { return null; } - return super.getChangedComponentNames(storageData, substitutor); + return super.getChangedComponentNames(newStorageData, substitutor); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java index 05a303f271a2..dd145a3758f5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.diagnostic.IdeErrorsDialog; import com.intellij.diagnostic.PluginException; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.application.ApplicationManager; @@ -27,10 +26,14 @@ import com.intellij.openapi.components.store.ReadOnlyModificationException; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMExternalizable; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.RoamingTypeDisabled; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; +import com.intellij.util.ArrayUtilRt; import com.intellij.util.ReflectionUtil; +import com.intellij.util.containers.SmartHashSet; import com.intellij.util.messages.MessageBus; import gnu.trove.THashMap; import org.jdom.Element; @@ -45,7 +48,6 @@ public abstract class ComponentStoreImpl implements IComponentStore { private static final Logger LOG = Logger.getInstance(ComponentStoreImpl.class); private final Map myComponents = Collections.synchronizedMap(new THashMap()); private final List mySettingsSavingComponents = Collections.synchronizedList(new ArrayList()); - @Nullable private SaveSessionImpl mySession; @Nullable protected abstract StateStorage getDefaultsStorage(); @@ -84,48 +86,42 @@ public abstract class ComponentStoreImpl implements IComponentStore { } @Override - public boolean isSaving() { - return mySession != null; - } + @Nullable + public final ComponentSaveSession startSave() { + if (myComponents.isEmpty()) { + return null; + } + StateStorageManager storageManager = getStateStorageManager(); + StateStorageManager.ExternalizationSession externalizationSession = storageManager.startExternalization(); + + String[] names = ArrayUtilRt.toStringArray(myComponents.keySet()); + Arrays.sort(names); + for (String name : names) { + Object component = myComponents.get(name); + if (component instanceof PersistentStateComponent) { + commitPersistentComponent((PersistentStateComponent)component, externalizationSession); + } + else if (component instanceof JDOMExternalizable) { + externalizationSession.setStateInOldStorage(component, ComponentManagerImpl.getComponentName(component), component); + } + } + + StateStorageManager.SaveSession storageManagerSaveSession = storageManager.startSave(externalizationSession); + if (storageManagerSaveSession == null) { + return null; + } - @Override - @NotNull - public ComponentSaveSession startSave() { SaveSessionImpl session = createSaveSession(); - try { - session.commit(); - } - catch (Throwable e) { - try { - session.reset(); - } - catch (Exception e1_ignored) { - LOG.info(e1_ignored); - } - - PluginId pluginId = IdeErrorsDialog.findPluginId(e); - if (pluginId != null) { - throw new PluginException(e, pluginId); - } - - throw new StateStorageException(e); - } - mySession = session; - return mySession; + session.myStorageManagerSaveSession = storageManagerSaveSession; + return session; } - protected SaveSessionImpl createSaveSession() throws StateStorageException { + protected SaveSessionImpl createSaveSession() { return new SaveSessionImpl(); } - public void finishSave(@NotNull final ComponentSaveSession saveSession) { - assert mySession == saveSession; - mySession.finishSave(); - mySession = null; - } - - private void commitPersistentComponent(@NotNull final PersistentStateComponent persistentStateComponent, + private void commitPersistentComponent(@NotNull PersistentStateComponent persistentStateComponent, @NotNull StateStorageManager.ExternalizationSession session) { T state = persistentStateComponent.getState(); if (state != null) { @@ -147,11 +143,14 @@ public abstract class ComponentStoreImpl implements IComponentStore { loadJdomDefaults(component, componentName); StateStorage stateStorage = getStateStorageManager().getOldStorage(component, componentName, StateStorageOperation.READ); + if (stateStorage == null) { + return null; + } - if (stateStorage == null) return null; Element element = getJdomState(component, componentName, stateStorage); - - if (element == null) return null; + if (element == null) { + return null; + } try { if (LOG.isDebugEnabled()) { @@ -176,13 +175,17 @@ public abstract class ComponentStoreImpl implements IComponentStore { myComponents.put(componentName, component); } - private void loadJdomDefaults(@NotNull final Object component, final String componentName) { + private void loadJdomDefaults(@NotNull Object component, @NotNull String componentName) { try { StateStorage defaultsStorage = getDefaultsStorage(); - if (defaultsStorage == null) return; + if (defaultsStorage == null) { + return; + } Element defaultState = getJdomState(component, componentName, defaultsStorage); - if (defaultState == null) return; + if (defaultState == null) { + return; + } ((JDOMExternalizable)component).readExternal(defaultState); } @@ -192,8 +195,7 @@ public abstract class ComponentStoreImpl implements IComponentStore { } @Nullable - private static Element getJdomState(final Object component, final String componentName, @NotNull final StateStorage defaultsStorage) - throws StateStorageException { + private static Element getJdomState(final Object component, @NotNull String componentName, @NotNull StateStorage defaultsStorage) { ComponentRoamingManager roamingManager = ComponentRoamingManager.getInstance(); if (component instanceof RoamingTypeDisabled) { roamingManager.setRoamingType(componentName, RoamingType.DISABLED); @@ -351,7 +353,6 @@ public abstract class ComponentStoreImpl implements IComponentStore { protected StateStorageManager.SaveSession myStorageManagerSaveSession; public SaveSessionImpl() { - ShutDownTracker.getInstance().registerStopperThread(Thread.currentThread()); } @NotNull @@ -374,55 +375,8 @@ public abstract class ComponentStoreImpl implements IComponentStore { @Override public void finishSave() { - try { - getStateStorageManager().finishSave(myStorageManagerSaveSession); - myStorageManagerSaveSession = null; - } - finally { - ShutDownTracker.getInstance().unregisterStopperThread(Thread.currentThread()); - mySession = null; - } - } - - @Override - public void reset() { - try { - getStateStorageManager().reset(); - myStorageManagerSaveSession = null; - } - finally { - ShutDownTracker.getInstance().unregisterStopperThread(Thread.currentThread()); - mySession = null; - } - } - - protected void commit() { - final StateStorageManager storageManager = getStateStorageManager(); - final StateStorageManager.ExternalizationSession session = storageManager.startExternalization(); - - String[] names = ArrayUtil.toStringArray(myComponents.keySet()); - Arrays.sort(names); - for (String name : names) { - Object component = myComponents.get(name); - if (component instanceof PersistentStateComponent) { - commitPersistentComponent((PersistentStateComponent)component, session); - } - else if (component instanceof JDOMExternalizable) { - session.setStateInOldStorage(component, ComponentManagerImpl.getComponentName(component), component); - } - } - myStorageManagerSaveSession = storageManager.startSave(session); - } - - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { - return myStorageManagerSaveSession.analyzeExternalChanges(changedFiles); - } - - @Override - public void collectAllStorageFiles(boolean includingSubStructures, @NotNull List files) { - myStorageManagerSaveSession.collectAllStorageFiles(files); + getStateStorageManager().finishSave(myStorageManagerSaveSession); + myStorageManagerSaveSession = null; } } @@ -455,64 +409,61 @@ public abstract class ComponentStoreImpl implements IComponentStore { } @Override - public void reinitComponents(@NotNull final Set componentNames, final boolean reloadData) { - for (String componentName : componentNames) { - final PersistentStateComponent component = (PersistentStateComponent)myComponents.get(componentName); - if (component != null) { - initPersistentComponent(component, reloadData); - } + public final void reinitComponents(@NotNull Set componentNames, boolean reloadData) { + reinitComponents(componentNames, Collections.emptySet(), reloadData); + } + + protected boolean reinitComponent(@NotNull String componentName, boolean reloadData) { + PersistentStateComponent component = (PersistentStateComponent)myComponents.get(componentName); + if (component == null) { + return false; + } + else { + initPersistentComponent(component, reloadData); + return true; } } - protected void doReload(@NotNull Set> changedFiles, @NotNull Set componentNames) { - for (Pair pair : changedFiles) { - assert pair != null; - StateStorage storage = pair.second; - assert storage != null : "Null storage for: " + pair.first; - storage.reload(componentNames); - } - } + @NotNull + protected abstract MessageBus getMessageBus(); @Nullable - protected final Collection reload(@NotNull Set> changedFiles, @NotNull MessageBus messageBus) { - ComponentSaveSession saveSession = startSave(); - Set componentNames; + protected final Collection doReload(@NotNull Set> changedFiles) { + Set componentNames = new SmartHashSet(); + for (Pair pair : changedFiles) { + StateStorage storage = pair.second; + try { + // we must update (reload in-memory storage data) even if non-reloadable component will be detected later + // not saved -> user does own modification -> new (on disk) state will be overwritten and not applied + storage.analyzeExternalChangesAndUpdateIfNeed(changedFiles, componentNames); + } + catch (Throwable e) { + LOG.error(e); + } + } + + if (componentNames.isEmpty()) { + return Collections.emptySet(); + } + + Collection notReloadableComponents = getNotReloadableComponents(componentNames); + reinitComponents(componentNames, notReloadableComponents, false); + return notReloadableComponents.isEmpty() ? null : notReloadableComponents; + } + + @Override + public final void reinitComponents(@NotNull Set componentNames, @NotNull Collection notReloadableComponents, boolean reloadData) { + MessageBus messageBus = getMessageBus(); + messageBus.syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateStarted(); try { - componentNames = saveSession.analyzeExternalChanges(changedFiles); - if (componentNames == null) { - return Collections.emptyList(); - } - - for (Pair pair : changedFiles) { - if (pair.second == null) { - return Collections.emptyList(); + for (String componentName : componentNames) { + if (!notReloadableComponents.contains(componentName)) { + reinitComponent(componentName, reloadData); } } - - Collection currentNotReloadableComponents = getNotReloadableComponents(componentNames); - - StorageUtil.logStateDiffInfo(changedFiles, componentNames); - - if (!currentNotReloadableComponents.isEmpty()) { - return currentNotReloadableComponents; - } } finally { - finishSave(saveSession); + messageBus.syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished(); } - - if (!componentNames.isEmpty()) { - messageBus.syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateStarted(); - - try { - doReload(changedFiles, componentNames); - reinitComponents(componentNames, false); - } - finally { - messageBus.syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished(); - } - } - - return null; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java index 424cf47f374d..3df9fa12c28c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java @@ -20,8 +20,6 @@ import com.intellij.openapi.options.StreamProvider; import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.project.impl.ProjectManagerImpl; import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.VirtualFile; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -30,8 +28,6 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collection; import java.util.Collections; -import java.util.List; -import java.util.Set; //todo: extends from base store class public class DefaultProjectStoreImpl extends ProjectStoreImpl { @@ -77,8 +73,21 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { } @Override - protected MySaveSession createSaveSession(final MyExternalizationSession externalizationSession) { - return new DefaultSaveSession(externalizationSession); + protected MySaveSession createSaveSession(@NotNull StorageData storageData) { + return new MySaveSession(storageData) { + @Override + protected void doSave(@Nullable Element element) { + myProjectManager.setDefaultProjectRootElement(element == null ? null : element); + } + + // we must not collapse paths here, because our solution is just a big hack + // by default, getElementToSave() returns collapsed paths -> setDefaultProjectRootElement -> project manager writeExternal -> save -> compare old and new - diff because old has expanded, but new collapsed + // -> needless save + @Override + protected boolean isCollapsePathsOnSave() { + return false; + } + }; } @Override @@ -86,22 +95,6 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { protected StorageData createStorageData() { return new BaseStorageData(ROOT_TAG_NAME); } - - class DefaultSaveSession extends MySaveSession { - public DefaultSaveSession(MyExternalizationSession externalizationSession) { - super(externalizationSession); - } - - @Override - protected void doSave() throws StateStorageException { - Element element = getElementToSave(); - myProjectManager.setDefaultProjectRootElement(element == null ? null : element); - } - - @Override - public void collectAllStorageFiles(@NotNull List files) { - } - } }; //noinspection deprecation @@ -151,15 +144,15 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { return new MyExternalizationSession(storage); } - @NotNull + @Nullable @Override public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { - return new MySaveSession(storage, externalizationSession); + StateStorage.SaveSession saveSession = storage.startSave(((MyExternalizationSession)externalizationSession).externalizationSession); + return saveSession == null ? null : new MySaveSession(saveSession); } @Override public void finishSave(@NotNull SaveSession saveSession) { - storage.finishSave(((MySaveSession)saveSession).saveSession); } @NotNull @@ -201,10 +194,6 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { public Collection getStorageFileNames() { throw new UnsupportedOperationException("Method getStorageFileNames not implemented in " + getClass()); } - - @Override - public void reset() { - } }; } @@ -236,23 +225,12 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { private static class MySaveSession implements StateStorageManager.SaveSession { @NotNull private final StateStorage.SaveSession saveSession; - public MySaveSession(@NotNull XmlElementStorage storage, @NotNull StateStorageManager.ExternalizationSession externalizationSession) { - saveSession = storage.startSave(((MyExternalizationSession)externalizationSession).externalizationSession); - } - - //returns set of component which were changed, null if changes are much more than just component state. - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull Set> files) { - throw new UnsupportedOperationException("Method analyzeExternalChanges not implemented in " + getClass()); + public MySaveSession(@NotNull StateStorage.SaveSession saveSession) { + this.saveSession = saveSession; } @Override - public void collectAllStorageFiles(@NotNull List files) { - } - - @Override - public void save() throws StateStorageException { + public void save() { saveSession.save(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java index 1e1363633875..8a28e1dd36aa 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java @@ -20,6 +20,8 @@ import com.intellij.openapi.components.PathMacroManager; import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.xmlb.JDOMXIncluder; import org.jdom.Document; import org.jdom.Element; @@ -43,7 +45,9 @@ class DefaultsStateStorage implements StateStorage { @Nullable public Element getState(final Object component, final String componentName) throws StateStorageException { final URL url = DecodeDefaultsUtil.getDefaults(component, componentName); - if (url == null) return null; + if (url == null) { + return null; + } try { Document document = JDOMUtil.loadDocument(url); @@ -64,33 +68,29 @@ class DefaultsStateStorage implements StateStorage { } } + @Override @Nullable - public T getState(final Object component, @NotNull final String componentName, final Class stateClass, @Nullable final T mergeInto) throws - StateStorageException { + public T getState(final Object component, @NotNull final String componentName, @NotNull final Class stateClass, @Nullable final T mergeInto) { return DefaultStateSerializer.deserializeState(getState(component, componentName), stateClass, mergeInto); } - public boolean hasState(final Object component, @NotNull final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { - final URL url = DecodeDefaultsUtil.getDefaults(component, componentName); - return url != null; + @Override + public boolean hasState(@Nullable final Object component, @NotNull final String componentName, final Class aClass, final boolean reloadData) { + return DecodeDefaultsUtil.getDefaults(component, componentName) != null; } + @Override @NotNull public ExternalizationSession startExternalization() { throw new UnsupportedOperationException("Method startExternalization not implemented in " + getClass()); } - @NotNull + @Override public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { - throw new UnsupportedOperationException("Method startSave not implemented in " + getClass()); + return null; } - public void finishSave(@NotNull SaveSession saveSession) { - throw new UnsupportedOperationException("Method finishSave not implemented in " + getClass()); + @Override + public void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set> changedFiles, @NotNull Set result) { } - - public void reload(@NotNull Set changedComponents) { - throw new UnsupportedOperationException("Method reload not implemented in " + getClass()); - } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java index 35115eeb0f5a..7393926d1c6a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java @@ -30,19 +30,13 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileAdapter; import com.intellij.openapi.vfs.VirtualFileEvent; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.SmartHashSet; -import com.intellij.util.messages.MessageBus; -import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.picocontainer.PicoContainer; import java.io.File; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.Set; //todo: support missing plugins @@ -55,42 +49,41 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { private final StateSplitter mySplitter; private final FileTypeManager myFileTypeManager; - private Object mySession; private DirectoryStorageData myStorageData = null; public DirectoryBasedStorage(@Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, @NotNull String dir, @NotNull StateSplitter splitter, @NotNull Disposable parentDisposable, - @NotNull PicoContainer picoContainer) { + @Nullable final Listener listener) { myPathMacroSubstitutor = pathMacroSubstitutor; myDir = new File(dir); mySplitter = splitter; Disposer.register(parentDisposable, this); - VirtualFileTracker virtualFileTracker = (VirtualFileTracker)picoContainer.getComponentInstanceOfType(VirtualFileTracker.class); - MessageBus messageBus = (MessageBus)picoContainer.getComponentInstanceOfType(MessageBus.class); - - if (virtualFileTracker != null && messageBus != null) { + VirtualFileTracker virtualFileTracker = ServiceManager.getService(VirtualFileTracker.class); + if (virtualFileTracker != null && listener != null) { final String path = myDir.getAbsolutePath(); final String fileUrl = LocalFileSystem.PROTOCOL_PREFIX + path.replace(File.separatorChar, '/'); - final Listener listener = messageBus.syncPublisher(STORAGE_TOPIC); virtualFileTracker.addTracker(fileUrl, new VirtualFileAdapter() { @Override public void contentsChanged(@NotNull final VirtualFileEvent event) { - if (!StringUtil.endsWithIgnoreCase(event.getFile().getName(), ".xml")) return; + if (!StringUtilRt.endsWithIgnoreCase(event.getFile().getNameSequence(), ".xml")) return; + assert listener != null; listener.storageFileChanged(event, DirectoryBasedStorage.this); } @Override public void fileDeleted(@NotNull final VirtualFileEvent event) { - if (!StringUtil.endsWithIgnoreCase(event.getFile().getName(), ".xml")) return; + if (!StringUtilRt.endsWithIgnoreCase(event.getFile().getNameSequence(), ".xml")) return; + assert listener != null; listener.storageFileChanged(event, DirectoryBasedStorage.this); } @Override public void fileCreated(@NotNull final VirtualFileEvent event) { - if (!StringUtil.endsWithIgnoreCase(event.getFile().getName(), ".xml")) return; + if (!StringUtilRt.endsWithIgnoreCase(event.getFile().getNameSequence(), ".xml")) return; + assert listener != null; listener.storageFileChanged(event, DirectoryBasedStorage.this); } }, false, this); @@ -100,11 +93,30 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } @Override - @Nullable - public T getState(final Object component, @NotNull final String componentName, Class stateClass, @Nullable T mergeInto) - throws StateStorageException { - if (myStorageData == null) myStorageData = loadState(); + public void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set> changedFiles, @NotNull Set result) { + boolean containsSelf = false; + for (Pair pair : changedFiles) { + if (pair.second == this && StringUtilRt.endsWithIgnoreCase(pair.first.getNameSequence(), ".xml")) { + containsSelf = true; + break; + } + } + if (!containsSelf) { + return; + } + + // todo reload only changed file, compute diff + myStorageData = loadState(); + result.addAll(myStorageData.getComponentNames()); + } + + @Override + @Nullable + public T getState(final Object component, @NotNull final String componentName, @NotNull Class stateClass, @Nullable T mergeInto) { + if (myStorageData == null) { + myStorageData = loadState(); + } if (!myStorageData.containsComponent(componentName)) { return DefaultStateSerializer.deserializeState(new Element(StorageData.COMPONENT), stateClass, mergeInto); @@ -119,11 +131,14 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { return storageData; } - @Override - public boolean hasState(final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { - if (!myDir.exists()) return false; - if (reloadData) myStorageData = null; + public boolean hasState(@Nullable final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) { + if (!myDir.exists()) { + return false; + } + if (reloadData) { + myStorageData = null; + } return true; } @@ -138,35 +153,13 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { LOG.error(e); } } - final ExternalizationSession session = new MyExternalizationSession(myStorageData.clone()); - - mySession = session; - return session; + return new MyExternalizationSession(myStorageData.clone()); } + @Nullable @Override - @NotNull - public SaveSession startSave(@NotNull final ExternalizationSession externalizationSession) { - assert mySession == externalizationSession; - - final MySaveSession session = - new MySaveSession(((MyExternalizationSession)externalizationSession).myStorageData, myPathMacroSubstitutor); - mySession = session; - return session; - } - - @Override - public void finishSave(@NotNull final SaveSession saveSession) { - try { - LOG.assertTrue(mySession == saveSession); - } finally { - mySession = null; - } - } - - @Override - public void reload(@NotNull Set changedComponents) { - myStorageData = loadState(); + public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { + return new MySaveSession(((MyExternalizationSession)externalizationSession).myStorageData, myPathMacroSubstitutor); } @Override @@ -177,14 +170,13 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { private final DirectoryStorageData myStorageData; private final TrackingPathMacroSubstitutor myPathMacroSubstitutor; - private MySaveSession(final DirectoryStorageData storageData, final TrackingPathMacroSubstitutor pathMacroSubstitutor) { + private MySaveSession(@NotNull DirectoryStorageData storageData, @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor) { myStorageData = storageData; myPathMacroSubstitutor = pathMacroSubstitutor; } @Override public void save() throws StateStorageException { - assert mySession == this; final Set currentNames = new SmartHashSet(); File[] children = myDir.listFiles(); if (children != null) { @@ -206,7 +198,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } if (file.lastModified() <= myStorageData.getLastTimeStamp()) { - StorageUtil.save(file, element, MySaveSession.this, false, null); + StorageUtil.save(file, element, MySaveSession.this, false, LocalFileSystem.getInstance().findFileByIoFile(file)); myStorageData.updateLastTimestamp(file); } } @@ -243,39 +235,6 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { myStorageData.clear(); } - - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull Set> changedFiles) { - boolean containsSelf = false; - for (Pair pair : changedFiles) { - if (pair.second == DirectoryBasedStorage.this) { - if (StringUtilRt.endsWithIgnoreCase(pair.first.getNameSequence(), ".xml")) { - containsSelf = true; - break; - } - } - } - - if (!containsSelf) { - return Collections.emptySet(); - } - - if (myStorageData.getComponentNames().isEmpty()) { - // no state yet, so try to initialize it now - return new THashSet(loadState().getComponentNames()); - } - else { - return new THashSet(myStorageData.getComponentNames()); - } - } - - @Override - public void collectAllStorageFiles(@NotNull List files) { - for (File file : myStorageData.getAllStorageFiles().keySet()) { - ContainerUtil.addIfNotNull(files, LocalFileSystem.getInstance().findFileByIoFile(file)); - } - } } private class MyExternalizationSession implements ExternalizationSession { @@ -287,29 +246,30 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override public void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, Storage storageSpec) { - assert mySession == this; - setState(componentName, state, storageSpec); - } - - private void setState(final String componentName, @NotNull Object state, final Storage storageSpec) { + Element element; try { - final Element element = DefaultStateSerializer.serializeState(state, storageSpec); - if (element != null) { - for (Pair pair : mySplitter.splitState(element)) { - Element e = pair.first; - String name = pair.second; - - Element statePart = new Element(StorageData.COMPONENT); - statePart.setAttribute(StorageData.NAME, componentName); - statePart.addContent(e.detach()); - - myStorageData.put(componentName, new File(myDir, name), statePart, false); - } - } + element = DefaultStateSerializer.serializeState(state, storageSpec); } catch (WriteExternalException e) { throw new StateStorageException(e); } + catch (Throwable e) { + LOG.info("Unable to serialize component state!", e); + return; + } + + if (element != null) { + for (Pair pair : mySplitter.splitState(element)) { + Element e = pair.first; + String name = pair.second; + + Element statePart = new Element(StorageData.COMPONENT); + statePart.setAttribute(StorageData.NAME, componentName); + statePart.addContent(e.detach()); + + myStorageData.put(componentName, new File(myDir, name), statePart, false); + } + } } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java index 05411fd50471..9246976fd136 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java @@ -22,23 +22,24 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.messages.MessageBus; +import com.intellij.util.LineSeparator; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.picocontainer.PicoContainer; import java.io.File; import java.io.IOException; -import java.util.List; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Collections; import java.util.Set; public class FileBasedStorage extends XmlElementStorage { @@ -47,6 +48,7 @@ public class FileBasedStorage extends XmlElementStorage { private final String myFilePath; private final File myFile; private volatile VirtualFile myCachedVirtualFile; + private LineSeparator myLineSeparator; public FileBasedStorage(@NotNull String filePath, @NotNull String fileSpec, @@ -54,7 +56,7 @@ public class FileBasedStorage extends XmlElementStorage { @Nullable TrackingPathMacroSubstitutor pathMacroManager, @NotNull String rootElementName, @NotNull Disposable parentDisposable, - PicoContainer picoContainer, + @Nullable final Listener listener, @Nullable StreamProvider streamProvider, ComponentVersionProvider componentVersionProvider) { super(fileSpec, roamingType, pathMacroManager, parentDisposable, rootElementName, streamProvider, componentVersionProvider); @@ -62,81 +64,88 @@ public class FileBasedStorage extends XmlElementStorage { myFilePath = filePath; myFile = new File(filePath); - VirtualFileTracker virtualFileTracker = ServiceManager.getService(VirtualFileTracker.class); - MessageBus messageBus = (MessageBus)picoContainer.getComponentInstanceOfType(MessageBus.class); - if (virtualFileTracker != null && messageBus != null) { - final Listener listener = messageBus.syncPublisher(STORAGE_TOPIC); - virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + myFile.getAbsolutePath().replace(File.separatorChar, '/'), new VirtualFileAdapter() { - @Override - public void fileMoved(@NotNull VirtualFileMoveEvent event) { - myCachedVirtualFile = null; - } - - @Override - public void fileDeleted(@NotNull VirtualFileEvent event) { - myCachedVirtualFile = null; - } - - @Override - public void fileCreated(@NotNull VirtualFileEvent event) { - myCachedVirtualFile = event.getFile(); - } - - @Override - public void contentsChanged(@NotNull final VirtualFileEvent event) { - if (!isDisposed()) { - listener.storageFileChanged(event, FileBasedStorage.this); + if (listener != null) { + VirtualFileTracker virtualFileTracker = ServiceManager.getService(VirtualFileTracker.class); + if (virtualFileTracker != null) { + virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + myFile.getAbsolutePath().replace(File.separatorChar, '/'), new VirtualFileAdapter() { + @Override + public void fileMoved(@NotNull VirtualFileMoveEvent event) { + myCachedVirtualFile = null; } - } - }, false, this); + + @Override + public void fileDeleted(@NotNull VirtualFileEvent event) { + myCachedVirtualFile = null; + } + + @Override + public void fileCreated(@NotNull VirtualFileEvent event) { + myCachedVirtualFile = event.getFile(); + } + + @Override + public void contentsChanged(@NotNull final VirtualFileEvent event) { + if (!isDisposed()) { + assert listener != null; + listener.storageFileChanged(event, FileBasedStorage.this); + } + } + }, false, this); + } } } + protected boolean isUseXmlProlog() { + return false; + } + + protected boolean isUseLfLineSeparatorByDefault() { + return isUseXmlProlog(); + } + @Override - protected MySaveSession createSaveSession(final MyExternalizationSession externalizationSession) { - return new FileSaveSession(externalizationSession); + protected MySaveSession createSaveSession(@NotNull StorageData storageData) { + return new FileSaveSession(storageData); } private class FileSaveSession extends MySaveSession { - protected FileSaveSession(MyExternalizationSession externalizationSession) { - super(externalizationSession); + protected FileSaveSession(@NotNull StorageData storageData) { + super(storageData); } @Override - protected boolean physicalContentNeedsSave() { - VirtualFile file = getVirtualFile(); - if (file == null || !file.exists()) { - return !myStorageData.isEmpty(); + protected void doSave(@Nullable Element element) throws IOException { + if (myLineSeparator == null) { + myLineSeparator = isUseLfLineSeparatorByDefault() ? LineSeparator.LF : LineSeparator.getSystemLineSeparator(); } - Element element = getElementToSave(); - return element == null || !StorageUtil.contentEquals(element, file); - } - @Override - protected int calcHash() { - int hash = myStorageData.getHash(); - if (myPathMacroSubstitutor != null) { - hash = 31 * hash + myPathMacroSubstitutor.hashCode(); - } - return hash; - } - - @Override - protected void doSave() throws StateStorageException { - if (myBlockSavingTheContent) { - return; - } - if (ApplicationManager.getApplication().isUnitTestMode() && myFile != null && StringUtil.startsWithChar(myFile.getPath(), '$')) { + BufferExposingByteArrayOutputStream content = element == null ? null : StorageUtil.writeToBytes(element, myLineSeparator.getSeparatorString()); + if (ApplicationManager.getApplication().isUnitTestMode() && StringUtil.startsWithChar(myFile.getPath(), '$')) { throw new StateStorageException("It seems like some macros were not expanded for path: " + myFile.getPath()); } - LOG.assertTrue(myFile != null); - myCachedVirtualFile = StorageUtil.save(myFile, getElementToSave(), this, true, myCachedVirtualFile); - } + try { + if (myStreamProvider != null && myStreamProvider.isEnabled()) { + // stream provider always use LF separator + saveForProvider(myLineSeparator == LineSeparator.LF ? content : null, element); + } + } + catch (Throwable e) { + LOG.error(e); + } - @Override - public void collectAllStorageFiles(@NotNull List files) { - ContainerUtil.addIfNotNull(files, getVirtualFile()); + if (content == null) { + StorageUtil.deleteFile(myFile, this, getVirtualFile()); + myCachedVirtualFile = null; + } + else { + VirtualFile file = getVirtualFile(); + if (file == null || !file.exists()) { + FileUtil.createParentDirs(myFile); + file = null; + } + myCachedVirtualFile = StorageUtil.writeFile(myFile, this, file, content, isUseXmlProlog() ? myLineSeparator : null); + } } } @@ -205,28 +214,31 @@ public class FileBasedStorage extends XmlElementStorage { if (file.getLength() == 0) { return processReadException(null); } - return StorageData.load(file); + + CharBuffer charBuffer = CharsetToolkit.UTF8_CHARSET.decode(ByteBuffer.wrap(file.contentsToByteArray())); + myLineSeparator = StorageUtil.detectLineSeparators(charBuffer, isUseLfLineSeparatorByDefault() ? null : LineSeparator.LF); + return JDOMUtil.loadDocument(charBuffer).getRootElement(); } - catch (final JDOMException e) { + catch (JDOMException e) { return processReadException(e); } - catch (final IOException e) { + catch (IOException e) { return processReadException(e); } } @Nullable - private Element processReadException(@Nullable final Exception e) { + private Element processReadException(@Nullable Exception e) { boolean contentTruncated = e == null; myBlockSavingTheContent = isProjectOrModuleOrWorkspaceFile() && !contentTruncated; if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) { if (e != null) { LOG.info(e); } - final String message = "Cannot load settings from file '" + myFile.getPath() + "': " + (e == null ? "content truncated" : e.getLocalizedMessage()) + "\n" + - getInvalidContentMessage(contentTruncated); Notifications.Bus.notify( - new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Load Settings", message, NotificationType.WARNING)); + new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Load Settings", + "Cannot load settings from file '" + myFile.getPath() + "': " + (e == null ? "content truncated" : e.getLocalizedMessage()) + "\n" + + getInvalidContentMessage(contentTruncated), NotificationType.WARNING)); } return null; @@ -255,26 +267,26 @@ public class FileBasedStorage extends XmlElementStorage { StorageUtil.deleteFile(myFile, this, myCachedVirtualFile); // if data was loaded, mark as changed all loaded components if (myLoadedData != null) { - changedComponentNames.addAll(myLoadedData.myComponentStates.keySet()); - resetData(); + changedComponentNames.addAll(myLoadedData.getComponentNames()); + myLoadedData = null; } } else if (myLoadedData != null) { StorageData newStorageData = createStorageData(); loadState(newStorageData, newElement); - changedComponentNames.addAll(newStorageData.getChangedComponentNames(myLoadedData, myPathMacroSubstitutor)); + changedComponentNames.addAll(myLoadedData.getChangedComponentNames(newStorageData, myPathMacroSubstitutor)); myLoadedData = newStorageData; } } - catch (Exception e) { - LOG.warn(e); + catch (Throwable e) { + LOG.error(e); } } @Nullable @Deprecated public File updateFileExternallyFromStreamProviders() throws IOException { - Element element = getElement(loadData(true)); + Element element = getElement(loadData(true), true, Collections.emptyMap()); if (element == null) { FileUtil.delete(myFile); return null; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java index ff972dac785a..e425d9271bbc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java @@ -21,7 +21,6 @@ import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.IOException; import java.util.Collection; import java.util.Set; @@ -39,5 +38,5 @@ public interface IApplicationStore extends IComponentStore { * list of not reloadable components (reload is not performed) */ @Nullable - Collection reload(@NotNull Set> changedFiles) throws IOException; + Collection reload(@NotNull Set> changedFiles); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java index 6c8cfd22cb84..0c5fa2011c50 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java @@ -18,6 +18,7 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.store.ComponentSaveSession; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collection; @@ -28,6 +29,8 @@ public interface IComponentStore { void reinitComponents(@NotNull Set componentNames, boolean reloadData); + void reinitComponents(@NotNull Set componentNames, @NotNull Collection notReloadableComponents, boolean reloadData); + @NotNull Collection getNotReloadableComponents(@NotNull Collection componentNames); @@ -35,8 +38,6 @@ public interface IComponentStore { void load() throws IOException, StateStorageException; - boolean isSaving(); - @NotNull StateStorageManager getStateStorageManager(); @@ -49,6 +50,6 @@ public interface IComponentStore { } } - @NotNull + @Nullable ComponentSaveSession startSave(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IProjectStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IProjectStore.java index 3f9e0c68a74e..a6c50f22a9e7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IProjectStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IProjectStore.java @@ -55,7 +55,7 @@ public interface IProjectStore extends IComponentStore { @Nullable String getPresentableUrl(); - boolean reload(@NotNull Set> changedFiles) throws IOException; + boolean reload(@NotNull Set> changedFiles); //------ This methods should be got rid of /** @deprecated to remove in IDEA 14 */ diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java index 256bdba22b43..10154b34f218 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java @@ -72,4 +72,10 @@ class ProjectStateStorageManager extends StateStorageManagerImpl { private static boolean isWorkspace(final Map options) { return options != null && Boolean.parseBoolean((String)options.get(ProjectStoreImpl.OPTION_WORKSPACE)); } + + @NotNull + @Override + protected StateStorage.Listener createStorageTopicListener() { + return myProject.getMessageBus().syncPublisher(StateStorage.PROJECT_STORAGE_TOPIC); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java index e108ccf3fcd2..e0fbd85adb20 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java @@ -39,6 +39,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.util.PathUtilRt; import com.intellij.util.containers.OrderedSet; +import com.intellij.util.messages.MessageBus; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; @@ -460,7 +461,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @Override - protected SaveSessionImpl createSaveSession() throws StateStorageException { + protected SaveSessionImpl createSaveSession() { return new ProjectSaveSession(); } @@ -630,7 +631,13 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @Override - public boolean reload(@NotNull Set> changedFiles) throws IOException { - return reload(changedFiles, myProject.getMessageBus()) == null; + public boolean reload(@NotNull Set> changedFiles) { + return doReload(changedFiles) == null; + } + + @NotNull + @Override + protected MessageBus getMessageBus() { + return myProject.getMessageBus(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index d3cd06ac7143..a9cafbc09bfc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -18,14 +18,10 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.*; import com.intellij.openapi.options.StreamProvider; import com.intellij.openapi.util.Couple; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; -import java.util.List; -import java.util.Set; /** * @author mike @@ -62,7 +58,10 @@ public interface StateStorageManager { @NotNull ExternalizationSession startExternalization(); - @NotNull + /** + * return null if nothing to save + */ + @Nullable SaveSession startSave(@NotNull ExternalizationSession externalizationSession); void finishSave(@NotNull SaveSession saveSession); @@ -84,8 +83,6 @@ public interface StateStorageManager { @Nullable com.intellij.openapi.components.impl.stores.StreamProvider getStreamProvider(); - void reset(); - interface ExternalizationSession { void setState(@NotNull Storage[] storageSpecs, @NotNull Object component, @NotNull String componentName, @NotNull Object state); @@ -93,12 +90,6 @@ public interface StateStorageManager { } interface SaveSession { - // returns set of component which were changed, null if changes are much more than just component state - @Nullable - Set analyzeExternalChanges(@NotNull Set> files); - - void collectAllStorageFiles(@NotNull List files); - - void save() throws StateStorageException; + void save(); } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java index 13ff1c700a02..e942479c5b57 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java @@ -22,16 +22,18 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.CurrentUserHolder; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.RoamingTypeDisabled; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ObjectUtils; import com.intellij.util.PathUtilRt; import com.intellij.util.ReflectionUtil; import com.intellij.util.SmartList; +import com.intellij.util.messages.MessageBus; import gnu.trove.THashMap; -import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; import org.jdom.Document; import org.jdom.Element; @@ -67,7 +69,6 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di private final Map myPathToStorage = new THashMap(); private final TrackingPathMacroSubstitutor myPathMacroSubstitutor; private final String myRootTagName; - private Object mySession; private final PicoContainer myPicoContainer; private TObjectLongHashMap myComponentVersions; @@ -253,11 +254,11 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Nullable private StateStorage createDirectoryStateStorage(String file, Class splitterClass) { StateSplitter splitter = ReflectionUtil.newInstance(splitterClass); - return new DirectoryBasedStorage(myPathMacroSubstitutor, expandMacros(file), splitter, this, myPicoContainer); + return new DirectoryBasedStorage(myPathMacroSubstitutor, expandMacros(file), splitter, this, createStorageTopicListener()); } @Nullable - private StateStorage createFileStateStorage(@NotNull final String fileSpec, @Nullable RoamingType roamingType) { + private StateStorage createFileStateStorage(@NotNull String fileSpec, @Nullable RoamingType roamingType) { String expandedFile = expandMacros(fileSpec); if (!ourHeadlessEnvironment && PathUtilRt.getFileName(expandedFile).lastIndexOf('.') < 0) { @@ -270,15 +271,30 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di beforeFileBasedStorageCreate(); return new FileBasedStorage(expandedFile, fileSpec, roamingType, getMacroSubstitutor(fileSpec), myRootTagName, this, - myPicoContainer, getStreamProvider(), this) { + createStorageTopicListener(), getStreamProvider(), this) { @Override @NotNull protected StorageData createStorageData() { - return StateStorageManagerImpl.this.createStorageData(fileSpec); + return StateStorageManagerImpl.this.createStorageData(myFileSpec); + } + + @Override + protected boolean isUseXmlProlog() { + return StateStorageManagerImpl.this.isUseXmlProlog(); } }; } + @Nullable + protected StateStorage.Listener createStorageTopicListener() { + MessageBus messageBus = (MessageBus)myPicoContainer.getComponentInstanceOfType(MessageBus.class); + return messageBus == null ? null : messageBus.syncPublisher(StateStorage.STORAGE_TOPIC); + } + + protected boolean isUseXmlProlog() { + return true; + } + protected void beforeFileBasedStorageCreate() { } @@ -355,7 +371,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di return myPathMacroSubstitutor; } - protected abstract StorageData createStorageData(String storageSpec); + protected abstract StorageData createStorageData(@NotNull String storageSpec); private static final Pattern MACRO_PATTERN = Pattern.compile("(\\$[^\\$]*\\$)"); @@ -390,47 +406,56 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @NotNull @Override public ExternalizationSession startExternalization() { - if (mySession != null) { - LOG.error("Starting duplicate externalization session: " + mySession); - } - ExternalizationSession session = new MyExternalizationSession(); - mySession = session; - return session; + return new StateStorageManagerExternalizationSession(); } - @NotNull + @Nullable @Override public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { - assert mySession == externalizationSession; - SaveSession session = new MySaveSession((MyExternalizationSession)externalizationSession); - mySession = session; - return session; + StateStorageManagerExternalizationSession myExternalizationSession = (StateStorageManagerExternalizationSession)externalizationSession; + Map saveSessions = null; + for (StateStorage stateStorage : myExternalizationSession.mySessions.keySet()) { + StateStorage.SaveSession saveSession = stateStorage.startSave(myExternalizationSession.getExternalizationSession(stateStorage)); + if (saveSession != null) { + if (saveSessions == null) { + saveSessions = new SmartHashMap(); + } + saveSessions.put(stateStorage, saveSession); + } + } + + if (saveSessions == null) { + return null; + } + + return new StateStorageSaveSession(saveSessions); } @Override - public void finishSave(@NotNull final SaveSession saveSession) { - try { - assert mySession == saveSession : "mySession=" + mySession + " saveSession=" + saveSession; - ((MySaveSession)saveSession).finishSave(); + public void finishSave(@NotNull SaveSession saveSession) { + if (!isDirty) { + return; } - finally { - mySession = null; - save(); + + String filePath = getNotNullVersionsFilePath(); + if (filePath != null) { + File file = new File(filePath); + FileUtilRt.createParentDirs(file); + try { + JDOMUtil.writeParent(createComponentVersionsXml(getComponentVersions()), file, "\n"); + isDirty = false; + } + catch (IOException e) { + LOG.info(e); + } } } - @Override - public void reset() { - mySession = null; - } - - protected class MyExternalizationSession implements ExternalizationSession { + private final class StateStorageManagerExternalizationSession implements ExternalizationSession { final Map mySessions = new SmartHashMap(); @Override public void setState(@NotNull Storage[] storageSpecs, @NotNull Object component, @NotNull String componentName, @NotNull Object state) { - assert mySession == this; - for (Storage storageSpec : storageSpecs) { StateStorage stateStorage = getStateStorage(storageSpec); if (stateStorage == null) { @@ -443,7 +468,6 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Override public void setStateInOldStorage(@NotNull Object component, @NotNull String componentName, @NotNull Object state) { - assert mySession == this; StateStorage stateStorage = getOldStorage(component, componentName, StateStorageOperation.WRITE); if (stateStorage != null) { getExternalizationSession(stateStorage).setState(component, componentName, state, null); @@ -471,73 +495,19 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Nullable protected abstract String getOldStorageSpec(@NotNull Object component, @NotNull String componentName, @NotNull StateStorageOperation operation); - protected class MySaveSession implements SaveSession { - private final Map mySaveSessions = new SmartHashMap(); + private final static class StateStorageSaveSession implements SaveSession { + private final Map mySaveSessions; - public MySaveSession(@NotNull MyExternalizationSession externalizationSession) { - for (StateStorage stateStorage : externalizationSession.mySessions.keySet()) { - mySaveSessions.put(stateStorage, stateStorage.startSave(externalizationSession.getExternalizationSession(stateStorage))); - } + public StateStorageSaveSession(@NotNull Map saveSessions) { + mySaveSessions = saveSessions; } @Override - public void collectAllStorageFiles(@NotNull List files) { - for (StateStorage.SaveSession saveSession : mySaveSessions.values()) { - saveSession.collectAllStorageFiles(files); - } - } - - @Override - public void save() throws StateStorageException { - assert mySession == this; + public void save() { for (StateStorage.SaveSession saveSession : mySaveSessions.values()) { saveSession.save(); } } - - public void finishSave() { - RuntimeException re = null; - try { - LOG.assertTrue(mySession == this); - } - finally { - for (StateStorage stateStorage : mySaveSessions.keySet()) { - try { - stateStorage.finishSave(mySaveSessions.get(stateStorage)); - } - catch (RuntimeException e) { - re = e; - } - } - } - - if (re != null) { - throw re; - } - } - - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull Set> changedFiles) { - Set result = null; - for (Pair pair : changedFiles) { - StateStorage.SaveSession saveSession = mySaveSessions.get(pair.second); - if (saveSession == null) { - continue; - } - - Set changes = saveSession.analyzeExternalChanges(changedFiles); - if (changes == null) { - return null; - } - - if (result == null) { - result = new THashSet(); - } - result.addAll(changes); - } - return result == null ? Collections.emptySet() : result; - } } @Override @@ -556,25 +526,6 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di myStreamProvider = streamProvider; } - public void save() { - if (!isDirty) { - return; - } - - String filePath = getNotNullVersionsFilePath(); - if (filePath != null) { - File file = new File(filePath); - FileUtilRt.createParentDirs(file); - try { - JDOMUtil.writeParent(createComponentVersionsXml(getComponentVersions()), file, "\n"); - isDirty = false; - } - catch (IOException e) { - LOG.info(e); - } - } - } - private TObjectLongHashMap getComponentVersions() { synchronized (myComponentVersionsLock) { if (myComponentVersions == null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java index 04aa057f2254..348f30eeff10 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java @@ -19,7 +19,9 @@ import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.NotificationsManager; -import com.intellij.openapi.application.*; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.components.*; import com.intellij.openapi.components.store.ReadOnlyModificationException; import com.intellij.openapi.diagnostic.Logger; @@ -35,8 +37,9 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileEvent; +import com.intellij.util.LineSeparator; import com.intellij.util.SystemProperties; -import com.intellij.util.UniqueFileNamesProvider; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jdom.Document; @@ -48,11 +51,9 @@ import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.io.*; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.nio.ByteBuffer; import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; /** * @author mike @@ -60,13 +61,17 @@ import java.util.Set; public class StorageUtil { private static final Logger LOG = Logger.getInstance(StorageUtil.class); - private static final boolean DUMP_COMPONENT_STATES = SystemProperties.getBooleanProperty("idea.log.externally.changed.component.states", false); + private static final byte[] XML_PROLOG = "".getBytes(CharsetToolkit.UTF8_CHARSET); + @SuppressWarnings("SpellCheckingInspection") - private static final SimpleDateFormat LOG_DIR_FORMAT = new SimpleDateFormat("yyyyMMdd-HHmmss"); private static final Pair NON_EXISTENT_FILE_DATA = Pair.create(null, SystemProperties.getLineSeparator()); private StorageUtil() { } + public static boolean isChangedByStorageOrSaveSession(@NotNull VirtualFileEvent event) { + return event.getRequestor() instanceof StateStorage.SaveSession || event.getRequestor() instanceof StateStorage; + } + public static void notifyUnknownMacros(@NotNull TrackingPathMacroSubstitutor substitutor, @NotNull final Project project, @Nullable String componentName) { @@ -158,53 +163,58 @@ public class StorageUtil { FileUtil.createParentDirs(file); byteOut = writeToBytes(document, SystemProperties.getLineSeparator()); } - - // mark this action as modifying the file which daemon analyzer should ignore - AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); - try { - if (virtualFile == null) { - virtualFile = getOrCreateVirtualFile(requestor, file); - } - OutputStream virtualFileOut = virtualFile.getOutputStream(requestor); - try { - byteOut.writeTo(virtualFileOut); - } - finally { - virtualFileOut.close(); - } - return virtualFile; - } - catch (FileNotFoundException e) { - if (virtualFile == null) { - throw e; - } - else { - throw new ReadOnlyModificationException(virtualFile); - } - } - finally { - token.finish(); - } + return writeFile(file, requestor, virtualFile, byteOut, null); } catch (IOException e) { throw new StateStorageException(e); } } - public static void deleteFile(@NotNull File file, @NotNull Object requestor, @Nullable VirtualFile cachedVirtualFile) throws IOException { - if (!file.exists()) { - return; + @NotNull + public static VirtualFile writeFile(@NotNull File file, @NotNull Object requestor, @Nullable VirtualFile virtualFile, @NotNull BufferExposingByteArrayOutputStream content, @Nullable LineSeparator lineSeparatorIfPrependXmlProlog) throws IOException { + // mark this action as modifying the file which daemon analyzer should ignore + AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); + try { + if (virtualFile == null || !virtualFile.isValid()) { + virtualFile = getOrCreateVirtualFile(requestor, file); + } + OutputStream out = virtualFile.getOutputStream(requestor); + try { + if (lineSeparatorIfPrependXmlProlog != null) { + out.write(XML_PROLOG); + out.write(lineSeparatorIfPrependXmlProlog.getSeparatorBytes()); + } + content.writeTo(out); + } + finally { + out.close(); + } + return virtualFile; + } + catch (FileNotFoundException e) { + if (virtualFile == null) { + throw e; + } + else { + throw new ReadOnlyModificationException(virtualFile); + } + } + finally { + token.finish(); + } + } + + public static void deleteFile(@NotNull File file, @NotNull Object requestor, @Nullable VirtualFile virtualFile) throws IOException { + if (virtualFile == null) { + LOG.warn("Cannot find virtual file " + file.getAbsolutePath()); } - VirtualFile virtualFile = cachedVirtualFile; - if (virtualFile == null || !virtualFile.isValid()) { - virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); - } if (virtualFile == null) { - LOG.info("Cannot find virtual file " + file.getAbsolutePath()); - FileUtil.delete(file); + if (file.exists()) { + FileUtil.delete(file); + } } - else { + else if (virtualFile.exists()) { AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); try { virtualFile.delete(requestor); @@ -216,14 +226,14 @@ public class StorageUtil { } @NotNull - private static BufferExposingByteArrayOutputStream writeToBytes(@NotNull Parent element, @NotNull String lineSeparator) throws IOException { + public static BufferExposingByteArrayOutputStream writeToBytes(@NotNull Parent element, @NotNull String lineSeparator) throws IOException { BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); JDOMUtil.writeParent(element, out, lineSeparator); return out; } @NotNull - static VirtualFile getOrCreateVirtualFile(@Nullable Object requestor, @NotNull File ioFile) throws IOException { + private static VirtualFile getOrCreateVirtualFile(@Nullable Object requestor, @NotNull File ioFile) throws IOException { VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); if (virtualFile == null) { File parentFile = ioFile.getParentFile(); @@ -249,15 +259,26 @@ public class StorageUtil { byte[] bytes = file.contentsToByteArray(); String lineSeparator = file.getDetectedLineSeparator(); if (lineSeparator == null) { - String fileText = new String(bytes, CharsetToolkit.UTF8); - final int index = fileText.indexOf('\n'); - lineSeparator = index == -1 - ? SystemProperties.getLineSeparator() - : index - 1 >= 0 ? fileText.charAt(index - 1) == '\r' ? "\r\n" : "\n" : "\n"; + lineSeparator = detectLineSeparators(CharsetToolkit.UTF8_CHARSET.decode(ByteBuffer.wrap(bytes)), null).getSeparatorString(); } return Pair.create(bytes, lineSeparator); } + @NotNull + public static LineSeparator detectLineSeparators(@NotNull CharSequence chars, @Nullable LineSeparator defaultSeparator) { + for (int i = 0, n = chars.length(); i < n; i++) { + char c = chars.charAt(i); + if (c == '\r') { + return LineSeparator.CRLF; + } + else if (c == '\n') { + // if we are here, there was no \r before + return LineSeparator.LF; + } + } + return defaultSeparator == null ? LineSeparator.getSystemLineSeparator() : defaultSeparator; + } + public static boolean contentEquals(@NotNull Parent element, @NotNull VirtualFile file) { return newContentIfDiffers(element, file) == null; } @@ -359,71 +380,6 @@ public class StorageUtil { provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type, async); } - public static void logStateDiffInfo(Set> changedFiles, Set componentNames) { - if (componentNames.isEmpty() || !(DUMP_COMPONENT_STATES || ApplicationManager.getApplication().isInternal())) { - return; - } - - try { - File logDirectory = createLogDirectory(); - if (!logDirectory.mkdirs()) { - throw new IOException("Cannot create " + logDirectory); - } - - for (Pair pair : changedFiles) { - File file = new File(pair.first.getPath()); - StateStorage storage = pair.second; - - if (storage instanceof XmlElementStorage) { - Element state = ((XmlElementStorage)storage).logComponents(); - if (state != null) { - JDOMUtil.writeParent(state, new File(logDirectory, "prev_" + file.getName()), "\n"); - } - } - - if (file.exists()) { - File logFile = new File(logDirectory, "new_" + file.getName()); - FileUtil.copy(file, logFile); - } - } - - File logFile = new File(logDirectory, "components.txt"); - FileUtil.writeToFile(logFile, componentNames.toString() + "\n"); - } - catch (Throwable e) { - LOG.info(e); - } - } - - private static File createLogDirectory() { - UniqueFileNamesProvider namesProvider = new UniqueFileNamesProvider(); - - File statesDir = new File(PathManager.getSystemPath(), "log/componentStates"); - File[] children = statesDir.listFiles(); - if (children != null) { - if (children.length > 10) { - File childToDelete = null; - - for (File child : children) { - if (childToDelete == null || childToDelete.lastModified() > child.lastModified()) { - childToDelete = child; - } - } - - if (childToDelete != null) { - FileUtil.delete(childToDelete); - } - } - - for (File child : children) { - namesProvider.reserveFileName(child.getName()); - } - } - - String name = "state-" + LOG_DIR_FORMAT.format(new Date()) + "-" + ApplicationInfo.getInstance().getBuild().asString(); - return new File(statesDir, namesProvider.suggestName(name)); - } - public static boolean isProjectOrModuleFile(@NotNull String fileSpec) { return StoragePathMacros.PROJECT_FILE.equals(fileSpec) || fileSpec.startsWith(StoragePathMacros.PROJECT_CONFIG_DIR) || fileSpec.equals("$MODULE_FILE$"); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java index 20735fc2775e..10a96cb631bc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StoreUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,13 @@ */ package com.intellij.openapi.components.impl.stores; +import com.intellij.diagnostic.IdeErrorsDialog; +import com.intellij.diagnostic.PluginException; +import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.store.ComponentSaveSession; +import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.ShutDownTracker; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; @@ -31,16 +36,38 @@ public class StoreUtil { } public static void doSave(@NotNull IComponentStore stateStore) { - ComponentSaveSession session = null; + ComponentSaveSession session = stateStore.startSave(); + if (session == null) { + return; + } + + ShutDownTracker.getInstance().registerStopperThread(Thread.currentThread()); try { - session = stateStore.startSave(); List> readonlyFiles = new SmartList>(); session.save(readonlyFiles); } + catch (Throwable e) { + PluginId pluginId = IdeErrorsDialog.findPluginId(e); + if (pluginId == null) { + //noinspection InstanceofCatchParameter + if (e instanceof RuntimeException) { + throw ((RuntimeException)e); + } + else { + throw new StateStorageException(e); + } + } + else { + throw new PluginException(e, pluginId); + } + } finally { - if (session != null) { + try { session.finishSave(); } + finally { + ShutDownTracker.getInstance().unregisterStopperThread(Thread.currentThread()); + } } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java index b4a869ce4ec0..c542b2baa3e2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java @@ -23,9 +23,9 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.vfs.VirtualFile; import gnu.trove.THashMap; -import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; import org.jdom.Document; import org.jdom.Element; @@ -33,10 +33,14 @@ import org.jdom.JDOMException; import org.jdom.filter.ElementFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; public abstract class XmlElementStorage implements StateStorage, Disposable { private static final Logger LOG = Logger.getInstance(XmlElementStorage.class); @@ -48,29 +52,17 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { protected TrackingPathMacroSubstitutor myPathMacroSubstitutor; @NotNull protected final String myRootElementName; - private Object mySession; protected StorageData myLoadedData; protected final StreamProvider myStreamProvider; protected final String myFileSpec; protected boolean myBlockSavingTheContent = false; - protected int myUpToDateHash = -1; - private int myProviderUpToDateHash = -1; private boolean mySavingDisabled = false; - private final Map myStorageComponentStates = new THashMap(); // at load we store Element, on setState Integer of hash - private final ComponentVersionProvider myLocalVersionProvider; protected final RemoteComponentVersionProvider myRemoteVersionProvider; private final RoamingType myRoamingType; - protected ComponentVersionListener myListener = new ComponentVersionListener(){ - @Override - public void componentStateChanged(String componentName) { - myLocalVersionProvider.changeVersion(componentName, System.currentTimeMillis()); - } - }; - private boolean myDisposed; protected XmlElementStorage(@NotNull String fileSpec, @@ -98,28 +90,16 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Nullable protected abstract Element loadLocalData(); - @Nullable - public synchronized Element getState(@NotNull String componentName) { - final StorageData storageData = getStorageData(false); - final Element state = storageData.getState(componentName); - if (state != null) { - if (!myStorageComponentStates.containsKey(componentName)) { - myStorageComponentStates.put(componentName, state); - } - storageData.removeState(componentName); - } - return state; - } - @Override - public boolean hasState(final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(@Nullable Object component, @NotNull String componentName, Class aClass, boolean reloadData) throws StateStorageException { return getStorageData(reloadData).hasState(componentName); } @Override @Nullable - public T getState(final Object component, @NotNull String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException { - return DefaultStateSerializer.deserializeState(getState(componentName), stateClass, mergeInto); + public T getState(Object component, @NotNull String componentName, @NotNull Class stateClass, @Nullable T mergeInto) throws StateStorageException { + Element state = getStorageData(false).getStateAndArchive(componentName); + return DefaultStateSerializer.deserializeState(state, stateClass, mergeInto); } @NotNull @@ -196,109 +176,36 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Override @NotNull - public ExternalizationSession startExternalization() { - ExternalizationSession session = new MyExternalizationSession(getStorageData().clone(), myListener); - mySession = session; - return session; + public final ExternalizationSession startExternalization() { + return createSaveSession(getStorageData()); } + @Nullable @Override - @NotNull - public SaveSession startSave(@NotNull final ExternalizationSession externalizationSession) { - LOG.assertTrue(mySession == externalizationSession); - - final SaveSession saveSession = mySavingDisabled ? createNullSession() : createSaveSession((MyExternalizationSession)externalizationSession); - mySession = saveSession; - return saveSession; - } - - private static SaveSession createNullSession() { - return new SaveSession(){ - @Override - public void save() { - } - - @Override - public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { - return Collections.emptySet(); - } - - @Override - public void collectAllStorageFiles(@NotNull List files) { - } - }; - } - - protected abstract MySaveSession createSaveSession(MyExternalizationSession externalizationSession); - - @Override - public void finishSave(@NotNull final SaveSession saveSession) { - try { - if (mySession != saveSession) { - LOG.error("mySession=" + mySession + " saveSession=" + saveSession); - } - } finally { - mySession = null; + public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { + if (mySavingDisabled) { + return null; + } + else { + MySaveSession session = (MySaveSession)externalizationSession; + return session.myCopiedStorageData == null ? null : session; } } + protected abstract MySaveSession createSaveSession(@NotNull StorageData storageData); + public void disableSaving() { mySavingDisabled = true; } - protected class MyExternalizationSession implements ExternalizationSession { - private final StorageData myStorageData; - private final ComponentVersionListener myListener; - - public MyExternalizationSession(final StorageData storageData, ComponentVersionListener listener) { - myStorageData = storageData; - myListener = listener; - } - - @Override - public void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, @Nullable Storage storageSpec) { - assert mySession == this; - - Element element; - try { - element = DefaultStateSerializer.serializeState(state, storageSpec); - } - catch (WriteExternalException e) { - LOG.debug(e); - return; - } - - if (element == null || JDOMUtil.isEmpty(element)) { - return; - } - - setState(componentName, element); - } - - private synchronized void setState(@NotNull String componentName, @NotNull Element element) { - myStorageData.setState(componentName, element); - int hash = JDOMUtil.getTreeHash(element); - try { - Object oldElementState = myStorageComponentStates.get(componentName); - if (oldElementState instanceof Element && !JDOMUtil.areElementsEqual((Element)oldElementState, element) || - oldElementState instanceof Integer && hash != (Integer)oldElementState) { - myListener.componentStateChanged(componentName); - } - } - finally { - myStorageComponentStates.put(componentName, hash); - } - } - } - @Nullable - protected Element getElement(@NotNull StorageData data) { - Element element = data.save(); + protected final Element getElement(@NotNull StorageData data, boolean collapsePaths, @NotNull Map newLiveStates) { + Element element = data.save(newLiveStates); if (element == null || JDOMUtil.isEmpty(element)) { return null; } - if (myPathMacroSubstitutor != null) { + if (collapsePaths && myPathMacroSubstitutor != null) { try { myPathMacroSubstitutor.collapsePaths(element); } @@ -310,95 +217,84 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { return element; } - protected abstract class MySaveSession implements SaveSession { - final StorageData myStorageData; - private Element myElementToSave; - - public MySaveSession(MyExternalizationSession externalizationSession) { - myStorageData = externalizationSession.myStorageData; + @Override + public void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set> changedFiles, @NotNull Set result) { + StorageData oldData = myLoadedData; + StorageData newData = getStorageData(true); + if (oldData == null) { + result.addAll(newData.getComponentNames()); } - - private boolean _needsSave(int hash) { - if (myBlockSavingTheContent) { - return false; - } - - if (myUpToDateHash == -1) { - if (hash != -1) { - if (!physicalContentNeedsSave()) { - myUpToDateHash = hash; - return false; - } - else { - return true; - } - } - else { - return true; - } - } - else if (hash != -1) { - if (hash == myUpToDateHash) { - return false; - } - if (!physicalContentNeedsSave()) { - myUpToDateHash = hash; - return false; - } - else { - return true; - } - } - else { - return physicalContentNeedsSave(); + else { + Set changedComponentNames = oldData.getChangedComponentNames(newData, myPathMacroSubstitutor); + if (changedComponentNames != null) { + result.addAll(changedComponentNames); } } + } - protected boolean physicalContentNeedsSave() { - return true; + protected abstract class MySaveSession implements SaveSession, ExternalizationSession { + private final StorageData myOriginalStorageData; + private StorageData myCopiedStorageData; + + private final Map myNewLiveStates = new THashMap(); + + public MySaveSession(@NotNull StorageData storageData) { + myOriginalStorageData = storageData; } - protected abstract void doSave() throws StateStorageException; + @Override + public final void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, @Nullable Storage storageSpec) { + Element element; + try { + element = DefaultStateSerializer.serializeState(state, storageSpec); + } + catch (WriteExternalException e) { + LOG.debug(e); + return; + } + catch (Throwable e) { + LOG.info("Unable to serialize component state!", e); + return; + } - protected int calcHash() { - return -1; + if (myCopiedStorageData == null) { + myCopiedStorageData = StorageData.setStateAndCloneIfNeed(componentName, element, myOriginalStorageData, myNewLiveStates); + if (myCopiedStorageData != null) { + myLocalVersionProvider.changeVersion(componentName, System.currentTimeMillis()); + } + } + else if (myCopiedStorageData.setState(componentName, element, myNewLiveStates) != null) { + myLocalVersionProvider.changeVersion(componentName, System.currentTimeMillis()); + } } @Override public final void save() { - assert mySession == this; - if (myBlockSavingTheContent) { return; } - int hash = calcHash(); try { - if (myStreamProvider != null && myStreamProvider.isEnabled() && (myProviderUpToDateHash == -1 || myProviderUpToDateHash != hash)) { - try { - saveForProvider(); - myProviderUpToDateHash = hash; - } - catch (Throwable e) { - LOG.error(e); - } - } + doSave(getElement(myCopiedStorageData, isCollapsePathsOnSave(), myNewLiveStates)); } - finally { - if (!(myUpToDateHash != -1 && myUpToDateHash == hash) && _needsSave(hash)) { - doSave(); - } - myUpToDateHash = hash; + catch (IOException e) { + throw new StateStorageException(e); } } - private void saveForProvider() throws IOException { + // only because default project store hack + protected boolean isCollapsePathsOnSave() { + return true; + } + + protected abstract void doSave(@Nullable Element element) throws IOException; + + protected void saveForProvider(@Nullable BufferExposingByteArrayOutputStream content, @Nullable Element element) throws IOException { if (!myStreamProvider.isApplicable(myFileSpec, myRoamingType)) { return; } - Element element = getElementToSave(); - if (element == null || element.getChildren().isEmpty()) { + if (element == null) { myStreamProvider.delete(myFileSpec, myRoamingType); return; } @@ -411,68 +307,30 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { if (myFileSpec.equals(StoragePathMacros.WORKSPACE_FILE)) { Element copiedElement = JDOMUtil.cloneElement(element, DISABLED_ROAMING_ELEMENT_FILTER); if (copiedElement != null) { - doSaveForProvider(copiedElement, DISABLED_ROAMING_ELEMENT_FILTER.myRoamingType, myStreamProvider); + doSaveForProvider(copiedElement, DISABLED_ROAMING_ELEMENT_FILTER.myRoamingType, content); } } else { - doSaveForProvider(element, myRoamingType, myStreamProvider); + doSaveForProvider(element, myRoamingType, content); } } - private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @NotNull StreamProvider streamProvider) throws IOException { - StorageUtil.doSendContent(streamProvider, myFileSpec, element, roamingType, true); - if (streamProvider.isVersioningRequired()) { + private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @Nullable BufferExposingByteArrayOutputStream content) throws IOException { + if (content == null) { + StorageUtil.doSendContent(myStreamProvider, myFileSpec, element, roamingType, true); + } + else { + myStreamProvider.saveContent(myFileSpec, content.getInternalBuffer(), content.size(), myRoamingType, true); + } + + if (myStreamProvider.isVersioningRequired()) { TObjectLongHashMap versions = loadVersions(element.getChildren(StorageData.COMPONENT)); if (!versions.isEmpty()) { Element versionDoc = StateStorageManagerImpl.createComponentVersionsXml(versions); - StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); + StorageUtil.doSendContent(myStreamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); } } } - - @Nullable - protected Element getElementToSave() { - if (myElementToSave == null) { - myElementToSave = getElement(myStorageData); - } - return myElementToSave; - } - - public StorageData getData() { - return myStorageData; - } - - @Override - @Nullable - public Set analyzeExternalChanges(@NotNull Set> changedFiles) { - boolean containsSelf = false; - for (Pair pair : changedFiles) { - if (pair.second == XmlElementStorage.this) { - containsSelf = true; - break; - } - } - - if (!containsSelf) { - return Collections.emptySet(); - } - - try { - Element element = loadLocalData(); - if (element == null) { - return Collections.emptySet(); - } - - StorageData storageData = createStorageData(); - loadState(storageData, element); - return storageData.getChangedComponentNames(myStorageData, myPathMacroSubstitutor); - } - catch (StateStorageException e) { - LOG.info(e); - } - - return null; - } } private TObjectLongHashMap loadVersions(List elements) { @@ -494,34 +352,11 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { myDisposed = true; } - public void resetData(){ + @TestOnly + public void resetData() { myLoadedData = null; } - @Override - public void reload(@NotNull final Set changedComponents) { - final StorageData storageData = loadData(false); - final StorageData oldLoadedData = myLoadedData; - if (oldLoadedData != null) { - Set componentsToRetain = new THashSet(oldLoadedData.myComponentStates.keySet()); - componentsToRetain.addAll(changedComponents); - - // add empty configuration tags for removed components - for (String componentToRetain : componentsToRetain) { - if (!storageData.myComponentStates.containsKey(componentToRetain) && myStorageComponentStates.containsKey(componentToRetain)) { - Element emptyElement = new Element("component"); - LOG.info("Create empty component element for " + componentsToRetain); - emptyElement.setAttribute(StorageData.NAME, componentToRetain); - storageData.myComponentStates.put(componentToRetain, emptyElement); - } - } - - storageData.myComponentStates.keySet().retainAll(componentsToRetain); - } - - myLoadedData = storageData; - } - private void filterOutOfDate(@NotNull Element element) { if (myRemoteVersionProvider == null) { return; @@ -540,13 +375,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - @Nullable - Element logComponents() { - return mySession instanceof MySaveSession ? getElement(((MySaveSession)mySession).myStorageData) : null; - } - public void resetProviderCache() { - myProviderUpToDateHash = -1; if (myRemoteVersionProvider != null) { myRemoteVersionProvider.myProviderVersions = null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/store/ComponentSaveSession.java b/platform/platform-impl/src/com/intellij/openapi/components/store/ComponentSaveSession.java index 0e6b90bf9767..042048add609 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/store/ComponentSaveSession.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/store/ComponentSaveSession.java @@ -15,26 +15,16 @@ */ package com.intellij.openapi.components.store; -import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.impl.stores.StateStorageManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Set; public interface ComponentSaveSession { @NotNull ComponentSaveSession save(@NotNull List> readonlyFiles); void finishSave(); - - void reset(); - - @Nullable - Set analyzeExternalChanges(@NotNull Set> changedFiles); - - void collectAllStorageFiles(boolean includingSubStructures, @NotNull List files); } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectManagerEx.java index 05b894248b8e..e9f85b703be3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectManagerEx.java @@ -50,6 +50,7 @@ public abstract class ProjectManagerEx extends ProjectManager { public abstract void saveChangedProjectFile(@NotNull VirtualFile file, @NotNull Project project); + @Deprecated public abstract boolean isFileSavedToBeReloaded(VirtualFile file); public abstract void blockReloadingProjectOnExternalChanges(); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 1b7c5c2091fd..9b80ac319367 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -32,9 +32,10 @@ import com.intellij.openapi.components.ExportableApplicationComponent; import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; +import com.intellij.openapi.components.impl.stores.FileBasedStorage; +import com.intellij.openapi.components.impl.stores.StateStorageManager; import com.intellij.openapi.components.impl.stores.StorageUtil; import com.intellij.openapi.components.impl.stores.XmlElementStorage; -import com.intellij.openapi.components.store.ComponentSaveSession; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.*; @@ -56,12 +57,11 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.SmartList; import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.containers.SmartHashSet; import com.intellij.util.messages.MessageBus; -import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; -import gnu.trove.THashMap; import gnu.trove.THashSet; -import gnu.trove.TObjectLongHashMap; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; @@ -93,10 +93,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt private final Set myTestProjects = new THashSet(); - private final Map mySavedCopies = new THashMap(); - private final TObjectLongHashMap mySavedTimestamps = new TObjectLongHashMap(); - private final Map>> myChangedProjectFiles = - new THashMap>>(); + private final MultiMap myChangedProjectFiles = MultiMap.createSet(); private final Alarm myChangedFilesAlarm = new Alarm(); private final List> myChangedApplicationFiles = new SmartList>(); private final AtomicInteger myReloadBlockCount = new AtomicInteger(0); @@ -130,11 +127,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt new ProjectManagerListener() { @Override public void projectOpened(final Project project) { - MessageBus messageBus = project.getMessageBus(); - MessageBusConnection connection = messageBus.connect(project); - connection.subscribe(StateStorage.STORAGE_TOPIC, new StateStorage.Listener() { + project.getMessageBus().connect(project).subscribe(StateStorage.PROJECT_STORAGE_TOPIC, new StateStorage.Listener() { @Override - public void storageFileChanged(@NotNull final VirtualFileEvent event, @NotNull final StateStorage storage) { + public void storageFileChanged(@NotNull VirtualFileEvent event, @NotNull StateStorage storage) { projectStorageFileChanged(event, storage, project); } }); @@ -176,9 +171,23 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt registerExternalProjectFileListener(virtualFileManager); } + static class ChangedFileEntry { + public VirtualFile file; + public StateStorage storage; + public long timestamp; + public byte[] savedContent; + + public ChangedFileEntry(@NotNull VirtualFile file, @NotNull StateStorage storage) throws IOException { + this.file = file; + this.storage = storage; + timestamp = file.getTimeStamp(); + savedContent = file.contentsToByteArray(); + } + } + private void projectStorageFileChanged(@NotNull VirtualFileEvent event, @NotNull StateStorage storage, @Nullable Project project) { VirtualFile file = event.getFile(); - if (!file.isDirectory() && !(event.getRequestor() instanceof StateStorage.SaveSession)) { + if (!StorageUtil.isChangedByStorageOrSaveSession(event) && !file.isDirectory()) { registerProjectToReload(project, file, storage); } } @@ -622,22 +631,23 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt LOG.debug("[RELOAD] myReloadBlockCount = " + myReloadBlockCount.get()); if (myReloadBlockCount.get() == 0) { Set projects; - synchronized (myChangedProjectFiles) { - if (myChangedProjectFiles.isEmpty()) return; - projects = new HashSet(myChangedProjectFiles.keySet()); + if (myChangedProjectFiles.isEmpty()) { + return; + } + projects = new THashSet(myChangedProjectFiles.keySet()); } - List projectsToReload = new ArrayList(); - + List>> projectsToReload = new SmartList>>(); for (Project project : projects) { - if (shouldReloadProject(project)) { - projectsToReload.add(project); + Collection changedFileEntries = shouldReloadProject(project); + if (changedFileEntries != null) { + projectsToReload.add(Pair.create(project, changedFileEntries)); } } - for (final Project projectToReload : projectsToReload) { - reloadProjectImpl(projectToReload, false); + for (Pair> projectToReload : projectsToReload) { + reloadProjectImpl(projectToReload.first, projectToReload.second); } } } @@ -699,39 +709,41 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt } } - private boolean shouldReloadProject(final Project project) { - if (project.isDisposed()) return false; - final HashSet> causes = new HashSet>(); - - synchronized (myChangedProjectFiles) { - final List> changes = myChangedProjectFiles.remove(project); - if (changes != null) { - causes.addAll(changes); - } - - if (causes.isEmpty()) return false; + @Nullable + private Collection shouldReloadProject(@NotNull Project project) { + if (project.isDisposed()) { + return null; } - final boolean[] reloadOk = {false}; - - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - LOG.debug("[RELOAD] Reloading project/components..."); - reloadOk[0] = ((ProjectEx)project).getStateStore().reload(causes); - } - catch (StateStorageException e) { - Messages.showWarningDialog(ProjectBundle.message("project.reload.failed", e.getMessage()), - ProjectBundle.message("project.reload.failed.title")); - } - catch (IOException e) { - Messages.showWarningDialog(ProjectBundle.message("project.reload.failed", e.getMessage()), - ProjectBundle.message("project.reload.failed.title")); + final Set> causes = new SmartHashSet>(); + Collection changes; + synchronized (myChangedProjectFiles) { + changes = myChangedProjectFiles.remove(project); + if (!ContainerUtil.isEmpty(changes)) { + for (ChangedFileEntry change : changes) { + causes.add(Pair.create(change.file, change.storage)); } } - }); - if (reloadOk[0]) return false; + } + + if (causes.isEmpty()) { + return null; + } + + AccessToken token = WriteAction.start(); + try { + LOG.debug("[RELOAD] Reloading project/components..."); + if (((ProjectEx)project).getStateStore().reload(causes)) { + return null; + } + } + catch (StateStorageException e) { + Messages.showWarningDialog(ProjectBundle.message("project.reload.failed", e.getMessage()), + ProjectBundle.message("project.reload.failed.title")); + } + finally { + token.finish(); + } String message; if (causes.size() == 1) { @@ -757,16 +769,26 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt message = ProjectBundle.message("project.reload.external.change.multiple", filesBuilder.toString()); } - return Messages.showDialog(message, - ProjectBundle.message("project.reload.external.change.title"), - new String[]{"&Reload Project", "&Discard Changes"}, - -1, - Messages.getQuestionIcon()) == 0; + if (Messages.showDialog(message, + ProjectBundle.message("project.reload.external.change.title"), + new String[]{"&Reload Project", "&Discard Changes"}, + -1, + Messages.getQuestionIcon()) == 0) { + return changes; + } + else { + return null; + } } @Override public boolean isFileSavedToBeReloaded(VirtualFile candidate) { - return mySavedCopies.containsKey(candidate); + for (ChangedFileEntry entry : myChangedProjectFiles.values()) { + if (entry.file.equals(candidate)) { + return true; + } + } + return false; } @Override @@ -822,40 +844,33 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt } @Override - public void saveChangedProjectFile(@NotNull VirtualFile file, @Nullable Project project) { - registerProjectToReload(project, file, null); + public void saveChangedProjectFile(@NotNull VirtualFile file, @NotNull Project project) { + StateStorageManager storageManager = ((ProjectEx)project).getStateStore().getStateStorageManager(); + String fileSpec = storageManager.collapseMacros(file.getPath()); + Couple> storages = storageManager.getCachedFileStateStorages(Collections.singletonList(fileSpec), Collections.emptyList()); + FileBasedStorage storage = ContainerUtil.getFirstItem(storages.first); + // if empty, so, storage is not yet loaded, so, we don't have to reload + if (storage != null) { + registerProjectToReload(project, file, storage); + } } - private void registerProjectToReload(@Nullable Project project, VirtualFile cause, @Nullable StateStorage storage) { - if (cause.exists()) { - try { - byte[] bytes = cause.contentsToByteArray(); - mySavedCopies.put(cause, bytes); - mySavedTimestamps.put(cause, cause.getTimeStamp()); - } - catch (IOException e) { - LOG.error(e); - } - } - + private void registerProjectToReload(@Nullable Project project, @NotNull VirtualFile cause, @NotNull StateStorage storage) { if (LOG.isDebugEnabled()) { LOG.debug("[RELOAD] Registering project to reload: " + cause, new Exception()); } - if (project != null) { - synchronized (myChangedProjectFiles) { - List> changedProjectFiles = myChangedProjectFiles.get(project); - if (changedProjectFiles == null) { - changedProjectFiles = new SmartList>(); - myChangedProjectFiles.put(project, changedProjectFiles); - } - - changedProjectFiles.add(Pair.create(cause, storage)); - } - } - else { + if (project == null) { myChangedApplicationFiles.add(Pair.create(cause, storage)); } + else if (cause.exists()) { + try { + myChangedProjectFiles.putValue(project, new ChangedFileEntry(cause, storage)); + } + catch (IOException e) { + LOG.error(e); + } + } myChangedFilesAlarm.cancelAllRequests(); myChangedFilesAlarm.addRequest(new Runnable() { @@ -869,45 +884,25 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt }, 444); } - private void restoreCopy(VirtualFile file) { - try { - if (file == null) return; // Externally deleted actually. - if (!file.isWritable()) return; // IDEA was unable to save it as well. So no need to restore. - - final byte[] bytes = mySavedCopies.get(file); - if (bytes != null) { - try { - file.setBinaryContent(bytes, -1, mySavedTimestamps.get(file)); - } - catch (IOException e) { - Messages.showWarningDialog(ProjectBundle.message("project.reload.write.failed", file.getPresentableUrl()), - ProjectBundle.message("project.reload.write.failed.title")); - } - } - } - finally { - mySavedCopies.remove(file); - mySavedTimestamps.remove(file); - } - } - @Override - public void reloadProject(@NotNull final Project p) { - reloadProjectImpl(p, true); + public void reloadProject(@NotNull Project p) { + myChangedProjectFiles.remove(p); + reloadProjectImpl(p, Collections.emptyList()); } - public void reloadProjectImpl(@NotNull final Project p, final boolean clearCopyToRestore) { - if (clearCopyToRestore) { - mySavedCopies.clear(); - mySavedTimestamps.clear(); + public void reloadProjectImpl(@NotNull Project p, @Nullable Collection changedFileEntries) { + final Collection filesToRestore; + if (changedFileEntries == null) { + // our internal ReloadProjectAction + filesToRestore = myChangedProjectFiles.remove(p); } - + else { + filesToRestore = changedFileEntries; + } + final Project[] project = {p}; - - ProjectReloadState.getInstance(project[0]).onBeforeAutomaticProjectReload(); - final Application application = ApplicationManager.getApplication(); - - application.invokeLater(new Runnable() { + ProjectReloadState.getInstance(p).onBeforeAutomaticProjectReload(); + ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { LOG.debug("Reloading project."); @@ -916,26 +911,28 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt return; } final String location = projectImpl.getPresentableUrl(); - final List original = new SmartList(); - try { - ComponentSaveSession saveSession = projectImpl.getStateStore().startSave(); - saveSession.collectAllStorageFiles(true, original); - saveSession.finishSave(); - } - catch (Exception e) { - LOG.error(e); - return; - } - if (project[0].isDisposed() || ProjectUtil.closeAndDispose(project[0])) { - application.runWriteAction(new Runnable() { - @Override - public void run() { - for (VirtualFile originalFile : original) { - restoreCopy(originalFile); + if (!ContainerUtil.isEmpty(filesToRestore)) { + AccessToken token = WriteAction.start(); + try { + for (ChangedFileEntry entry : filesToRestore) { + if (!entry.file.isWritable()) { + continue; // IDEA was unable to save it as well. So no need to restore. + } + + try { + entry.file.setBinaryContent(entry.savedContent, -1, entry.timestamp); + } + catch (IOException e) { + Messages.showWarningDialog(ProjectBundle.message("project.reload.write.failed", entry.file.getPresentableUrl()), + ProjectBundle.message("project.reload.write.failed.title")); + } } } - }); + finally { + token.finish(); + } + } project[0] = null; // Let it go. @@ -1133,7 +1130,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt } } - public void setDefaultProjectRootElement(final Element defaultProjectRootElement) { + public void setDefaultProjectRootElement(@Nullable Element defaultProjectRootElement) { myDefaultProjectRootElement = defaultProjectRootElement; } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java index 149f8e128cd7..cb80c49d889e 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java @@ -9,6 +9,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.testFramework.LightPlatformLangTestCase; +import com.intellij.util.messages.MessageBus; import com.intellij.util.xmlb.XmlSerializerUtil; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; @@ -137,7 +138,7 @@ public class ApplicationStoreTest extends LightPlatformLangTestCase { TrackingPathMacroSubstitutor macroSubstitutor = new ApplicationPathMacroManager().createTrackingSubstitutor(); stateStorageManager = new StateStorageManagerImpl(macroSubstitutor, "application", this, ApplicationManager.getApplication().getPicoContainer()) { @Override - protected StorageData createStorageData(String storageSpec) { + protected StorageData createStorageData(@NotNull String storageSpec) { return new FileBasedStorage.FileStorageData("application"); } @@ -183,6 +184,12 @@ public class ApplicationStoreTest extends LightPlatformLangTestCase { protected StateStorage getDefaultsStorage() { return null; } + + @NotNull + @Override + protected MessageBus getMessageBus() { + return ApplicationManager.getApplication().getMessageBus(); + } } static class SeveralStoragesConfiguredStorageChooser implements StateStorageChooser { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java index f0035de591a2..2dbf5f1e5a2c 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java @@ -41,7 +41,7 @@ public class StateStorageManagerImplTest extends LightPlatformLangTestCase { super.setUp(); myStateStorageManager = new StateStorageManagerImpl(null, "foo", null, ApplicationManager.getApplication().getPicoContainer()) { @Override - protected StorageData createStorageData(String storageSpec) { + protected StorageData createStorageData(@NotNull String storageSpec) { throw new UnsupportedOperationException("Method createStorageData not implemented in " + getClass()); } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java index 18a1147f7c4d..1f2c31c6a207 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java @@ -21,9 +21,9 @@ import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.components.impl.stores.ComponentVersionProvider; +import com.intellij.openapi.components.impl.stores.StorageData; import com.intellij.openapi.components.impl.stores.XmlElementStorage; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.LightPlatformLangTestCase; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -31,7 +31,6 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Set; import static com.intellij.openapi.util.JDOMBuilder.attr; @@ -98,16 +97,11 @@ public class XmlElementStorageTest extends LightPlatformLangTestCase { } @Override - protected MySaveSession createSaveSession(final MyExternalizationSession externalizationSession) { - return new MySaveSession(externalizationSession) { + protected MySaveSession createSaveSession(@NotNull StorageData storageData) { + return new MySaveSession(storageData) { @Override - protected void doSave() throws StateStorageException { - Element elementToSave = getElementToSave(); - mySavedElement = elementToSave == null ? null : elementToSave.clone(); - } - - @Override - public void collectAllStorageFiles(@NotNull List files) { + protected void doSave(@Nullable Element element) { + mySavedElement = element == null ? null : element.clone(); } }; } diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java b/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java index d417152c3c1d..cac89bb27040 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.openapi.components; import com.intellij.openapi.util.Pair; @@ -23,26 +22,33 @@ import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; import java.util.Set; public interface StateStorage { - Topic STORAGE_TOPIC = new Topic("STORAGE_LISTENER", Listener.class, Topic.BroadcastDirection.TO_PARENT); + // app storage files changed + Topic STORAGE_TOPIC = new Topic("STORAGE_LISTENER", Listener.class, Topic.BroadcastDirection.NONE); + // project storage files changes (project or modules, it is reason why we use broadcast TO_PARENT - to be notified when some module storage file changed + // even if listen only project message bus) + Topic PROJECT_STORAGE_TOPIC = new Topic("PROJECT_STORAGE_LISTENER", Listener.class, Topic.BroadcastDirection.NONE); @Nullable - T getState(final Object component, @NotNull String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException; + T getState(@Nullable Object component, @NotNull String componentName, @NotNull Class stateClass, @Nullable T mergeInto) throws StateStorageException; - boolean hasState(final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException; + boolean hasState(@Nullable Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException; @NotNull ExternalizationSession startExternalization(); - @NotNull + /** + * return null if nothing to save + */ + @Nullable SaveSession startSave(@NotNull ExternalizationSession externalizationSession); - void finishSave(@NotNull SaveSession saveSession); - - void reload(@NotNull Set changedComponents); + /** + * Get changed component names + */ + void analyzeExternalChangesAndUpdateIfNeed(@NotNull Set> changedFiles, @NotNull Set result); interface ExternalizationSession { void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, @Nullable Storage storageSpec); @@ -50,14 +56,6 @@ public interface StateStorage { interface SaveSession { void save(); - - /** - * Get changed component names - */ - @Nullable - Set analyzeExternalChanges(@NotNull Set> changedFiles); - - void collectAllStorageFiles(@NotNull List files); } interface Listener { diff --git a/platform/projectModel-impl/projectModel-impl.iml b/platform/projectModel-impl/projectModel-impl.iml index 3bde48255eb4..911ec39a1743 100644 --- a/platform/projectModel-impl/projectModel-impl.iml +++ b/platform/projectModel-impl/projectModel-impl.iml @@ -12,6 +12,7 @@ + diff --git a/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosCollector.java b/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosCollector.java index b4ed15f34f66..b327280fe6d2 100644 --- a/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosCollector.java +++ b/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosCollector.java @@ -23,11 +23,12 @@ import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtilRt; -import gnu.trove.THashSet; +import com.intellij.util.containers.SmartHashSet; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; @@ -54,10 +55,16 @@ public class PathMacrosCollector extends PathMacroMap { PathMacros.getInstance()); } - public static Set getMacroNames(Element root, @Nullable PathMacroFilter filter, @NotNull final PathMacros pathMacros) { + @NotNull + public static Set getMacroNames(Element root, @Nullable PathMacroFilter filter, @NotNull PathMacros pathMacros) { final PathMacrosCollector collector = new PathMacrosCollector(); collector.substitute(root, true, false, filter); - final Set result = new THashSet(collector.myMacroMap.keySet()); + Set preResult = collector.myMacroMap.keySet(); + if (preResult.isEmpty()) { + return Collections.emptySet(); + } + + Set result = new SmartHashSet(preResult); result.removeAll(pathMacros.getSystemMacroNames()); result.removeAll(pathMacros.getLegacyMacroNames()); result.removeAll(PathMacrosImpl.getToolMacroNames()); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java index d4f87027191d..fc575753bae2 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java @@ -23,6 +23,7 @@ import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.util.ReflectionUtil; import com.intellij.util.xmlb.Accessor; import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; import com.intellij.util.xmlb.XmlSerializer; @@ -41,45 +42,31 @@ class DefaultStateSerializer { } @Nullable - static Element serializeState(@NotNull Object state, final Storage storage) throws WriteExternalException { + static Element serializeState(@NotNull Object state, @Nullable final Storage storage) throws WriteExternalException { if (state instanceof Element) { return (Element)state; } else if (state instanceof JDOMExternalizable) { - JDOMExternalizable jdomExternalizable = (JDOMExternalizable)state; - - final Element element = new Element("temp_element"); - try { - jdomExternalizable.writeExternal(element); - } - catch (WriteExternalException e) { - throw e; - } - catch (Throwable e) { - LOG.info("Unable to serialize component state!", e); - return null; - } + Element element = new Element("temp_element"); + ((JDOMExternalizable)state).writeExternal(element); return element; } else { return XmlSerializer.serialize(state, new SkipDefaultValuesSerializationFilters() { @Override public boolean accepts(final Accessor accessor, final Object bean) { - if (!super.accepts(accessor, bean)) return false; - - if (storage != null) { - final Annotation[] annotations = accessor.getAnnotations(); - for (Annotation annotation : annotations) { - if (StorageId.class.isAssignableFrom(annotation.annotationType())) { - StorageId storageId = (StorageId)annotation; - - if (!storageId.value().equals(storage.id())) return false; - } - } - - return storage.isDefault(); + if (!super.accepts(accessor, bean)) { + return false; } + if (storage != null) { + for (Annotation annotation : accessor.getAnnotations()) { + if (StorageId.class.isAssignableFrom(annotation.annotationType()) && !((StorageId)annotation).value().equals(storage.id())) { + return false; + } + } + return storage.isDefault(); + } return true; } }); @@ -100,32 +87,21 @@ class DefaultStateSerializer { String elementText = JDOMUtil.writeElement(stateElement, "\n"); LOG.error("State is " + stateClass.getName() + ", merge into is " + mergeInto.toString() + ", state element text is " + elementText); } + final T t = ReflectionUtil.newInstance(stateClass); try { - final T t = stateClass.newInstance(); - try { - ((JDOMExternalizable)t).readExternal(stateElement); - return t; - } - catch (InvalidDataException e) { - throw new StateStorageException(e); - } + ((JDOMExternalizable)t).readExternal(stateElement); + return t; } - catch (InstantiationException e) { - throw new StateStorageException(e); - } - catch (IllegalAccessException e) { + catch (InvalidDataException e) { throw new StateStorageException(e); } } + else if (mergeInto == null) { + return XmlSerializer.deserialize(stateElement, stateClass); + } else { - if (mergeInto == null) { - return XmlSerializer.deserialize(stateElement, stateClass); - } - else { - XmlSerializer.deserializeInto(mergeInto, stateElement); - return mergeInto; - } + XmlSerializer.deserializeInto(mergeInto, stateElement); + return mergeInto; } } - } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java index 22e3fc9d72e0..d4f301bee8de 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,16 +18,22 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.application.options.PathMacrosCollector; import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.openapi.components.PathMacroSubstitutor; +import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.components.XmlConfigurationMerger; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; +import com.intellij.util.SystemProperties; +import com.intellij.util.containers.SmartHashSet; import gnu.trove.THashMap; -import gnu.trove.THashSet; +import org.iq80.snappy.SnappyInputStream; +import org.iq80.snappy.SnappyOutputStream; import org.jdom.Attribute; import org.jdom.Element; import org.jdom.JDOMException; @@ -35,27 +41,35 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.*; +@SuppressWarnings("IOResourceOpenedButNotSafelyClosed") public class StorageData { private static final Logger LOG = Logger.getInstance(StorageData.class); @NonNls public static final String COMPONENT = "component"; @NonNls public static final String NAME = "name"; - final Map myComponentStates; + private final Map myComponentStates; + protected final String myRootElementName; - private int myHash = -1; public StorageData(@NotNull String rootElementName) { - myComponentStates = new THashMap(); + myComponentStates = new THashMap(); myRootElementName = rootElementName; } StorageData(@NotNull StorageData storageData) { myRootElementName = storageData.myRootElementName; - myComponentStates = new THashMap(storageData.myComponentStates); + myComponentStates = new THashMap(storageData.myComponentStates); + } + + @NotNull + public Set getComponentNames() { + return myComponentStates.keySet(); } public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { @@ -66,32 +80,31 @@ public class StorageData { for (Iterator iterator = rootElement.getChildren(COMPONENT).iterator(); iterator.hasNext(); ) { Element element = iterator.next(); String name = element.getAttributeValue(NAME); - if (name == null) { - LOG.info("Broken content in file : " + this); + if (StringUtil.isEmpty(name)) { + LOG.warn("No name attribute for component in " + this); continue; } if (element.getAttributes().size() > 1 || !element.getChildren().isEmpty()) { - assert element.getAttributeValue(NAME) != null : "No name attribute for component: " + name + " in " + this; - iterator.remove(); if (intern) { IdeaPluginDescriptorImpl.internJDOMElement(element); } - Element serverElement = myComponentStates.get(name); + Object serverElement = myComponentStates.get(name); if (serverElement != null) { - element = mergeElements(name, element, serverElement); + element = mergeElements(name, element, (Element)serverElement); } myComponentStates.put(name, element); - } - } - if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { - for (String componentName : myComponentStates.keySet()) { - ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(componentName, PathMacrosCollector.getMacroNames(myComponentStates.get(componentName))); + if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { + ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(name, PathMacrosCollector.getMacroNames(element)); + } } + + // remove only after "getMacroNames" - some PathMacroFilter requires element name attribute + element.removeAttribute(NAME); } } @@ -106,8 +119,9 @@ public class StorageData { return serverElement; } + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") @Nullable - protected Element save() { + protected Element save(@NotNull Map newLiveStates) { if (myComponentStates.isEmpty()) { return null; } @@ -117,55 +131,212 @@ public class StorageData { Arrays.sort(componentNames); for (String componentName : componentNames) { assert componentName != null; - final Element element = myComponentStates.get(componentName); + Element element; + Object state = myComponentStates.get(componentName); + if (state instanceof Element) { + element = ((Element)state).clone(); + } + else { + element = newLiveStates.get(componentName); + if (element == null) { + try { + element = unarchiveState((byte[])state); + } + catch (IOException e) { + throw new StateStorageException(e); + } + catch (JDOMException e) { + throw new StateStorageException(e); + } + } + } - if (element.getAttribute(NAME) == null) element.setAttribute(NAME, componentName); + // name attribute should be first + assert element != null; + List elementAttributes = element.getAttributes(); + if (elementAttributes.isEmpty()) { + element.setAttribute(NAME, componentName); + } + else { + Attribute nameAttribute = element.getAttribute(NAME); + if (nameAttribute == null) { + nameAttribute = new Attribute(NAME, componentName); + elementAttributes.add(0, nameAttribute); + } + else { + nameAttribute.setValue(componentName); + if (elementAttributes.get(0) != nameAttribute) { + elementAttributes.remove(nameAttribute); + elementAttributes.add(0, nameAttribute); + } + } + } - rootElement.addContent(element.clone()); + rootElement.addContent(element); } return rootElement; } @Nullable - public Element getState(final String name) { - final Element element = myComponentStates.get(name); - if (element != null) { - assert element.getAttributeValue(NAME) != null : "No name attribute for component: " + name + " in " + this; - element.removeAttribute(NAME); - } - return element; + public Element getState(@NotNull String name) { + Object state = myComponentStates.get(name); + return state instanceof Element ? (Element)state : null; } - void removeState(final String componentName) { - myComponentStates.remove(componentName); - clearHash(); + @Nullable + public Element getStateAndArchive(@NotNull String name) { + Object state = myComponentStates.get(name); + if (!(state instanceof Element)) { + return null; + } + + myComponentStates.put(name, archiveState((Element)state)); + return (Element)state; } - void setState(@NotNull String componentName, @NotNull Element element) { - element.setName(COMPONENT); - - // componentName should be first - List elementAttributes = element.getAttributes(); - if (elementAttributes.isEmpty()) { - element.setAttribute(NAME, componentName); - } - else { - Attribute nameAttribute = element.getAttribute(NAME); - if (nameAttribute == null) { - nameAttribute = new Attribute(NAME, componentName); - elementAttributes.add(0, nameAttribute); + @NotNull + private static byte[] archiveState(@NotNull Element state) { + BufferExposingByteArrayOutputStream byteOut = new BufferExposingByteArrayOutputStream(); + OutputStream out = null; + try { + try { + out = new SnappyOutputStream(byteOut); + JDOMUtil.writeParent(state, out, "\n"); } - else { - nameAttribute.setValue(componentName); - if (elementAttributes.get(0) != nameAttribute) { - elementAttributes.remove(nameAttribute); - elementAttributes.add(0, nameAttribute); + finally { + if (out != null) { + out.close(); } } } + catch (IOException e) { + throw new StateStorageException(e); + } + return ArrayUtil.realloc(byteOut.getInternalBuffer(), byteOut.size()); + } - myComponentStates.put(componentName, element); - clearHash(); + @NotNull + private static Element unarchiveState(@NotNull byte[] state) throws IOException, JDOMException { + InputStream in = null; + try { + in = new SnappyInputStream(new ByteArrayInputStream(state)); + //noinspection ConstantConditions + return JDOMUtil.loadDocument(in).detachRootElement(); + } + finally { + if (in != null) { + in.close(); + } + } + } + + @Nullable + static StorageData setStateAndCloneIfNeed(@NotNull String componentName, @Nullable Element newState, @NotNull StorageData storageData, @NotNull Map newLiveStates) { + Object oldState = storageData.myComponentStates.get(componentName); + if (newState == null || JDOMUtil.isEmpty(newState)) { + if (oldState == null) { + return null; + } + + StorageData newStorageData = storageData.clone(); + newStorageData.myComponentStates.remove(componentName); + return newStorageData; + } + + prepareElement(newState); + + newLiveStates.put(componentName, newState); + + byte[] newBytes = null; + if (oldState instanceof Element) { + if (JDOMUtil.areElementsEqual((Element)oldState, newState)) { + return null; + } + } + else if (oldState != null) { + newBytes = getNewByteIfDiffers(componentName, newState, (byte[])oldState); + if (newBytes == null) { + return null; + } + } + + StorageData newStorageData = storageData.clone(); + newStorageData.myComponentStates.put(componentName, newBytes == null ? newState : newBytes); + return newStorageData; + } + + @Nullable + final Object setState(@NotNull String componentName, @Nullable Element newState, @NotNull Map newLiveStates) { + if (newState == null || JDOMUtil.isEmpty(newState)) { + return myComponentStates.remove(componentName); + } + + prepareElement(newState); + + newLiveStates.put(componentName, newState); + + Object oldState = myComponentStates.get(componentName); + + byte[] newBytes = null; + if (oldState instanceof Element) { + if (JDOMUtil.areElementsEqual((Element)oldState, newState)) { + return null; + } + } + else if (oldState != null) { + newBytes = getNewByteIfDiffers(componentName, newState, (byte[])oldState); + if (newBytes == null) { + return null; + } + } + + myComponentStates.put(componentName, newBytes == null ? newState : newBytes); + return newState; + } + + @Nullable + private static byte[] getNewByteIfDiffers(@NotNull String componentName, @NotNull Object newState, @NotNull byte[] oldState) { + byte[] newBytes = newState instanceof Element ? archiveState((Element)newState) : (byte[])newState; + if (Arrays.equals(newBytes, oldState)) { + return null; + } + else if (LOG.isDebugEnabled() && SystemProperties.getBooleanProperty("idea.log.changed.components", false)) { + String before = stateToString(oldState); + String after = stateToString(newState); + if (before.equals(after)) { + LOG.debug("Serialization error: serialized are different, but unserialized are equal"); + } + else { + LOG.debug(componentName + " " + StringUtil.repeat("=", 80 - componentName.length()) + "\nBefore:\n" + before + "\nAfter:\n" + after); + } + } + return newBytes; + } + + @NotNull + private static String stateToString(@NotNull Object state) { + Element element; + if (state instanceof Element) { + element = (Element)state; + } + else { + try { + element = unarchiveState((byte[])state); + } + catch (IOException e) { + LOG.error(e); + return "internal error"; + } + catch (JDOMException e) { + LOG.error(e); + return "internal error"; + } + } + return JDOMUtil.writeParent(element, "\n"); + } + + private static void prepareElement(@NotNull Element element) { + element.setName(COMPONENT); } @Override @@ -173,52 +344,28 @@ public class StorageData { return new StorageData(this); } - public final int getHash() { - if (myHash == -1) { - myHash = computeHash(); - if (myHash == -1) { - myHash = 0; - } - } - return myHash; - } + // newStorageData - myComponentStates contains only live (unarchived) states + public Set getChangedComponentNames(@NotNull StorageData newStorageData, @Nullable PathMacroSubstitutor substitutor) { + Set bothStates = new SmartHashSet(myComponentStates.keySet()); + bothStates.retainAll(newStorageData.myComponentStates.keySet()); - protected int computeHash() { - int result = 0; - for (String name : myComponentStates.keySet()) { - result = 31 * result + name.hashCode(); - result = 31 * result + JDOMUtil.getTreeHash(myComponentStates.get(name)); - } - return result; - } - - protected void clearHash() { - myHash = -1; - } - - public Set getChangedComponentNames(@NotNull StorageData storageData, @Nullable PathMacroSubstitutor substitutor) { - Set bothStates = new THashSet(myComponentStates.keySet()); - bothStates.retainAll(storageData.myComponentStates.keySet()); - - Set diffs = new THashSet(); - diffs.addAll(storageData.myComponentStates.keySet()); + Set diffs = new SmartHashSet(); + diffs.addAll(newStorageData.myComponentStates.keySet()); diffs.addAll(myComponentStates.keySet()); diffs.removeAll(bothStates); for (String componentName : bothStates) { - final Element e1 = myComponentStates.get(componentName); - final Element e2 = storageData.myComponentStates.get(componentName); - - // some configurations want to collapse path elements in writeExternal so make sure paths are expanded - if (substitutor != null) { - substitutor.expandPaths(e2); + Object oldState = myComponentStates.get(componentName); + Object newState = newStorageData.myComponentStates.get(componentName); + if (oldState instanceof Element) { + if (!JDOMUtil.areElementsEqual((Element)oldState, (Element)newState)) { + diffs.add(componentName); + } } - - if (!JDOMUtil.areElementsEqual(e1, e2)) { + else if (getNewByteIfDiffers(componentName, newState, (byte[])oldState) != null) { diffs.add(componentName); } } - return diffs; } @@ -227,7 +374,7 @@ public class StorageData { } public boolean hasState(@NotNull String componentName) { - return myComponentStates.containsKey(componentName); + return myComponentStates.get(componentName) instanceof Element; } @NotNull diff --git a/platform/testFramework/src/com/intellij/mock/MockProjectStore.java b/platform/testFramework/src/com/intellij/mock/MockProjectStore.java index d4ea6d3fe32e..bcdcdcb52493 100644 --- a/platform/testFramework/src/com/intellij/mock/MockProjectStore.java +++ b/platform/testFramework/src/com/intellij/mock/MockProjectStore.java @@ -159,9 +159,4 @@ public class MockProjectStore implements IProjectStore { public StateStorageManager getStateStorageManager() { throw new UnsupportedOperationException("Method getStateStorageManager not implemented in " + getClass()); } - - @Override - public boolean isSaving() { - return false; - } } diff --git a/platform/util/src/com/intellij/util/LineSeparator.java b/platform/util/src/com/intellij/util/LineSeparator.java index 67d7cc840132..339b7cce18c5 100644 --- a/platform/util/src/com/intellij/util/LineSeparator.java +++ b/platform/util/src/com/intellij/util/LineSeparator.java @@ -18,13 +18,14 @@ package com.intellij.util; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** *

Identifies a line separator: * either Unix ({@code \n}), Windows (@{code \r\n}) or (possible not actual anymore) Classic Mac ({@code \r}).

- * + *

*

The intention is to use this class everywhere, where a line separator is needed, instead of just Strings.

* * @author Kirill Likhodedov @@ -36,9 +37,11 @@ public enum LineSeparator { private static final Logger LOG = Logger.getInstance(LineSeparator.class); private final String mySeparatorString; + private final byte[] myBytes; LineSeparator(@NotNull String separatorString) { mySeparatorString = separatorString; + myBytes = separatorString.getBytes(CharsetToolkit.UTF8_CHARSET); } @NotNull @@ -57,6 +60,11 @@ public enum LineSeparator { return mySeparatorString; } + @NotNull + public byte[] getSeparatorBytes() { + return myBytes; + } + public static boolean knownAndDifferent(@Nullable LineSeparator separator1, @Nullable LineSeparator separator2) { return separator1 != null && separator2 != null && !separator1.equals(separator2); }