diff options
Diffstat (limited to 'platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java')
-rw-r--r-- | platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java | 304 |
1 files changed, 145 insertions, 159 deletions
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 b028676168d5..76f47db77533 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 @@ -15,44 +15,48 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.openapi.Disposable; 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.WriteExternalException; import com.intellij.openapi.vfs.SafeWriteRequestor; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; +import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; import org.jdom.Document; import org.jdom.Element; +import org.jdom.JDOMException; import org.jdom.filter.ElementFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.*; public abstract class XmlElementStorage implements StateStorage, Disposable { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.XmlElementStorage"); + private static final Logger LOG = Logger.getInstance(XmlElementStorage.class); + + private final static RoamingElementFilter DISABLED_ROAMING_ELEMENT_FILTER = new RoamingElementFilter(RoamingType.DISABLED); private static final String ATTR_NAME = "name"; private static final String VERSION_FILE_SUFFIX = ".ver"; protected TrackingPathMacroSubstitutor myPathMacroSubstitutor; - @NotNull private final String myRootElementName; + @NotNull protected final String myRootElementName; private Object mySession; private StorageData myLoadedData; protected final StreamProvider myStreamProvider; protected final String myFileSpec; - private final ComponentRoamingManager myComponentRoamingManager; protected boolean myBlockSavingTheContent = false; protected int myUpToDateHash = -1; - protected int myProviderUpToDateHash = -1; + private int myProviderUpToDateHash = -1; private boolean mySavingDisabled = false; private final Map<String, Object> myStorageComponentStates = new THashMap<String, Object>(); // at load we store Element, on setState Integer of hash @@ -60,6 +64,8 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { private final ComponentVersionProvider myLocalVersionProvider; protected final RemoteComponentVersionProvider myRemoteVersionProvider; + private final RoamingType myRoamingType; + protected ComponentVersionListener myListener = new ComponentVersionListener(){ @Override public void componentStateChanged(String componentName) { @@ -69,18 +75,18 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { private boolean myDisposed; - protected XmlElementStorage(@Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, + protected XmlElementStorage(@NotNull String fileSpec, + @Nullable RoamingType roamingType, + @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, @NotNull Disposable parentDisposable, @NotNull String rootElementName, @Nullable StreamProvider streamProvider, - String fileSpec, - ComponentRoamingManager componentRoamingManager, ComponentVersionProvider componentVersionProvider) { + myFileSpec = fileSpec; + myRoamingType = roamingType == null ? RoamingType.PER_USER : roamingType; myPathMacroSubstitutor = pathMacroSubstitutor; myRootElementName = rootElementName; - myStreamProvider = streamProvider; - myFileSpec = fileSpec; - myComponentRoamingManager = componentRoamingManager; + myStreamProvider = myRoamingType == RoamingType.DISABLED ? null : streamProvider; Disposer.register(parentDisposable, this); myLocalVersionProvider = componentVersionProvider; @@ -92,36 +98,39 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Nullable - protected abstract Document loadDocument() throws StateStorageException; + protected abstract Element loadLocalData(); @Nullable - public synchronized Element getState(final String componentName) throws StateStorageException { + 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, final String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull String componentName, final Class<?> aClass, final boolean reloadData) throws StateStorageException { return getStorageData(reloadData).hasState(componentName); } @Override @Nullable - public <T> T getState(final Object component, final String componentName, Class<T> stateClass, @Nullable T mergeInto) throws StateStorageException { + public <T> T getState(final Object component, @NotNull String componentName, Class<T> stateClass, @Nullable T mergeInto) throws StateStorageException { return DefaultStateSerializer.deserializeState(getState(componentName), stateClass, mergeInto); } @NotNull - protected StorageData getStorageData(final boolean reloadData) throws StateStorageException { + protected StorageData getStorageData() { + return getStorageData(false); + } + + @NotNull + private StorageData getStorageData(boolean reloadData) { if (myLoadedData != null && !reloadData) { return myLoadedData; } @@ -131,48 +140,48 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @NotNull - protected StorageData loadData(boolean useProvidersData) throws StateStorageException { - Document document = loadDocument(); + protected StorageData loadData(boolean useProvidersData) { StorageData result = createStorageData(); - if (document != null) { - loadState(result, document.getRootElement()); - } - if (useProvidersData && myStreamProvider != null && myStreamProvider.isEnabled()) { - for (RoamingType roamingType : RoamingType.values()) { - if (roamingType != RoamingType.DISABLED && roamingType != RoamingType.GLOBAL) { - try { - Document sharedDocument = StorageUtil.loadDocument(myStreamProvider.loadContent(myFileSpec, roamingType)); - if (sharedDocument != null) { - filterOutOfDate(sharedDocument.getRootElement()); - loadState(result, sharedDocument.getRootElement()); - } - } - catch (Exception e) { - LOG.warn(e); - } - } + boolean wasLoaded = false; + try { + wasLoaded = loadDataFromStreamProvider(result); + } + catch (Exception e) { + LOG.warn(e); + } + + //noinspection deprecation + if (wasLoaded && !myStreamProvider.isVersioningRequired() && !(myStreamProvider instanceof OldStreamProviderAdapter || myStreamProvider instanceof CurrentUserHolder)) { + // we don't use local data if stream provider has one (to preserve backward compatibility, we don't use this logic for old stream providers) + return result; } } + Element element = loadLocalData(); + if (element != null) { + loadState(result, element); + } + return result; } - protected void loadState(final StorageData result, final Element element) throws StateStorageException { - if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.expandPaths(element); + private boolean loadDataFromStreamProvider(@NotNull StorageData result) throws IOException, JDOMException { + assert myStreamProvider != null; + InputStream inputStream = myStreamProvider.loadContent(myFileSpec, myRoamingType); + if (inputStream == null) { + return false; } - IdeaPluginDescriptorImpl.internJDOMElement(element); + Element element = JDOMUtil.loadDocument(inputStream).getRootElement(); + filterOutOfDate(element); + loadState(result, element); + return true; + } - try { - result.load(element); - result.checkUnknownMacros(myPathMacroSubstitutor); - } - catch (IOException e) { - throw new StateStorageException(e); - } + private void loadState(@NotNull StorageData result, @NotNull Element element) { + result.load(element, myPathMacroSubstitutor, true); } @NotNull @@ -182,25 +191,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { public void setDefaultState(final Element element) { myLoadedData = createStorageData(); - try { - loadState(myLoadedData, element); - } - catch (StateStorageException e) { - LOG.error(e); - } + loadState(myLoadedData, element); } @Override @NotNull public ExternalizationSession startExternalization() { - try { - final ExternalizationSession session = new MyExternalizationSession(getStorageData(false).clone(), myListener); - mySession = session; - return session; - } - catch (StateStorageException e) { - throw new RuntimeException(e); - } + ExternalizationSession session = new MyExternalizationSession(getStorageData().clone(), myListener); + mySession = session; + return session; } @Override @@ -226,19 +225,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @NotNull @Override - public Collection<IFile> getStorageFilesToSave() throws StateStorageException { + public Collection<File> getStorageFilesToSave() throws StateStorageException { return Collections.emptySet(); } @NotNull @Override - public List<IFile> getAllStorageFiles() { + public List<File> getAllStorageFiles() { return Collections.emptyList(); } }; } - protected abstract MySaveSession createSaveSession(final MyExternalizationSession externalizationSession); + protected abstract MySaveSession createSaveSession(MyExternalizationSession externalizationSession); @Override public void finishSave(@NotNull final SaveSession saveSession) { @@ -265,22 +264,26 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Override - public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) throws StateStorageException { + public void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, @Nullable Storage storageSpec) { assert mySession == this; + Element element; try { - setState(componentName, DefaultStateSerializer.serializeState(state, storageSpec)); + element = DefaultStateSerializer.serializeState(state, storageSpec); } catch (WriteExternalException e) { LOG.debug(e); + return; } - } - private synchronized void setState(final String componentName, final Element element) { - if (element.getAttributes().isEmpty() && element.getChildren().isEmpty()) { + 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 { @@ -296,8 +299,12 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - protected Document getDocument(StorageData data) { - final Element element = data.save(); + @Nullable + protected Element getElement(@NotNull StorageData data) { + Element element = data.save(); + if (element == null || JDOMUtil.isEmpty(element)) { + return null; + } if (myPathMacroSubstitutor != null) { try { @@ -308,18 +315,18 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - return new Document(element); + return element; } protected abstract class MySaveSession implements SaveSession, SafeWriteRequestor { final StorageData myStorageData; - private Document myDocumentToSave; + private Element myElementToSave; public MySaveSession(MyExternalizationSession externalizationSession) { myStorageData = externalizationSession.myStorageData; } - public final boolean needsSave() throws StateStorageException { + public final boolean needsSave() { assert mySession == this; return _needsSave(calcHash()); } @@ -382,11 +389,10 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { try { if (myStreamProvider != null && myStreamProvider.isEnabled() && (myProviderUpToDateHash == -1 || myProviderUpToDateHash != hash)) { try { - //noinspection IfStatementWithIdenticalBranches - if (saveForProvider(myStreamProvider)) { - //noinspection UnnecessaryReturnStatement - return; - } + saveForProvider(); + } + catch (IOException e) { + LOG.warn(e); } finally { myProviderUpToDateHash = hash; @@ -398,9 +404,9 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - private void saveLocally(final Integer hash) { + private void saveLocally(int hash) { try { - if (!isHashUpToDate(hash) && _needsSave(hash)) { + if (!(myUpToDateHash != -1 && myUpToDateHash == hash) && _needsSave(hash)) { doSave(); } } @@ -409,15 +415,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - private boolean saveForProvider(@NotNull StreamProvider streamProvider) { - if (!streamProvider.isApplicable(myFileSpec, RoamingType.PER_USER)) { - return false; + private void saveForProvider() throws IOException { + if (!myStreamProvider.isApplicable(myFileSpec, myRoamingType)) { + return; } - Document document = getDocumentToSave(); - Element rootElement = document.getRootElement(); - if (rootElement.getChildren().isEmpty()) { - return false; + Element element = getElementToSave(); + if (element == null || element.getChildren().isEmpty()) { + myStreamProvider.delete(myFileSpec, myRoamingType); + return; } // skip the whole document if some component has disabled roaming type @@ -425,59 +431,34 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { // one exclusion: workspace file (you don't have choice in this case) // for example, it is important for ICS ProjectId - we cannot keep project in another place, // but this project id must not be shared - if (!myFileSpec.equals(StoragePathMacros.WORKSPACE_FILE) && - rootElement.getContent(new RoamingElementFilter(RoamingType.DISABLED)).iterator().hasNext()) { - return false; - } - - RoamingElementFilter perPlatformFilter = new RoamingElementFilter(RoamingType.PER_PLATFORM); - if (rootElement.getContent(perPlatformFilter).iterator().hasNext()) { - return doSaveForProvider(rootElement, new RoamingElementFilter(RoamingType.PER_USER)) || - doSaveForProvider(rootElement, perPlatformFilter); + 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); + } } else { - return doSaveForProvider(document, RoamingType.PER_USER, streamProvider); + doSaveForProvider(element, myRoamingType, myStreamProvider); } } - private boolean doSaveForProvider(Element element, RoamingElementFilter filter) { - Element copiedElement = JDOMUtil.cloneElement(element, filter); - return copiedElement != null && doSaveForProvider(new Document(copiedElement), filter.myRoamingType, myStreamProvider); - } - - private boolean doSaveForProvider(Document actualDocument, RoamingType roamingType, StreamProvider streamProvider) { - try { - boolean result = StorageUtil.doSendContent(streamProvider, myFileSpec, actualDocument, roamingType, true); - if (streamProvider.isVersioningRequired()) { - TObjectLongHashMap<String> versions = loadVersions(actualDocument.getRootElement().getChildren(StorageData.COMPONENT)); - if (!versions.isEmpty()) { - Document versionDoc = new Document(StateStorageManagerImpl.createComponentVersionsXml(versions)); - StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); - } + private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @NotNull StreamProvider streamProvider) throws IOException { + StorageUtil.doSendContent(streamProvider, myFileSpec, element, roamingType, true); + if (streamProvider.isVersioningRequired()) { + TObjectLongHashMap<String> versions = loadVersions(element.getChildren(StorageData.COMPONENT)); + if (!versions.isEmpty()) { + Element versionDoc = StateStorageManagerImpl.createComponentVersionsXml(versions); + StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); } - return result; - } - catch (IOException e) { - LOG.warn(e); - return false; } } - private boolean isHashUpToDate(final Integer hash) { - return myUpToDateHash != -1 && myUpToDateHash == hash; - } - - protected Document getDocumentToSave() { - if (myDocumentToSave != null) return myDocumentToSave; - - final Element element = myStorageData.save(); - myDocumentToSave = new Document(element); - - if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.collapsePaths(element); + @Nullable + protected Element getElementToSave() { + if (myElementToSave == null) { + myElementToSave = getElement(myStorageData); } - - return myDocumentToSave; + return myElementToSave; } public StorageData getData() { @@ -488,12 +469,12 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Nullable public Set<String> analyzeExternalChanges(@NotNull final Set<Pair<VirtualFile,StateStorage>> changedFiles) { try { - Document document = loadDocument(); + Element element = loadLocalData(); StorageData storageData = createStorageData(); - if (document == null) { + if (element == null) { return Collections.emptySet(); } - loadState(storageData, document.getRootElement()); + loadState(storageData, element); return storageData.getDifference(myStorageData, myPathMacroSubstitutor); } catch (StateStorageException e) { @@ -502,21 +483,6 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { return null; } - - private class RoamingElementFilter extends ElementFilter { - final RoamingType myRoamingType; - - public RoamingElementFilter(RoamingType roamingType) { - super(StorageData.COMPONENT); - - myRoamingType = roamingType; - } - - @Override - public boolean matches(Object obj) { - return super.matches(obj) && myComponentRoamingManager.getRoamingType(((Element)obj).getAttributeValue(StorageData.NAME)) == myRoamingType; - } - } } private TObjectLongHashMap<String> loadVersions(List<Element> elements) { @@ -545,11 +511,9 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Override public void reload(@NotNull final Set<String> changedComponents) throws StateStorageException { final StorageData storageData = loadData(false); - final StorageData oldLoadedData = myLoadedData; - if (oldLoadedData != null) { - Set<String> componentsToRetain = new HashSet<String>(oldLoadedData.myComponentStates.keySet()); + Set<String> componentsToRetain = new THashSet<String>(oldLoadedData.myComponentStates.keySet()); componentsToRetain.addAll(changedComponents); // add empty configuration tags for removed components @@ -568,7 +532,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { myLoadedData = storageData; } - private void filterOutOfDate(Element element) { + private void filterOutOfDate(@NotNull Element element) { if (myRemoteVersionProvider == null) { return; } @@ -587,12 +551,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Nullable - Document logComponents() throws StateStorageException { - return mySession instanceof MySaveSession ? getDocument(((MySaveSession)mySession).myStorageData) : null; + Element logComponents() { + return mySession instanceof MySaveSession ? getElement(((MySaveSession)mySession).myStorageData) : null; } - protected class RemoteComponentVersionProvider implements ComponentVersionProvider { - protected TObjectLongHashMap<String> myProviderVersions; + public void resetProviderCache() { + myProviderUpToDateHash = -1; + if (myRemoteVersionProvider != null) { + myRemoteVersionProvider.myProviderVersions = null; + } + } + + private final class RemoteComponentVersionProvider implements ComponentVersionProvider { + private TObjectLongHashMap<String> myProviderVersions; @Override public long getVersion(String name) { @@ -632,4 +603,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } } + + private static class RoamingElementFilter extends ElementFilter { + final RoamingType myRoamingType; + + public RoamingElementFilter(RoamingType roamingType) { + super(StorageData.COMPONENT); + + myRoamingType = roamingType; + } + + @Override + public boolean matches(Object obj) { + return super.matches(obj) && ComponentRoamingManager.getInstance().getRoamingType(((Element)obj).getAttributeValue(StorageData.NAME)) == myRoamingType; + } + } } |