diff --git a/platform/core-api/src/com/intellij/openapi/application/ApplicationNamesInfo.java b/platform/core-api/src/com/intellij/openapi/application/ApplicationNamesInfo.java index e767be478ed6..a264416d18eb 100644 --- a/platform/core-api/src/com/intellij/openapi/application/ApplicationNamesInfo.java +++ b/platform/core-api/src/com/intellij/openapi/application/ApplicationNamesInfo.java @@ -21,6 +21,7 @@ import com.intellij.util.PlatformUtilsCore; import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; /** * @author nik @@ -42,6 +43,7 @@ public class ApplicationNamesInfo { private ApplicationNamesInfoHolder() { } } + @NotNull public static ApplicationNamesInfo getInstance() { return ApplicationNamesInfoHolder.ourInstance; } 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 216f368d4a1d..c12d68935126 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 @@ -158,12 +158,14 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM return super.computeHash() * 31 + myOptions.hashCode(); } - @Override @Nullable - public Set getDifference(final StorageData storageData, PathMacroSubstitutor substitutor) { + @Override + public Set getChangedComponentNames(@NotNull StorageData storageData, @Nullable PathMacroSubstitutor substitutor) { final ModuleFileData data = (ModuleFileData)storageData; - if (!myOptions.equals(data.myOptions)) return null; - return super.getDifference(storageData, substitutor); + if (!myOptions.equals(data.myOptions)) { + return null; + } + return super.getChangedComponentNames(storageData, substitutor); } public void setOption(final String optionName, final String optionValue) { 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 c109c8c0e14d..cf4299b85ba1 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 @@ -89,14 +89,9 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { private class ProjectWithModulesSaveSession extends ProjectSaveSession { List myModuleSaveSessions = new ArrayList(); - public ProjectWithModulesSaveSession() throws StateStorageException { - try { - for (Module module : getPersistentModules()) { - myModuleSaveSessions.add(((ModuleImpl)module).getStateStore().startSave()); - } - } - catch (IOException e) { - throw new StateStorageException(e.getMessage()); + public ProjectWithModulesSaveSession() { + for (Module module : getPersistentModules()) { + myModuleSaveSessions.add(((ModuleImpl)module).getStateStore().startSave()); } } 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 36e344ff0acc..84040d4dcc62 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 @@ -207,7 +207,7 @@ public class ClasspathStorage implements StateStorage { } @Override - public void reload(@NotNull final Set changedComponents) throws StateStorageException { + public void reload(@NotNull Set changedComponents) { } public boolean needsSave() throws StateStorageException { 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 863d0f073130..212f0ce92aa2 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 @@ -28,7 +28,6 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collection; -import java.util.Collections; import java.util.Set; class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationStore { @@ -133,47 +132,8 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto } @Override - public boolean reload(@NotNull final Set> changedFiles, - @NotNull final Collection notReloadableComponents) throws StateStorageException, IOException { - final SaveSession saveSession = startSave(); - final Set componentNames = saveSession.analyzeExternalChanges(changedFiles); - - try { - if (componentNames == null) return false; - - for (Pair pair : changedFiles) { - if (pair.second == null) return false; - } - - for (String name : componentNames) { - if (!isReloadPossible(Collections.singleton(name))) { - notReloadableComponents.add(name); - } - } - - StorageUtil.logStateDiffInfo(changedFiles, componentNames); - - if (!isReloadPossible(componentNames)) { - return false; - } - } - finally { - finishSave(saveSession); - } - - if (!componentNames.isEmpty()) { - myApplication.getMessageBus().syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateStarted(); - - try { - doReload(changedFiles, componentNames); - reinitComponents(componentNames, false); - } - finally { - myApplication.getMessageBus().syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished(); - } - } - - return true; + public Collection reload(@NotNull Set> changedFiles) throws IOException { + return reload(changedFiles, 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 dc07fee151e2..b281671f74df 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 @@ -95,12 +95,14 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { return result; } - @Override @Nullable - public Set getDifference(final StorageData storageData, PathMacroSubstitutor substitutor) { - final BaseStorageData data = (BaseStorageData)storageData; - if (myVersion != data.myVersion) return null; - return super.getDifference(storageData, substitutor); + @Override + public Set getChangedComponentNames(@NotNull StorageData storageData, @Nullable PathMacroSubstitutor substitutor) { + BaseStorageData data = (BaseStorageData)storageData; + if (myVersion != data.myVersion) { + return null; + } + return super.getChangedComponentNames(storageData, 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 ed63bf12d4c4..8d7d480155b1 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 @@ -29,6 +29,7 @@ import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; +import com.intellij.util.messages.MessageBus; import gnu.trove.THashMap; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -90,37 +91,28 @@ public abstract class ComponentStoreImpl implements IComponentStore { @Override @NotNull - public SaveSession startSave() throws IOException { + public SaveSession startSave() { + SaveSessionImpl session = createSaveSession(); try { - final SaveSessionImpl session = createSaveSession(); + session.commit(); + } + catch (Throwable e) { try { - session.commit(); + session.reset(); } - 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); - } - - LOG.info(e); - IOException ioException = new IOException(e.getMessage()); - ioException.initCause(e); - throw ioException; + catch (Exception e1_ignored) { + LOG.info(e1_ignored); } - mySession = session; - return mySession; - } - catch (StateStorageException e) { - LOG.info(e); - throw new IOException(e.getMessage()); + + PluginId pluginId = IdeErrorsDialog.findPluginId(e); + if (pluginId != null) { + throw new PluginException(e, pluginId); + } + + throw new StateStorageException(e); } + mySession = session; + return mySession; } protected SaveSessionImpl createSaveSession() throws StateStorageException { @@ -460,6 +452,22 @@ public abstract class ComponentStoreImpl implements IComponentStore { return true; } + @Override + @NotNull + public final Collection getNotReloadableComponents(@NotNull Collection componentNames) { + Set notReloadableComponents = null; + for (String componentName : componentNames) { + Object component = myComponents.get(componentName); + if (component != null && (!(component instanceof PersistentStateComponent) || !getStateSpec((PersistentStateComponent)component).reloadable())) { + if (notReloadableComponents == null) { + notReloadableComponents = new LinkedHashSet(); + } + notReloadableComponents.add(componentName); + } + } + return notReloadableComponents == null ? Collections.emptySet() : notReloadableComponents; + } + @Override public void reinitComponents(@NotNull final Set componentNames, final boolean reloadData) { for (String componentName : componentNames) { @@ -470,13 +478,55 @@ public abstract class ComponentStoreImpl implements IComponentStore { } } - protected void doReload(final Set> changedFiles, @NotNull final Set componentNames) - throws StateStorageException { + protected void doReload(@NotNull Set> changedFiles, @NotNull Set componentNames) { for (Pair pair : changedFiles) { assert pair != null; - final StateStorage storage = pair.second; + StateStorage storage = pair.second; assert storage != null : "Null storage for: " + pair.first; storage.reload(componentNames); } } + + @Nullable + protected final Collection reload(@NotNull Set> changedFiles, @NotNull MessageBus messageBus) { + SaveSession saveSession = startSave(); + Set componentNames; + try { + componentNames = saveSession.analyzeExternalChanges(changedFiles); + if (componentNames == null) { + return Collections.emptyList(); + } + + for (Pair pair : changedFiles) { + if (pair.second == null) { + return Collections.emptyList(); + } + } + + Collection currentNotReloadableComponents = getNotReloadableComponents(componentNames); + + StorageUtil.logStateDiffInfo(changedFiles, componentNames); + + if (!currentNotReloadableComponents.isEmpty()) { + return currentNotReloadableComponents; + } + } + finally { + finishSave(saveSession); + } + + 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 fb12dc632a8a..47e184936837 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 @@ -19,6 +19,7 @@ import com.intellij.openapi.components.*; 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; @@ -137,6 +138,12 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { return storage; } + @NotNull + @Override + public Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted) { + return new Couple>(Collections.emptyList(), Collections.emptyList()); + } + @Override @Nullable public StateStorage getFileStateStorage(@NotNull String fileSpec) { 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 9415aee49b09..1e1363633875 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 @@ -89,7 +89,7 @@ class DefaultsStateStorage implements StateStorage { throw new UnsupportedOperationException("Method finishSave not implemented in " + getClass()); } - public void reload(@NotNull final Set changedComponents) throws StateStorageException { + 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 912c239cffcb..a8df62ad4dab 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 @@ -24,11 +24,13 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; import com.intellij.util.SmartList; 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; @@ -106,7 +108,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { return myStorageData.getMergedState(componentName, stateClass, mySplitter, mergeInto); } - private DirectoryStorageData loadState() throws StateStorageException { + private DirectoryStorageData loadState() { DirectoryStorageData storageData = new DirectoryStorageData(); storageData.loadFrom(LocalFileSystem.getInstance().findFileByIoFile(myDir), myPathMacroSubstitutor); return storageData; @@ -158,7 +160,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } @Override - public void reload(@NotNull final Set changedComponents) throws StateStorageException { + public void reload(@NotNull Set changedComponents) { myStorageData = loadState(); } @@ -239,28 +241,28 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @Nullable - public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { + public Set analyzeExternalChanges(@NotNull Set> changedFiles) { boolean containsSelf = false; - for (Pair pair : changedFiles) { if (pair.second == DirectoryBasedStorage.this) { - VirtualFile file = pair.first; - if ("xml".equalsIgnoreCase(file.getExtension())) { + if (StringUtilRt.endsWithIgnoreCase(pair.first.getNameSequence(), ".xml")) { containsSelf = true; break; } } } - if (!containsSelf) return Collections.emptySet(); - - if (myStorageData.getComponentNames().size() == 0) { - // no state yet, so try to initialize it now - final DirectoryStorageData storageData = loadState(); - return new HashSet(storageData.getComponentNames()); + if (!containsSelf) { + return Collections.emptySet(); } - return new HashSet(myStorageData.getComponentNames()); + 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 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 8795b6ad1b5e..785fe36c6128 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 @@ -40,6 +40,7 @@ import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; public class FileBasedStorage extends XmlElementStorage { private static final Logger LOG = Logger.getInstance(FileBasedStorage.class); @@ -262,7 +263,33 @@ public class FileBasedStorage extends XmlElementStorage { super.setDefaultState(element); } + public void updatedFromStreamProvider(@NotNull Set changedComponentNames, boolean deleted) { + resetProviderCache(); + + try { + Element newElement = deleted ? null : loadDataFromStreamProvider(); + if (newElement == null) { + StorageUtil.deleteFile(myFile, this, myCachedVirtualFile); + // if data was loaded, mark as changed all loaded components + if (myLoadedData != null) { + changedComponentNames.addAll(myLoadedData.myComponentStates.keySet()); + resetData(); + } + } + else if (myLoadedData != null) { + StorageData newStorageData = createStorageData(); + loadState(newStorageData, newElement); + changedComponentNames.addAll(newStorageData.getChangedComponentNames(myLoadedData, myPathMacroSubstitutor)); + myLoadedData = newStorageData; + } + } + catch (Exception e) { + LOG.warn(e); + } + } + @Nullable + @Deprecated public File updateFileExternallyFromStreamProviders() throws IOException { Element element = getElement(loadData(true)); if (element == 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 e0902fd6c0ff..ff972dac785a 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 @@ -16,10 +16,10 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.StateStorage; -import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.Collection; @@ -33,6 +33,11 @@ public interface IApplicationStore extends IComponentStore { void setConfigPath(@NotNull String configPath); - boolean reload(@NotNull Set> changedFiles, @NotNull Collection notReloadableComponents) - throws StateStorageException, IOException; + /** + * null if reloaded + * empty list if nothing to reload + * list of not reloadable components (reload is not performed) + */ + @Nullable + Collection reload(@NotNull Set> changedFiles) throws IOException; } 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 93a65a2eb50b..efcc813b9a15 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 @@ -24,15 +24,22 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.util.Collection; import java.util.List; import java.util.Set; public interface IComponentStore { void initComponent(@NotNull Object component, boolean service); + void reinitComponents(@NotNull Set componentNames, boolean reloadData); + + @NotNull + Collection getNotReloadableComponents(@NotNull Collection componentNames); + boolean isReloadPossible(@NotNull Set componentNames); void load() throws IOException, StateStorageException; + boolean isSaving(); @NotNull @@ -48,9 +55,8 @@ public interface IComponentStore { } } - //todo:remove throws @NotNull - SaveSession startSave() throws IOException; + SaveSession startSave(); interface SaveSession { @NotNull 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 5d32996ed3f9..3f9e0c68a74e 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 StateStorageException, IOException; + boolean reload(@NotNull Set> changedFiles) throws IOException; //------ 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/ProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java index 01e2f9e036d0..b8b162884595 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 @@ -691,43 +691,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @Override - public boolean reload(@NotNull final Set> changedFiles) throws IOException, StateStorageException { - final SaveSession saveSession = startSave(); - - final Set componentNames; - try { - componentNames = saveSession.analyzeExternalChanges(changedFiles); - if (componentNames == null) return false; - - // TODO[mike]: This is a hack to prevent NPE (assert != null) in StateStorageManagerImpl.reload, storage is null for... - for (Pair pair : changedFiles) { - if (pair.second == null) { - return false; - } - } - - StorageUtil.logStateDiffInfo(changedFiles, componentNames); - - if (!isReloadPossible(componentNames)) { - return false; - } - } - finally { - finishSave(saveSession); - } - - if (!componentNames.isEmpty()) { - myProject.getMessageBus().syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateStarted(); - - try { - doReload(changedFiles, componentNames); - reinitComponents(componentNames, false); - } - finally { - myProject.getMessageBus().syncPublisher(BatchUpdateListener.TOPIC).onBatchUpdateFinished(); - } - } - - return true; + public boolean reload(@NotNull Set> changedFiles) throws IOException { + return reload(changedFiles, myProject.getMessageBus()) == null; } } 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 787c4d227336..32d170c878d1 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 @@ -17,6 +17,7 @@ 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; @@ -42,13 +43,18 @@ public interface StateStorageManager { @Nullable StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType); + @SuppressWarnings("UnusedDeclaration") @Deprecated @Nullable /** * @deprecated Use {@link #getStateStorage(String, com.intellij.openapi.components.RoamingType)} + * to remove in IDEA 15 */ StateStorage getFileStateStorage(@NotNull String fileSpec); + @NotNull + Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted); + @NotNull Collection getStorageFileNames(); @@ -87,7 +93,7 @@ public interface StateStorageManager { } interface SaveSession { - //returns set of component which were changed, null if changes are much more than just component state. + // returns set of component which were changed, null if changes are much more than just component state @Nullable Set analyzeExternalChanges(@NotNull Set> files); 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 9e4a797e4a1e..a9a9549604ad 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 @@ -21,10 +21,7 @@ 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.Disposer; -import com.intellij.openapi.util.JDOMUtil; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.RoamingTypeDisabled; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -153,6 +150,37 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di return getStateStorage(fileSpec, RoamingType.PER_USER); } + @NotNull + @Override + public Couple> getCachedFileStateStorages(@NotNull Collection changed, @NotNull Collection deleted) { + myStorageLock.lock(); + try { + return Couple.of(getCachedFileStorages(changed), getCachedFileStorages(deleted)); + } + finally { + myStorageLock.unlock(); + } + } + + @NotNull + private Collection getCachedFileStorages(@NotNull Collection fileSpecs) { + if (fileSpecs.isEmpty()) { + return Collections.emptyList(); + } + + List result = null; + for (String fileSpec : fileSpecs) { + StateStorage storage = myStorages.get(fileSpec); + if (storage instanceof FileBasedStorage) { + if (result == null) { + result = new SmartList(); + } + result.add((FileBasedStorage)storage); + } + } + return result == null ? Collections.emptyList() : result; + } + @NotNull @Override public Collection getStorageFileNames() { @@ -480,23 +508,27 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } } - //returns set of component which were changed, null if changes are much more than just component state. @Override @Nullable - public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { - Set result = new THashSet(); + public Set analyzeExternalChanges(@NotNull Set> changedFiles) { + Set result = null; for (Pair pair : changedFiles) { - final StateStorage.SaveSession saveSession = myCompoundSaveSession.getSaveSession(pair.second); + StateStorage.SaveSession saveSession = myCompoundSaveSession.getSaveSession(pair.second); if (saveSession == null) { continue; } - final Set changes = saveSession.analyzeExternalChanges(changedFiles); + + Set changes = saveSession.analyzeExternalChanges(changedFiles); if (changes == null) { return null; } + + if (result == null) { + result = new THashSet(); + } result.addAll(changes); } - return result; + return result == null ? Collections.emptySet() : result; } } 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 2344c8526b0a..52c44f57d5b4 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 @@ -127,31 +127,13 @@ public class StorageUtil { * Due to historical reasons files in ROOT_CONFIG don’t wrapped into document (xml prolog) opposite to files in APP_CONFIG */ @Nullable - static VirtualFile save(@NotNull File file, @Nullable Parent element, Object requestor, boolean wrapAsDocument, @Nullable VirtualFile cachedVirtualFile) throws StateStorageException { + static VirtualFile save(@NotNull File file, @Nullable Parent element, @NotNull Object requestor, boolean wrapAsDocument, @Nullable VirtualFile cachedVirtualFile) throws StateStorageException { if (isEmpty(element)) { - if (!file.exists()) { - return null; + try { + deleteFile(file, requestor, cachedVirtualFile); } - - 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); - } - else { - AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); - try { - virtualFile.delete(requestor); - } - catch (IOException e) { - throw new StateStorageException(e); - } - finally { - token.finish(); - } + catch (IOException e) { + throw new StateStorageException(e); } return null; } @@ -200,6 +182,30 @@ public class StorageUtil { } } + public static void deleteFile(@NotNull File file, @NotNull Object requestor, @Nullable VirtualFile cachedVirtualFile) throws IOException { + if (!file.exists()) { + return; + } + + 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); + } + else { + AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); + try { + virtualFile.delete(requestor); + } + finally { + token.finish(); + } + } + } + @NotNull private static BufferExposingByteArrayOutputStream writeToBytes(@NotNull Parent element, @NotNull String lineSeparator) throws IOException { BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); 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 61a3527314fd..ead067cc9747 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 @@ -15,6 +15,8 @@ */ package com.intellij.openapi.components.impl.stores; +import org.jetbrains.annotations.NotNull; + import java.io.IOException; /** @@ -24,7 +26,7 @@ public class StoreUtil { private StoreUtil() { } - public static void doSave(IComponentStore stateStore) throws IOException { + public static void doSave(@NotNull IComponentStore stateStore) throws IOException { IComponentStore.SaveSession session = null; try { session = stateStore.startSave(); 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 b6b2a2cd5696..819e1e4dae80 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 @@ -51,7 +51,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { protected TrackingPathMacroSubstitutor myPathMacroSubstitutor; @NotNull protected final String myRootElementName; private Object mySession; - private StorageData myLoadedData; + protected StorageData myLoadedData; protected final StreamProvider myStreamProvider; protected final String myFileSpec; protected boolean myBlockSavingTheContent = false; @@ -145,7 +145,10 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { if (useProvidersData && myStreamProvider != null && myStreamProvider.isEnabled()) { try { - loadDataFromStreamProvider(result); + Element element = loadDataFromStreamProvider(); + if (element != null) { + loadState(result, element); + } //noinspection deprecation if (!myStreamProvider.isVersioningRequired() && !(myStreamProvider instanceof OldStreamProviderAdapter || myStreamProvider instanceof CurrentUserHolder)) { @@ -166,19 +169,20 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { return result; } - private void loadDataFromStreamProvider(@NotNull StorageData result) throws IOException, JDOMException { + @Nullable + protected final Element loadDataFromStreamProvider() throws IOException, JDOMException { assert myStreamProvider != null; InputStream inputStream = myStreamProvider.loadContent(myFileSpec, myRoamingType); if (inputStream == null) { - return; + return null; } Element element = JDOMUtil.loadDocument(inputStream).getRootElement(); filterOutOfDate(element); - loadState(result, element); + return element; } - private void loadState(@NotNull StorageData result, @NotNull Element element) { + protected final void loadState(@NotNull StorageData result, @NotNull Element element) { result.load(element, myPathMacroSubstitutor, true); } @@ -465,15 +469,28 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Override @Nullable - public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { + 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(); - StorageData storageData = createStorageData(); if (element == null) { return Collections.emptySet(); } + + StorageData storageData = createStorageData(); loadState(storageData, element); - return storageData.getDifference(myStorageData, myPathMacroSubstitutor); + return storageData.getChangedComponentNames(myStorageData, myPathMacroSubstitutor); } catch (StateStorageException e) { LOG.info(e); @@ -507,7 +524,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Override - public void reload(@NotNull final Set changedComponents) throws StateStorageException { + public void reload(@NotNull final Set changedComponents) { final StorageData storageData = loadData(false); final StorageData oldLoadedData = myLoadedData; if (oldLoadedData != null) { 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 3e8941ad8bf4..fc78d5cd7700 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 @@ -33,7 +33,6 @@ 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.IComponentStore; -import com.intellij.openapi.components.impl.stores.IProjectStore; import com.intellij.openapi.components.impl.stores.StorageUtil; import com.intellij.openapi.components.impl.stores.XmlElementStorage; import com.intellij.openapi.diagnostic.Logger; @@ -54,6 +53,7 @@ import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl; import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeFrame; import com.intellij.util.Alarm; 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.HashMap; @@ -98,7 +98,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt private final Map>> myChangedProjectFiles = new HashMap>>(); private final Alarm myChangedFilesAlarm = new Alarm(); - private final List> myChangedApplicationFiles = new ArrayList>(); + private final List> myChangedApplicationFiles = new SmartList>(); private final AtomicInteger myReloadBlockCount = new AtomicInteger(0); private final ProgressManager myProgressManager; private volatile boolean myDefaultProjectWasDisposed = false; @@ -120,11 +120,8 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt messageBus.connect(app).subscribe(StateStorage.STORAGE_TOPIC, new StateStorage.Listener() { @Override - public void storageFileChanged(@NotNull final VirtualFileEvent event, @NotNull final StateStorage storage) { - VirtualFile file = event.getFile(); - if (!file.isDirectory() && !(event.getRequestor() instanceof StateStorage.SaveSession)) { - saveChangedProjectFile(file, null, storage); - } + public void storageFileChanged(@NotNull VirtualFileEvent event, @NotNull StateStorage storage) { + projectStorageFileChanged(event, storage, null); } }); @@ -138,10 +135,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt connection.subscribe(StateStorage.STORAGE_TOPIC, new StateStorage.Listener() { @Override public void storageFileChanged(@NotNull final VirtualFileEvent event, @NotNull final StateStorage storage) { - VirtualFile file = event.getFile(); - if (!file.isDirectory() && !(event.getRequestor() instanceof StateStorage.SaveSession)) { - saveChangedProjectFile(file, project, storage); - } + projectStorageFileChanged(event, storage, project); } }); @@ -182,6 +176,13 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt registerExternalProjectFileListener(virtualFileManager); } + private void projectStorageFileChanged(@NotNull VirtualFileEvent event, @NotNull StateStorage storage, @Nullable Project project) { + VirtualFile file = event.getFile(); + if (!file.isDirectory() && !(event.getRequestor() instanceof StateStorage.SaveSession)) { + registerProjectToReload(project, file, storage); + } + } + @Override public void initComponent() { } @@ -242,6 +243,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt String message = e.getMessage(); if (message != null) return message; message = e.getLocalizedMessage(); + //noinspection ConstantConditions if (message != null) return message; message = e.toString(); Throwable cause = e.getCause(); @@ -641,34 +643,35 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt private boolean tryToReloadApplication() { try { final Application app = ApplicationManager.getApplication(); + if (app.isDisposed()) { + return false; + } + final Set> causes = new THashSet>(myChangedApplicationFiles); + if (causes.isEmpty()) { + return true; + } - if (app.isDisposed()) return false; - final HashSet> causes = new HashSet>(myChangedApplicationFiles); - if (causes.isEmpty()) return true; - - final boolean[] reloadOk = {false}; - final LinkedHashSet components = new LinkedHashSet(); - + final Ref> reloadResult = Ref.create(); ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { try { - reloadOk[0] = ((ApplicationImpl)app).getStateStore().reload(causes, components); + reloadResult.set(((ApplicationImpl)app).getStateStore().reload(causes)); } - catch (StateStorageException e) { - Messages.showWarningDialog(ProjectBundle.message("project.reload.failed", e.getMessage()), - ProjectBundle.message("project.reload.failed.title")); - } - catch (IOException e) { + catch (Exception e) { Messages.showWarningDialog(ProjectBundle.message("project.reload.failed", e.getMessage()), ProjectBundle.message("project.reload.failed.title")); } } }); - if (!reloadOk[0] && !components.isEmpty()) { + if (reloadResult.isNull()) { + return true; + } + + if (!reloadResult.get().isEmpty()) { String message = "Application components were changed externally and cannot be reloaded:\n"; - for (String component : components) { + for (String component : reloadResult.get()) { message += component + "\n"; } @@ -687,8 +690,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt ApplicationManagerEx.getApplicationEx().restart(true); } } - - return reloadOk[0]; + return false; } finally { myChangedApplicationFiles.clear(); @@ -788,8 +790,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - if (!tryToReloadApplication()) return; - askToReloadProjectIfConfigFilesChangedExternally(); + if (tryToReloadApplication()) { + askToReloadProjectIfConfigFilesChangedExternally(); + } } }, ModalityState.NON_MODAL); //} @@ -818,20 +821,21 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt @Override public void saveChangedProjectFile(@NotNull VirtualFile file, @Nullable Project project) { - if (file.exists()) { - copyToTemp(file); - } registerProjectToReload(project, file, null); } - private void saveChangedProjectFile(final VirtualFile file, @Nullable final Project project, final StateStorage storage) { - if (file.exists()) { - copyToTemp(file); - } - 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); + } + } + if (LOG.isDebugEnabled()) { LOG.debug("[RELOAD] Registering project to reload: " + cause, new Exception()); } @@ -840,7 +844,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt synchronized (myChangedProjectFiles) { List> changedProjectFiles = myChangedProjectFiles.get(project); if (changedProjectFiles == null) { - changedProjectFiles = new ArrayList>(); + changedProjectFiles = new SmartList>(); myChangedProjectFiles.put(project, changedProjectFiles); } @@ -863,17 +867,6 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt }, 444); } - private void copyToTemp(VirtualFile file) { - try { - final byte[] bytes = file.contentsToByteArray(); - mySavedCopies.put(file, bytes); - mySavedTimestamps.put(file, file.getTimeStamp()); - } - catch (IOException e) { - LOG.error(e); - } - } - private void restoreCopy(VirtualFile file) { try { if (file == null) return; // Externally deleted actually. @@ -917,17 +910,17 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt public void run() { LOG.debug("Reloading project."); ProjectImpl projectImpl = (ProjectImpl)project[0]; - if (projectImpl.isDisposed()) return; - IProjectStore projectStore = projectImpl.getStateStore(); + if (projectImpl.isDisposed()) { + return; + } final String location = projectImpl.getPresentableUrl(); - final List original; try { - IComponentStore.SaveSession saveSession = projectStore.startSave(); + IComponentStore.SaveSession saveSession = projectImpl.getStateStore().startSave(); original = saveSession.getAllStorageFiles(true); saveSession.finishSave(); } - catch (IOException e) { + catch (Exception e) { LOG.error(e); return; } 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 ef68ac1e3a7f..1636c2993273 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java @@ -44,7 +44,7 @@ public interface StateStorage { void finishSave(@NotNull SaveSession saveSession); - void reload(@NotNull Set changedComponents) throws StateStorageException; + void reload(@NotNull Set changedComponents); interface ExternalizationSession { void setState(@NotNull Object component, final String componentName, @NotNull Object state, @Nullable final Storage storageSpec) throws StateStorageException; @@ -53,6 +53,9 @@ public interface StateStorage { interface SaveSession { void save() throws StateStorageException; + /** + * Get changed component names + */ @Nullable Set analyzeExternalChanges(@NotNull Set> changedFiles); 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 699440e22b54..7a59717665f7 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 @@ -188,7 +188,7 @@ public class StorageData { myHash = -1; } - public Set getDifference(final StorageData storageData, PathMacroSubstitutor substitutor) { + public Set getChangedComponentNames(@NotNull StorageData storageData, @Nullable PathMacroSubstitutor substitutor) { Set bothStates = new THashSet(myComponentStates.keySet()); bothStates.retainAll(storageData.myComponentStates.keySet()); diff --git a/platform/testFramework/src/com/intellij/mock/MockProjectStore.java b/platform/testFramework/src/com/intellij/mock/MockProjectStore.java index 73b353be1e2c..bbd31d8be20c 100644 --- a/platform/testFramework/src/com/intellij/mock/MockProjectStore.java +++ b/platform/testFramework/src/com/intellij/mock/MockProjectStore.java @@ -130,7 +130,7 @@ public class MockProjectStore implements IProjectStore { @Override @NotNull - public SaveSession startSave() throws IOException { + public SaveSession startSave() { throw new UnsupportedOperationException("Method startSave not implemented in " + getClass()); }