diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsPersistenceProjectTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsPersistenceProjectTests.java index 7dbdb464a2d..bf05ecf8948 100644 --- a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsPersistenceProjectTests.java +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsPersistenceProjectTests.java @@ -24,6 +24,7 @@ import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; +import org.eclipse.cdt.core.settings.model.ILanguageSettingsEditableProvider; import org.eclipse.cdt.core.settings.model.WriteAccessException; import org.eclipse.cdt.core.testplugin.CModelMock; import org.eclipse.cdt.core.testplugin.ResourceHelper; @@ -103,13 +104,20 @@ public class LanguageSettingsPersistenceProjectTests extends TestCase { } } - private class MockProvider extends LanguageSettingsSerializable { - public MockProvider(String id, String name) { + private class MockEditableProvider extends LanguageSettingsSerializable implements ILanguageSettingsEditableProvider { + public MockEditableProvider(String id, String name) { super(id, name); } + @Override + public MockEditableProvider cloneShallow() throws CloneNotSupportedException { + return (MockEditableProvider) super.cloneShallow(); + } + @Override + public MockEditableProvider clone() throws CloneNotSupportedException { + return (MockEditableProvider) super.clone(); + } } - /** * Constructor. * @param name - name of the test. @@ -178,21 +186,88 @@ public class LanguageSettingsPersistenceProjectTests extends TestCase { /** */ - public void testReadOnlyDescription() throws Exception { + public void testProjectDescription_ReadWriteProviders() throws Exception { // create a project IProject project = ResourceHelper.createCDTProjectWithConfig(getName()); - // get read-only description - ICProjectDescription prjDescription = CProjectDescriptionManager.getInstance().getProjectDescription(project, false); - assertNotNull(prjDescription); - ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); - assertNotNull(cfgDescription); + + { + // get read-only description + ICProjectDescription prjDescription = CProjectDescriptionManager.getInstance().getProjectDescription(project, false); + assertNotNull(prjDescription); + ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); + assertNotNull(cfgDescription); + + // try to write to it + try { + List providers = new ArrayList(); + cfgDescription.setLanguageSettingProviders(providers); + fail("WriteAccessException was expected but it was not throw."); + } catch (WriteAccessException e) { + // exception is expected + } + } - try { + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + { + // get project descriptions + ICProjectDescription writableProjDescription = coreModel.getProjectDescription(project); + assertNotNull(writableProjDescription); + ICConfigurationDescription[] cfgDescriptions = writableProjDescription.getConfigurations(); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + // create a provider + LanguageSettingsSerializable mockProvider = new MockEditableProvider(PROVIDER_0, PROVIDER_NAME_0); + mockProvider.setStoringEntriesInProjectArea(true); + mockProvider.setSettingEntries(cfgDescription, null, null, entries); List providers = new ArrayList(); + providers.add(mockProvider); cfgDescription.setLanguageSettingProviders(providers); - fail("WriteAccessException was expected but it was not throw."); - } catch (WriteAccessException e) { - // exception is expected + List storedProviders = cfgDescription.getLanguageSettingProviders(); + assertEquals(1, storedProviders.size()); + + // apply new project description to the project model + coreModel.setProjectDescription(project, writableProjDescription); + } + { + // get read-only project descriptions + ICProjectDescription readOnlyProjDescription = coreModel.getProjectDescription(project, false); + assertNotNull(readOnlyProjDescription); + ICConfigurationDescription[] cfgDescriptions = readOnlyProjDescription.getConfigurations(); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + List providers = cfgDescription.getLanguageSettingProviders(); + assertEquals(1, providers.size()); + ILanguageSettingsProvider loadedProvider = providers.get(0); + assertTrue(loadedProvider instanceof LanguageSettingsSerializable); + assertEquals(PROVIDER_0, loadedProvider.getId()); + assertEquals(PROVIDER_NAME_0, loadedProvider.getName()); + + List actual = loadedProvider.getSettingEntries(cfgDescription, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + { + // get writable project descriptions + ICProjectDescription writableProjDescription = coreModel.getProjectDescription(project); + assertNotNull(writableProjDescription); + ICConfigurationDescription[] cfgDescriptions = writableProjDescription.getConfigurations(); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + List providers = cfgDescription.getLanguageSettingProviders(); + assertEquals(1, providers.size()); + ILanguageSettingsProvider loadedProvider = providers.get(0); + assertTrue(loadedProvider instanceof LanguageSettingsSerializable); + assertEquals(PROVIDER_0, loadedProvider.getId()); + assertEquals(PROVIDER_NAME_0, loadedProvider.getName()); + + List actual = loadedProvider.getSettingEntries(cfgDescription, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); } } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsExtensionManager.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsExtensionManager.java index c5b204df57f..9bb935d61cd 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsExtensionManager.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsExtensionManager.java @@ -12,6 +12,7 @@ package org.eclipse.cdt.internal.core.language.settings.providers; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; @@ -24,11 +25,13 @@ import org.eclipse.cdt.core.AbstractExecutableExtensionBase; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsBaseProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializable; import org.eclipse.cdt.core.model.ILanguage; import org.eclipse.cdt.core.model.LanguageManager; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICSettingEntry; import org.eclipse.cdt.core.settings.model.ILanguageSettingsEditableProvider; import org.eclipse.cdt.core.settings.model.util.CDataUtil; @@ -37,7 +40,10 @@ import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; @@ -613,5 +619,141 @@ public class LanguageSettingsExtensionManager { return provider.equals(extensionProvider); } + /** + * TODO - remove me + * Temporary method to report inconsistency in log. + */ + @Deprecated + public static void assertConsistency(ICProjectDescription prjDescription) { + if (prjDescription != null) { + List prjProviders = new ArrayList(); + for (ICConfigurationDescription cfgDescription : prjDescription.getConfigurations()) { + List providers = cfgDescription.getLanguageSettingProviders(); + for (ILanguageSettingsProvider provider : providers) { + if (!LanguageSettingsManager.isWorkspaceProvider(provider)) { + if (isObjectInTheList(prjProviders, provider)) { + IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, "Inconsistent state, duplicate LSP in project description " + + "[" + System.identityHashCode(provider) + "] " + + provider); + CoreException e = new CoreException(status); + CCorePlugin.log(e); + } + prjProviders.add(provider); + } + } + } + } + } + + /** + * TODO - remove me + * Temporary method to report inconsistency in log. + */ + @Deprecated + public static void assertConsistency(ICConfigurationDescription cfgDescription) { + List listeners = new ArrayList(); + List providers = cfgDescription.getLanguageSettingProviders(); + for (ILanguageSettingsProvider provider : providers) { + if (isObjectInTheList(listeners, provider)) { + IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, "Inconsistent state, duplicate LSP in project description " + provider); + CoreException e = new CoreException(status); + CCorePlugin.log(e); + } + listeners.add(provider); + } + } + + /** + * Get a providers list including only providers of type IResourceChangeListener + * for a given project description - collecting from all configurations. + */ + private static List getResourceChangeListeners(ICProjectDescription prjDescription) { + List listeners = new ArrayList(); + if (prjDescription != null) { + for (ICConfigurationDescription cfgDescription : prjDescription.getConfigurations()) { + List providers = cfgDescription.getLanguageSettingProviders(); + for (ILanguageSettingsProvider provider : providers) { + if (provider instanceof IResourceChangeListener) { + listeners.add((IResourceChangeListener) provider); + } + } + } + } + return listeners; + } + + /** + * Unregister listeners which are not used anymore and register new listeners. + * The method is used when project description is applied to workspace. + * @param oldPrjDescription - old project descriptions being replaced in the workspace. + * @param newPrjDescription - new project description being applied to the workspace. + */ + public static void reRegisterListeners(ICProjectDescription oldPrjDescription, ICProjectDescription newPrjDescription) { + if (oldPrjDescription == newPrjDescription) { + assertConsistency(oldPrjDescription); // TODO - remove me + return; + } + + assertConsistency(oldPrjDescription); // TODO - remove me + assertConsistency(newPrjDescription); // TODO - remove me + + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + + List oldListeners = getResourceChangeListeners(oldPrjDescription); + List newListeners = getResourceChangeListeners(newPrjDescription); + + for (IResourceChangeListener listener : oldListeners) { + if (!isObjectInTheList(newListeners, listener)) { + workspace.removeResourceChangeListener(listener); + // TODO - remove me + CCorePlugin.log(new Status(IStatus.WARNING,CCorePlugin.PLUGIN_ID, oldPrjDescription.getProject() + ": Removed IResourceChangeListener " + + "[" + System.identityHashCode(listener) + "] " + + listener)); + } + } + + for (IResourceChangeListener listener : newListeners) { + if (!isObjectInTheList(oldListeners, listener)) { + workspace.addResourceChangeListener(listener); + // TODO - remove me + CCorePlugin.log(new Status(IStatus.WARNING,CCorePlugin.PLUGIN_ID, newPrjDescription.getProject() + ": Added IResourceChangeListener " + + "[" + System.identityHashCode(listener) + "] " + + listener)); + } + } + + } + + private static boolean isObjectInTheList(Collection list, T element) { + // list.contains(element) won't do it as we are interested in exact object, not in equal object + for (T elem : list) { + if (elem == element) + return true; + } + return false; + } + + /** + * Deep clone of a list of language settings providers. + * + * @param baseProviders - list of providers to clone. + * @return newly cloned list. + */ + public static List cloneProviders(List baseProviders) { + List newProviders = new ArrayList(); + for (ILanguageSettingsProvider provider : baseProviders) { + if (provider instanceof ILanguageSettingsEditableProvider) { + try { + provider = ((ILanguageSettingsEditableProvider) provider).clone(); + } catch (CloneNotSupportedException e) { + IStatus status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, "Not able to clone provider " + provider.getClass()); + CCorePlugin.log(new CoreException(status)); + } + } + newProviders.add(provider); + } + return new ArrayList(newProviders); + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java index 63dd2cac673..60418d3431a 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java @@ -41,6 +41,7 @@ import org.eclipse.cdt.internal.core.COwner; import org.eclipse.cdt.internal.core.COwnerConfiguration; import org.eclipse.cdt.internal.core.cdtvariables.StorableCdtVariables; import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsExtensionManager; import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; import org.eclipse.cdt.utils.envvar.StorableEnvironment; import org.eclipse.core.runtime.CoreException; @@ -188,7 +189,7 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ copyExtensionInfo(base); - fLanguageSettingsProviders = new ArrayList(base.getLanguageSettingProviders()); + fLanguageSettingsProviders = LanguageSettingsExtensionManager.cloneProviders(base.getLanguageSettingProviders()); } // private void copyRefInfos(Map infosMap){ diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java index f48a2d6d2bf..61649f71125 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/xml/XmlProjectDescriptionStorage.java @@ -34,7 +34,6 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager_TBD; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; import org.eclipse.cdt.core.settings.model.ICSettingsStorage; @@ -43,6 +42,7 @@ import org.eclipse.cdt.core.settings.model.extension.ICProjectConverter; import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.cdt.internal.core.XmlUtil; import org.eclipse.cdt.internal.core.envvar.ContributedEnvironment; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsExtensionManager; import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; import org.eclipse.cdt.internal.core.settings.model.AbstractCProjectDescriptionStorage; import org.eclipse.cdt.internal.core.settings.model.CProjectDescription; @@ -365,6 +365,7 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto if (!overwriteIfExists && fProjectDescription.get() != null) return false; + ICProjectDescription oldDes = fProjectDescription.get(); if (des != null) { if (project.exists() && project.isOpen()) { fProjectDescription = new SoftReference(des); @@ -375,6 +376,8 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto } else { fProjectDescription = new SoftReference(null); } + + LanguageSettingsExtensionManager.reRegisterListeners(oldDes, fProjectDescription.get()); return true; } @@ -484,10 +487,11 @@ public class XmlProjectDescriptionStorage extends AbstractCProjectDescriptionSto // Update the modification stamp projectModificaitonStamp = getModificationStamp(project.getFile(ICProjectDescriptionStorageType.STORAGE_FILE_NAME)); CProjectDescription des = new CProjectDescription(project, new XmlStorage(storage), storage, true, false); - LanguageSettingsProvidersSerializer.loadLanguageSettings(des); try { setThreadLocalProjectDesc(des); des.loadDatas(); + + LanguageSettingsProvidersSerializer.loadLanguageSettings(des); des.doneLoading(); } finally { setThreadLocalProjectDesc(null);