diff --git a/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF index 34e30a14a40..5083f3d95c8 100644 --- a/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core.tests/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Export-Package: org.eclipse.cdt.core.cdescriptor.tests, org.eclipse.cdt.core.internal.tests;x-internal:=true, org.eclipse.cdt.core.internal.tests.filesystem.ram, org.eclipse.cdt.core.language, + org.eclipse.cdt.core.language.settings.providers, org.eclipse.cdt.core.model.tests, org.eclipse.cdt.core.parser.tests, org.eclipse.cdt.core.parser.tests.ast2, diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/AllLanguageSettingsProvidersCoreTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/AllLanguageSettingsProvidersCoreTests.java new file mode 100644 index 00000000000..624bd1f146f --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/AllLanguageSettingsProvidersCoreTests.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Test suite to cover core Language Settings Providers functionality. + */ +public class AllLanguageSettingsProvidersCoreTests { + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite(AllLanguageSettingsProvidersCoreTests.class.getName()); + + suite.addTest(LanguageSettingsExtensionsTests.suite()); + suite.addTest(LanguageSettingsManagerTests.suite()); + suite.addTest(LanguageSettingsSerializableProviderTests.suite()); + suite.addTest(LanguageSettingsPersistenceProjectTests.suite()); + return suite; + } +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsExtensionsTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsExtensionsTests.java new file mode 100644 index 00000000000..3aa46779c0e --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsExtensionsTests.java @@ -0,0 +1,402 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.settings.model.CIncludeFileEntry; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CLibraryFileEntry; +import org.eclipse.cdt.core.settings.model.CLibraryPathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.CMacroFileEntry; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsExtensionManager; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; + +/** + * Test cases testing LanguageSettingsProvider extensions + */ +public class LanguageSettingsExtensionsTests extends BaseTestCase { + // These should match corresponding entries defined in plugin.xml + /*package*/ static final String EXTENSION_BASE_PROVIDER_ID = "org.eclipse.cdt.core.tests.language.settings.base.provider"; + /*package*/ static final String EXTENSION_BASE_PROVIDER_NAME = "Test Plugin Mock Language Settings Base Provider"; + /*package*/ static final String EXTENSION_BASE_PROVIDER_LANG_ID = "org.eclipse.cdt.core.tests.language.id"; + /*package*/ static final String EXTENSION_BASE_PROVIDER_PARAMETER = "custom parameter"; + /*package*/ static final String EXTENSION_BASE_PROVIDER_ATTR_PARAMETER = "parameter"; + /*package*/ static final String EXTENSION_CUSTOM_PROVIDER_ID = "org.eclipse.cdt.core.tests.custom.language.settings.provider"; + /*package*/ static final String EXTENSION_CUSTOM_PROVIDER_NAME = "Test Plugin Mock Language Settings Provider"; + /*package*/ static final String EXTENSION_BASE_SUBCLASS_PROVIDER_ID = "org.eclipse.cdt.core.tests.language.settings.base.provider.subclass"; + /*package*/ static final String EXTENSION_BASE_SUBCLASS_PROVIDER_PARAMETER = "custom parameter subclass"; + /*package*/ static final String EXTENSION_SERIALIZABLE_PROVIDER_ID = "org.eclipse.cdt.core.tests.custom.serializable.language.settings.provider"; + /*package*/ static final String EXTENSION_SERIALIZABLE_PROVIDER_NAME = "Test Plugin Mock Serializable Language Settings Provider"; + /*package*/ static final String EXTENSION_SERIALIZABLE_PROVIDER_MISSING_PARAMETER = "parameter"; + /*package*/ static final String EXTENSION_EDITABLE_PROVIDER_ID = "org.eclipse.cdt.core.tests.custom.editable.language.settings.provider"; + /*package*/ static final String EXTENSION_EDITABLE_PROVIDER_NAME = "Test Plugin Mock Editable Language Settings Provider"; + /*package*/ static final ICLanguageSettingEntry EXTENSION_SERIALIZABLE_PROVIDER_ENTRY = new CMacroEntry("MACRO", "value", 0); + /*package*/ static final ICLanguageSettingEntry EXTENSION_EDITABLE_PROVIDER_ENTRY = new CMacroEntry("MACRO", "value", 0); + /*package*/ static final String EXTENSION_REGISTERER_PROVIDER_ID = "org.eclipse.cdt.core.tests.language.settings.listener.registerer.provider"; + /*package*/ static final String EXTENSION_USER_PROVIDER_ID = "org.eclipse.cdt.ui.UserLanguageSettingsProvider"; + + // Arbitrary sample parameters used by the test + private static final String PROVIDER_0 = "test.provider.0.id"; + private static final String PROVIDER_NAME_0 = "test.provider.0.name"; + private static final String LANG_ID = "test.lang.id"; + private static final IFile FILE_0 = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path("/project/path0")); + + // Constants used in LanguageSettingsSerializableProvider + private static final String ATTR_ID = LanguageSettingsProvidersSerializer.ATTR_ID; + private static final String ATTR_NAME = LanguageSettingsProvidersSerializer.ATTR_NAME; + private static final String ATTR_CLASS = LanguageSettingsProvidersSerializer.ATTR_CLASS; + + /** + * Constructor. + * @param name - name of the test. + */ + public LanguageSettingsExtensionsTests(String name) { + super(name); + + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * @return - new TestSuite. + */ + public static TestSuite suite() { + return new TestSuite(LanguageSettingsExtensionsTests.class); + } + + /** + * main function of the class. + * + * @param args - arguments + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + /** + * Check that regular {@link ICLanguageSettingsProvider} extension defined in plugin.xml is accessible. + */ + public void testExtension() throws Exception { + { + // test provider defined as an extension + List providers = LanguageSettingsManager.getWorkspaceProviders(); + List ids = new ArrayList(); + for (ILanguageSettingsProvider provider : providers) { + ids.add(provider.getId()); + } + assertTrue("extension " + EXTENSION_BASE_PROVIDER_ID + " not found", ids.contains(EXTENSION_BASE_PROVIDER_ID)); + } + + { + // test provider that is not in the list + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getExtensionProviderCopy("missing.povider", true); + assertNull(providerExt); + } + + // this extension provider is not copyable + ILanguageSettingsProvider providerExtCopy = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_BASE_PROVIDER_ID, true); + assertNull(providerExtCopy); + + // test raw workspace provider defined as an extension + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + assertTrue(LanguageSettingsManager.isWorkspaceProvider(providerExt)); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(providerExt); + assertTrue(rawProvider instanceof LanguageSettingsBaseProvider); + LanguageSettingsBaseProvider provider = (LanguageSettingsBaseProvider)rawProvider; + assertEquals(EXTENSION_BASE_PROVIDER_ID, provider.getId()); + assertEquals(EXTENSION_BASE_PROVIDER_NAME, provider.getName()); + assertEquals(EXTENSION_BASE_PROVIDER_PARAMETER, provider.getProperty(EXTENSION_BASE_PROVIDER_ATTR_PARAMETER)); + // these attributes are not exposed as properties + assertEquals("", provider.getProperty(ATTR_ID)); + assertEquals("", provider.getProperty(ATTR_NAME)); + assertEquals("", provider.getProperty(ATTR_CLASS)); + + // attempt to get entries for wrong language + assertNull(provider.getSettingEntries(null, FILE_0, LANG_ID)); + + // benchmarks matching extension point definition + List entriesExt = new ArrayList(); + entriesExt.add(new CIncludePathEntry("/usr/include/", + ICSettingEntry.BUILTIN + | ICSettingEntry.LOCAL + | ICSettingEntry.RESOLVED + | ICSettingEntry.VALUE_WORKSPACE_PATH + | ICSettingEntry.UNDEFINED + )); + entriesExt.add(new CMacroEntry("TEST_DEFINE", "100", 0)); + entriesExt.add(new CIncludeFileEntry("/include/file.inc", 0)); + entriesExt.add(new CLibraryPathEntry("/usr/lib/", 0)); + entriesExt.add(new CLibraryFileEntry("libdomain.a", 0)); + entriesExt.add(new CMacroFileEntry("/macro/file.mac", 0)); + + // retrieve entries from extension point + List actual = provider.getSettingEntries(null, FILE_0, EXTENSION_BASE_PROVIDER_LANG_ID); + for (int i=0;i actual = provider.getSettingEntries(null, FILE_0, LANG_ID); + for (int i=0;i languages = new ArrayList(2); + languages.add("bogus.language.id"); + languages.add(LANG_ID); + + // create base provider + LanguageSettingsBaseProvider provider = new LanguageSettingsBaseProvider(PROVIDER_0, PROVIDER_NAME_0, languages, entries); + + { + // attempt to get entries for wrong language + List actual = provider.getSettingEntries(null, FILE_0, "wrong.lang.id"); + assertNull(actual); + } + + { + // retrieve the entries + List actual = provider.getSettingEntries(null, FILE_0, LANG_ID); + assertEquals(entries.get(0), actual.get(0)); + assertNotSame(entries, actual); + // retrieve languages + List actualLanguageIds = provider.getLanguageScope(); + for (String languageId: languages) { + assertTrue(actualLanguageIds.contains(languageId)); + } + assertEquals(languages.size(), actualLanguageIds.size()); + } + } + + /** + * Test ability to configure {@link LanguageSettingsBaseProvider}. + */ + public void testBaseProviderConfigure() throws Exception { + // sample entries + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("/usr/include/", 0)); + List languages = new ArrayList(); + languages.add(LANG_ID); + // create LanguageSettingsBaseProvider + LanguageSettingsBaseProvider provider1 = new LanguageSettingsBaseProvider(); + LanguageSettingsBaseProvider provider2 = new LanguageSettingsBaseProvider(); + { + // configure provider1 + Map properties = new HashMap(); + properties.put("key1", "value1"); + properties.put("key2", null); + properties.put("key3", ""); + properties.put("key4", "false"); + provider1.configureProvider(PROVIDER_0, PROVIDER_NAME_0, languages, entries, properties); + assertEquals(PROVIDER_0, provider1.getId()); + assertEquals(PROVIDER_NAME_0, provider1.getName()); + assertEquals(languages, provider1.getLanguageScope()); + assertEquals(entries, provider1.getSettingEntries(null, null, LANG_ID)); + assertEquals("value1", provider1.getProperty("key1")); + assertEquals("", provider1.getProperty("key2")); + assertEquals("", provider1.getProperty("key3")); + assertEquals("false", provider1.getProperty("key4")); + assertEquals(false, provider1.getPropertyBool("key4")); + assertEquals("", provider1.getProperty("keyX")); + assertEquals(false, provider1.getPropertyBool("keyX")); + } + { + // configure provider2 + Map properties = new HashMap(); + properties.put("key1", "value1"); + provider2.configureProvider(PROVIDER_0, PROVIDER_NAME_0, languages, entries, properties); + assertEquals(PROVIDER_0, provider2.getId()); + assertEquals(PROVIDER_NAME_0, provider2.getName()); + assertEquals(languages, provider2.getLanguageScope()); + assertEquals(entries, provider2.getSettingEntries(null, null, LANG_ID)); + assertEquals("value1", provider2.getProperty("key1")); + assertEquals("", provider2.getProperty("keyX")); + assertEquals(false, provider2.getPropertyBool("keyX")); + } + // test equality + assertTrue(provider1.equals(provider2)); + } + + /** + * {@link LanguageSettingsBaseProvider} is not allowed to be configured twice. + */ + public void testBaseProviderCantReconfigure() throws Exception { + // create LanguageSettingsBaseProvider + LanguageSettingsBaseProvider provider = new LanguageSettingsBaseProvider(); + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("/usr/include/", 0)); + // configure it + provider.configureProvider("test.id", "test.name", null, entries, null); + + try { + // attempt to configure it twice should fail + provider.configureProvider("test.id", "test.name", null, entries, null); + fail("LanguageSettingsBaseProvider is not allowed to be configured twice"); + } catch (UnsupportedOperationException e) { + } + } + + /** + * Test {@link LanguageSettingsSerializableProvider} defined via extension point. + */ + public void testSerializableProvider() throws Exception { + // get test plugin extension for serializable provider + ILanguageSettingsProvider providerExtCopy = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_SERIALIZABLE_PROVIDER_ID, true); + assertNull(providerExtCopy); + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + assertTrue(LanguageSettingsManager.isWorkspaceProvider(providerExt)); + + // get raw extension provider + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(providerExt); + assertTrue(rawProvider instanceof LanguageSettingsSerializableProvider); + LanguageSettingsSerializableProvider provider = (LanguageSettingsSerializableProvider) rawProvider; + + assertEquals(null, provider.getLanguageScope()); + assertEquals("", provider.getProperty(EXTENSION_SERIALIZABLE_PROVIDER_MISSING_PARAMETER)); + + List expected = new ArrayList(); + expected.add(EXTENSION_EDITABLE_PROVIDER_ENTRY); + assertEquals(expected, provider.getSettingEntries(null, null, null)); + } + + /** + * Test {@link ILanguageSettingsEditableProvider} defined via extension point. + */ + public void testEditableProvider() throws Exception { + // Non-editable providers cannot be copied so they are singletons + { + // get test plugin extension for serializable provider + ILanguageSettingsProvider providerExtCopy = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_SERIALIZABLE_PROVIDER_ID, true); + assertNull(providerExtCopy); + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + assertTrue(LanguageSettingsManager.isWorkspaceProvider(providerExt)); + + // get raw extension provider + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(providerExt); + assertTrue(rawProvider instanceof LanguageSettingsSerializableProvider); + assertTrue(LanguageSettingsManager.isEqualExtensionProvider(rawProvider, true)); + assertEquals(true, LanguageSettingsExtensionManager.isPreferShared(EXTENSION_SERIALIZABLE_PROVIDER_ID)); + } + + // Editable providers are retrieved by copy + { + // get extension provider + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_EDITABLE_PROVIDER_ID, true); + assertFalse(LanguageSettingsManager.isWorkspaceProvider(providerExt)); + assertTrue(providerExt instanceof ILanguageSettingsEditableProvider); + assertTrue(LanguageSettingsManager.isEqualExtensionProvider(providerExt, true)); + assertEquals(LanguageSettingsExtensionManager.isPreferShared(EXTENSION_EDITABLE_PROVIDER_ID), false); + + // test that different copies are not same + ILanguageSettingsProvider providerExt2 = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_EDITABLE_PROVIDER_ID, true); + assertNotSame(providerExt, providerExt2); + assertEquals(providerExt, providerExt2); + + // test that workspace provider is not the same as extension provider + ILanguageSettingsProvider providerWsp = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_EDITABLE_PROVIDER_ID); + ILanguageSettingsProvider providerWspRaw = LanguageSettingsManager.getRawProvider(providerWsp); + assertNotSame(providerExt, providerWspRaw); + assertEquals(providerExt, providerWspRaw); + assertTrue(LanguageSettingsManager.isEqualExtensionProvider(providerWspRaw, true)); + } + + // Test shallow copy + { + ILanguageSettingsProvider provider = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_EDITABLE_PROVIDER_ID, true); + assertNotNull(provider); + assertTrue(provider instanceof ILanguageSettingsEditableProvider); + + ILanguageSettingsProvider providerShallow = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_EDITABLE_PROVIDER_ID, false); + assertNotNull(providerShallow); + assertTrue(providerShallow instanceof ILanguageSettingsEditableProvider); + assertFalse(provider.equals(providerShallow)); + + assertFalse(LanguageSettingsManager.isEqualExtensionProvider(providerShallow, true)); + assertTrue(LanguageSettingsManager.isEqualExtensionProvider(providerShallow, false)); + } + } + +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsManagerTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsManagerTests.java new file mode 100644 index 00000000000..de2482c87ba --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsManagerTests.java @@ -0,0 +1,608 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.AbstractExecutableExtensionBase; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.testplugin.CModelMock; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; + +/** + * Test cases testing {@link LanguageSettingsManager} utility methods. + */ +public class LanguageSettingsManagerTests extends BaseTestCase { + // Those should match ids of plugin extensions defined in plugin.xml + private static final String EXTENSION_SERIALIZABLE_PROVIDER_ID = LanguageSettingsExtensionsTests.EXTENSION_SERIALIZABLE_PROVIDER_ID; + private static final String EXTENSION_SERIALIZABLE_PROVIDER_NAME = LanguageSettingsExtensionsTests.EXTENSION_SERIALIZABLE_PROVIDER_NAME; + private static final String EXTENSION_EDITABLE_PROVIDER_ID = LanguageSettingsExtensionsTests.EXTENSION_EDITABLE_PROVIDER_ID; + + // Arbitrary sample parameters used by the test case + private static final String PROVIDER_0 = "test.provider.0.id"; + private static final String PROVIDER_1 = "test.provider.1.id"; + private static final String PROVIDER_2 = "test.provider.2.id"; + private static final String PROVIDER_NAME_0 = "test.provider.0.name"; + private static final String PROVIDER_NAME_1 = "test.provider.1.name"; + private static final String PROVIDER_NAME_2 = "test.provider.2.name"; + private static final String CFG_ID = "test.configuration.id"; + private static final String LANG_ID = "test.lang.id"; + private static final IFile FILE_0 = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path("/project/path0")); + + /** + * Mock configuration description. + */ + class MockConfigurationDescription extends CModelMock.DummyCConfigurationDescription implements ILanguageSettingsProvidersKeeper { + List providers = new ArrayList(); + String[] defaultProvidersIds = null; + public MockConfigurationDescription(String id) { + super(id); + } + @Override + public void setLanguageSettingProviders(List providers) { + this.providers = new ArrayList(providers); + } + @Override + public List getLanguageSettingProviders() { + return providers; + } + @Override + public void setDefaultLanguageSettingsProvidersIds(String[] ids) { + defaultProvidersIds = ids; + } + @Override + public String[] getDefaultLanguageSettingsProvidersIds() { + return defaultProvidersIds; + } + } + + /** + * Mock language sttings provider. + */ + private class MockProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsProvider { + private List entries; + public MockProvider(String id, String name, List entries) { + super(id, name); + this.entries = entries; + } + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + return entries; + } + } + + /** + * Constructor. + * @param name - name of the test. + */ + public LanguageSettingsManagerTests(String name) { + super(name); + + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + LanguageSettingsManager.setWorkspaceProviders(null); + super.tearDown(); // includes ResourceHelper cleanup + } + + /** + * @return - new TestSuite. + */ + public static TestSuite suite() { + return new TestSuite(LanguageSettingsManagerTests.class); + } + + /** + * main function of the class. + * + * @param args - arguments + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + /** + * Test ILanguageSettingsProvidersKeeper API (getters and setters). + */ + public void testConfigurationDescription_Providers() throws Exception { + // mock configuration description + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + + // set providers + ILanguageSettingsProvider provider1 = new MockProvider(PROVIDER_1, PROVIDER_NAME_1, null); + ILanguageSettingsProvider provider2 = new MockProvider(PROVIDER_2, PROVIDER_NAME_2, null); + List providers = new ArrayList(); + providers.add(provider1); + providers.add(provider2); + cfgDescription.setLanguageSettingProviders(providers); + + // get providers + List actual = cfgDescription.getLanguageSettingProviders(); + assertEquals(provider1, actual.get(0)); + assertEquals(provider2, actual.get(1)); + assertEquals(providers.size(), actual.size()); + assertNotSame(actual, providers); + + // set default providers + String[] defaultProviders = { PROVIDER_0, PROVIDER_1 }; + cfgDescription.setDefaultLanguageSettingsProvidersIds(defaultProviders); + + // get default providers + assertEquals(defaultProviders, cfgDescription.getDefaultLanguageSettingsProvidersIds()); + } + + /** + * Test various cases of ill-defined providers. + */ + public void testRudeProviders() throws Exception { + // mock configuration description + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + // set impolite provider returning null by getSettingEntries() + ILanguageSettingsProvider providerNull = new MockProvider(PROVIDER_1, PROVIDER_NAME_1, null); + { + List providers = new ArrayList(); + providers.add(providerNull); + cfgDescription.setLanguageSettingProviders(providers); + } + + // use provider returning null, no exception should be recorded + { + List actual = LanguageSettingsManager + .getSettingEntriesUpResourceTree(providerNull, cfgDescription, FILE_0, LANG_ID); + assertNotNull(actual); + assertEquals(0, actual.size()); + } + { + List actual = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, 0); + assertNotNull(actual); + assertEquals(0, actual.size()); + } + + // set impolite provider returning null in getSettingEntries() array + ILanguageSettingsProvider providerNull_2 = new MockProvider(PROVIDER_2, PROVIDER_NAME_2, + new ArrayList() { + { // init via static initializer + add(null); + } + }); + { + List providers = new ArrayList(); + providers.add(providerNull); + cfgDescription.setLanguageSettingProviders(providers); + } + + // use provider returning null as item in array + { + List actual = LanguageSettingsManager + .getSettingEntriesUpResourceTree(providerNull_2, cfgDescription, FILE_0, LANG_ID); + assertNotNull(actual); + assertEquals(1, actual.size()); + } + { + List actual = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, 0); + assertNotNull(actual); + assertEquals(0, actual.size()); + } + + // use careless provider causing an exception + { + setExpectedNumberOfLoggedNonOKStatusObjects(1); + + ILanguageSettingsProvider providerNPE = new MockProvider(PROVIDER_1, PROVIDER_NAME_1, null) { + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + throw new NullPointerException("Can you handle me?"); + } + }; + try { + List actual = LanguageSettingsManager + .getSettingEntriesUpResourceTree(providerNPE, null, null, LANG_ID); + assertNotNull(actual); + assertEquals(0, actual.size()); + } catch (Throwable e) { + fail("Exceptions are expected to be swallowed (after logging) but got " + e); + } + } + } + + /** + * Test assigning and retrieving providers from a configuration. + */ + public void testProvider_Basic() throws Exception { + final MockConfigurationDescription modelCfgDescription = new MockConfigurationDescription(CFG_ID); + + final List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + List providers = new ArrayList(); + // define provider returning entries when configuration id matches and null otherwise + ILanguageSettingsProvider providerYes = new MockProvider(PROVIDER_0, PROVIDER_NAME_0, null) { + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + if (cfgDescription.getId().equals(modelCfgDescription.getId())) { + return entries; + } + return null; + } + + }; + providers.add(providerYes); + // define provider returning entries when configuration id does NOT match and null otherwise + ILanguageSettingsProvider providerNo = new MockProvider(PROVIDER_1, PROVIDER_NAME_1, null) { + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + if (cfgDescription!= null && !cfgDescription.getId().equals(modelCfgDescription.getId())) { + return entries; + } + return null; + } + + }; + providers.add(providerNo); + modelCfgDescription.setLanguageSettingProviders(providers); + + { + // retrieve the entries with provider returning the given list + List actual = LanguageSettingsManager + .getSettingEntriesUpResourceTree(providerYes, modelCfgDescription, FILE_0, LANG_ID); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + + { + // retrieve the entries with provider returning empty list + List actual = LanguageSettingsManager + .getSettingEntriesUpResourceTree(providerNo, modelCfgDescription, FILE_0, LANG_ID); + assertEquals(0, actual.size()); + } + } + + /** + * Test regular functionality with a few providers. + */ + public void testProvider_Regular() throws Exception { + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + + // create couple of providers + List entries1 = new ArrayList(); + entries1.add(new CIncludePathEntry("value1", 1)); + entries1.add(new CIncludePathEntry("value2", 2)); + + List entries2 = new ArrayList(); + entries2.add(new CIncludePathEntry("value1", 1)); + entries2.add(new CIncludePathEntry("value2", 2)); + entries2.add(new CIncludePathEntry("value3", 2)); + + ILanguageSettingsProvider provider1 = new MockProvider(PROVIDER_1, PROVIDER_NAME_1, entries1); + ILanguageSettingsProvider provider2 = new MockProvider(PROVIDER_2, PROVIDER_NAME_2, entries2); + List providers = new ArrayList(); + providers.add(provider1); + providers.add(provider2); + cfgDescription.setLanguageSettingProviders(providers); + + { + // retrieve the entries for provider-1 + List actual = LanguageSettingsManager + .getSettingEntriesUpResourceTree(provider1, cfgDescription, FILE_0, LANG_ID); + assertNotSame(entries1, actual); + + ICLanguageSettingEntry[] entriesArray = entries1.toArray(new ICLanguageSettingEntry[0]); + ICLanguageSettingEntry[] actualArray = actual.toArray(new ICLanguageSettingEntry[0]); + for (int i=0;i providers = new ArrayList(); + providers.add(provider0); + cfgDescription.setLanguageSettingProviders(providers); + + // retrieve entries by kind + List includes = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + assertEquals(new CIncludePathEntry("path0", 0),includes.get(0)); + assertEquals(new CIncludePathEntry("path1", 0),includes.get(1)); + assertEquals(new CIncludePathEntry("path2", 0),includes.get(2)); + assertEquals(3, includes.size()); + + List macros = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.MACRO); + assertEquals(new CMacroEntry("MACRO0", "value0",0), macros.get(0)); + assertEquals(new CMacroEntry("MACRO1", "value1",0), macros.get(1)); + assertEquals(2, macros.size()); + } + + /** + * Test how conflicting entries are resolved. + */ + public void testEntriesByKind_ConflictingEntries() throws Exception { + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + + // contribute the entries + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path", ICSettingEntry.BUILTIN)); + entries.add(new CIncludePathEntry("path", ICSettingEntry.UNDEFINED)); + entries.add(new CIncludePathEntry("path", 0)); + + ILanguageSettingsProvider provider0 = new MockProvider(PROVIDER_0, PROVIDER_NAME_0, entries); + List providers = new ArrayList(); + providers.add(provider0); + cfgDescription.setLanguageSettingProviders(providers); + + // retrieve entries by kind, only first entry should be returned + List includes = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + assertEquals(1, includes.size()); + assertEquals(entries.get(0),includes.get(0)); + } + + /** + * Check handling of {@link ICSettingEntry#UNDEFINED} flag. + */ + public void testEntriesByKind_Undefined() throws Exception { + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + + // contribute the entries + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path", ICSettingEntry.UNDEFINED)); + entries.add(new CIncludePathEntry("path", 0)); + + ILanguageSettingsProvider provider0 = new MockProvider(PROVIDER_0, PROVIDER_NAME_0, entries); + List providers = new ArrayList(); + providers.add(provider0); + cfgDescription.setLanguageSettingProviders(providers); + + // retrieve entries by kind, no entries should be returned + List includes = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + assertEquals(0, includes.size()); + } + + /** + * Check handling of local vs. system entries, see {@link ICSettingEntry#LOCAL} flag. + */ + public void testEntriesByKind_LocalAndSystem() throws Exception { + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + + // contribute the entries + List entries = new ArrayList(); + CIncludePathEntry localIncludeEntry = new CIncludePathEntry("path-local", ICSettingEntry.LOCAL); + CIncludePathEntry systemIncludeEntry = new CIncludePathEntry("path-system", 0); + entries.add(localIncludeEntry); + entries.add(systemIncludeEntry); + + ILanguageSettingsProvider provider0 = new MockProvider(PROVIDER_0, PROVIDER_NAME_0, entries); + List providers = new ArrayList(); + providers.add(provider0); + cfgDescription.setLanguageSettingProviders(providers); + + { + // retrieve local entries + List includes = LanguageSettingsProvidersSerializer + .getLocalSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + assertEquals(localIncludeEntry, includes.get(0)); + assertEquals(1, includes.size()); + } + + { + // retrieve system entries + List includes = LanguageSettingsProvidersSerializer + .getSystemSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + assertEquals(systemIncludeEntry, includes.get(0)); + assertEquals(1, includes.size()); + } + + { + // retrieve both local and system + List includes = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + assertEquals(entries.get(0), includes.get(0)); + assertEquals(entries.get(1), includes.get(1)); + assertEquals(2, includes.size()); + } + } + + /** + * Test conflicting entries contributed by different providers. + */ + public void testEntriesByKind_ConflictingProviders() throws Exception { + MockConfigurationDescription cfgDescription = new MockConfigurationDescription(CFG_ID); + + // contribute the entries + List providers = new ArrayList(); + + // contribute the higher ranked entries + List entriesHigh = new ArrayList(); + entriesHigh.add(new CIncludePathEntry("path0", ICSettingEntry.RESOLVED)); + entriesHigh.add(new CIncludePathEntry("path1", 0)); + entriesHigh.add(new CIncludePathEntry("path2", ICSettingEntry.UNDEFINED)); + ILanguageSettingsProvider highRankProvider = new MockProvider(PROVIDER_2, PROVIDER_NAME_2, entriesHigh); + providers.add(highRankProvider); + + // contribute the lower ranked entries + List entriesLow = new ArrayList(); + entriesLow.add(new CIncludePathEntry("path0", ICSettingEntry.BUILTIN)); + entriesLow.add(new CIncludePathEntry("path1", ICSettingEntry.UNDEFINED)); + entriesLow.add(new CIncludePathEntry("path2", 0)); + entriesLow.add(new CIncludePathEntry("path3", 0)); + ILanguageSettingsProvider lowRankProvider = new MockProvider(PROVIDER_1, PROVIDER_NAME_1, entriesLow); + providers.add(lowRankProvider); + + cfgDescription.setLanguageSettingProviders(providers); + + // retrieve entries by kind + List includes = LanguageSettingsProvidersSerializer + .getSettingEntriesByKind(cfgDescription, FILE_0, LANG_ID, ICSettingEntry.INCLUDE_PATH); + // path0 is taken from higher priority provider + assertEquals(entriesHigh.get(0),includes.get(0)); + // path1 disablement by lower priority provider is ignored + assertEquals(entriesHigh.get(1),includes.get(1)); + // path2 is removed because of DISABLED flag of high priority provider + // path3 gets there from low priority provider + assertEquals(entriesLow.get(3),includes.get(2)); + assertEquals(3, includes.size()); + } + + /** + * Test a workspace provider basics. + */ + public void testWorkspaceProvider_Basic() throws Exception { + // get workspace provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, provider.getId()); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_NAME, provider.getName()); + + // get raw provider + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, rawProvider.getId()); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_NAME, rawProvider.getName()); + assertTrue(rawProvider instanceof LanguageSettingsSerializableProvider); + // assert they are not the same object + assertNotSame(provider, rawProvider); + + { + // make sure entries are the same + List entries = provider.getSettingEntries(null, null, null); + assertEquals(1, entries.size()); // defined in the extension + List rawEntries = rawProvider.getSettingEntries(null, null, null); + assertEquals(entries, rawEntries); + } + + { + // set new entries to the raw provider + List newEntries = new ArrayList(); + newEntries.add(new CIncludePathEntry("path0", 0)); + newEntries.add(new CIncludePathEntry("path1", 0)); + ((LanguageSettingsSerializableProvider)rawProvider).setSettingEntries(null, null, null, newEntries); + + // check that the workspace provider gets them too + List newRawEntries = rawProvider.getSettingEntries(null, null, null); + assertEquals(newEntries, newRawEntries); + assertEquals(2, newEntries.size()); + } + } + + /** + * Test workspace providers equality. + */ + public void testWorkspaceProvider_Equals() throws Exception { + ILanguageSettingsProvider providerA = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + ILanguageSettingsProvider providerB = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + assertEquals(providerA, providerB); + } + + /** + * Test ability to replace underlying raw provider. + */ + public void testWorkspaceProvider_ReplaceRawProvider() throws Exception { + // get sample workspace provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_EDITABLE_PROVIDER_ID); + { + // check on its entries (1 predefined entry via extension point) + List entries = provider.getSettingEntries(null, null, null); + assertEquals(1, entries.size()); // defined in the extension + } + + // define new entries for the raw provider + List newEntries = new ArrayList(); + newEntries.add(new CIncludePathEntry("path0", 0)); + newEntries.add(new CIncludePathEntry("path1", 0)); + newEntries.add(new CIncludePathEntry("path2", 0)); + + { + // replace raw provider + List providers = new ArrayList(); + LanguageSettingsSerializableProvider newRawProvider = new LanguageSettingsSerializableProvider(EXTENSION_EDITABLE_PROVIDER_ID, PROVIDER_NAME_0); + newRawProvider.setSettingEntries(null, null, null, newEntries); + providers.add(newRawProvider); + LanguageSettingsManager.setWorkspaceProviders(providers); + } + + { + // check that provider provides the new entries + List entries = provider.getSettingEntries(null, null, null); + assertEquals(newEntries.size(), entries.size()); + assertEquals(newEntries, entries); + } + } + + /** + * Test ability to be called with workspace provider as well (NOOP). + */ + public void testWorkspaceProvider_ReplaceWithWorkspaceProvider() throws Exception { + // get sample workspace provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertNotSame(provider, rawProvider); + + // attempt to "replace" with workspace provider (which is a wrapper around raw provider), should be NOOP + List providers = new ArrayList(); + providers.add(provider); + LanguageSettingsManager.setWorkspaceProviders(providers); + ILanguageSettingsProvider newRawProvider = LanguageSettingsManager.getRawProvider(provider); + assertSame(rawProvider, newRawProvider); + + // check for no side effect + assertSame(provider, providers.get(0)); + } + +} 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 new file mode 100644 index 00000000000..f2439a48162 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsPersistenceProjectTests.java @@ -0,0 +1,1035 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.testplugin.CModelMock; +import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Test cases testing LanguageSettingsProvider functionality related to persistence. + */ +public class LanguageSettingsPersistenceProjectTests extends BaseTestCase { + // These should match extension points defined in plugin.xml + private static final String EXTENSION_BASE_PROVIDER_ID = LanguageSettingsExtensionsTests.EXTENSION_BASE_PROVIDER_ID; + private static final String EXTENSION_BASE_PROVIDER_NAME = LanguageSettingsExtensionsTests.EXTENSION_BASE_PROVIDER_NAME; + private static final String EXTENSION_SERIALIZABLE_PROVIDER_ID = LanguageSettingsExtensionsTests.EXTENSION_SERIALIZABLE_PROVIDER_ID; + private static final String EXTENSION_EDITABLE_PROVIDER_ID = LanguageSettingsExtensionsTests.EXTENSION_EDITABLE_PROVIDER_ID; + private static final ICLanguageSettingEntry EXTENSION_SERIALIZABLE_PROVIDER_ENTRY = LanguageSettingsExtensionsTests.EXTENSION_SERIALIZABLE_PROVIDER_ENTRY; + + // Constants from LanguageSettingsProvidersSerializer + public static final String LANGUAGE_SETTINGS_PROJECT_XML = ".settings/language.settings.xml"; + public static final String LANGUAGE_SETTINGS_WORKSPACE_XML = "language.settings.xml"; + + // Arbitrary sample parameters used by the test + private static final String CFG_ID = "test.configuration.id.0"; + private static final String CFG_ID_2 = "test.configuration.id.2"; + private static final String PROVIDER_0 = "test.provider.0.id"; + private static final String PROVIDER_2 = "test.provider.2.id"; + private static final String PROVIDER_NAME_0 = "test.provider.0.name"; + private static final String PROVIDER_NAME_2 = "test.provider.2.name"; + private static final String ATTR_PARAMETER = "parameter"; + private static final String CUSTOM_PARAMETER = "custom parameter"; + private static final String ELEM_TEST = "test"; + private static final String ELEM_PROVIDER = "provider "; // keep space for more reliable comparison + private static final String ELEM_PROVIDER_REFERENCE = "provider-reference"; + + /** + * Mock configuration description. + */ + class MockConfigurationDescription extends CModelMock.DummyCConfigurationDescription implements ILanguageSettingsProvidersKeeper { + List providers; + public MockConfigurationDescription(String id) { + super(id); + } + @Override + public void setLanguageSettingProviders(List providers) { + this.providers = new ArrayList(providers); + } + @Override + public List getLanguageSettingProviders() { + return providers; + } + @Override + public void setDefaultLanguageSettingsProvidersIds(String[] ids) { + } + @Override + public String[] getDefaultLanguageSettingsProvidersIds() { + return null; + } + } + + /** + * Mock project description. + */ + class MockProjectDescription extends CModelMock.DummyCProjectDescription { + ICConfigurationDescription[] cfgDescriptions; + public MockProjectDescription(ICConfigurationDescription[] cfgDescriptions) { + this.cfgDescriptions = cfgDescriptions; + } + public MockProjectDescription(ICConfigurationDescription cfgDescription) { + this.cfgDescriptions = new ICConfigurationDescription[] { cfgDescription }; + } + @Override + public ICConfigurationDescription[] getConfigurations() { + return cfgDescriptions; + + } + @Override + public ICConfigurationDescription getConfigurationById(String id) { + for (ICConfigurationDescription cfgDescription : cfgDescriptions) { + if (cfgDescription.getId().equals(id)) + return cfgDescription; + } + return null; + } + } + + /** + * Constructor. + * @param name - name of the test. + */ + public LanguageSettingsPersistenceProjectTests(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + LanguageSettingsManager.setWorkspaceProviders(null); + super.tearDown(); // includes ResourceHelper cleanup + } + + /** + * @return - new TestSuite. + */ + public static TestSuite suite() { + return new TestSuite(LanguageSettingsPersistenceProjectTests.class); + } + + /** + * main function of the class. + * + * @param args - arguments + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + /** + * Persist and reload when no customized providers are defined in the workspace. + */ + public void testWorkspacePersistence_NoProviders() throws Exception { + // serialize language settings of user defined providers (on workspace level) + LanguageSettingsProvidersSerializer.serializeLanguageSettingsWorkspace(); + LanguageSettingsProvidersSerializer.loadLanguageSettingsWorkspace(); + + // test passes if no exception was thrown + } + + /** + * Persist and reload a customized provider defined in the workspace. + */ + public void testWorkspacePersistence_ModifiedExtensionProvider() throws Exception { + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + { + // get the raw extension provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + LanguageSettingsSerializableProvider extProvider = (LanguageSettingsSerializableProvider) LanguageSettingsManager.getRawProvider(provider); + assertNotNull(extProvider); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, extProvider.getId()); + + // add entries + extProvider.setSettingEntries(null, null, null, entries); + List actual = extProvider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + + // serialize language settings of workspace providers + LanguageSettingsManager.serializeLanguageSettingsWorkspace(); + + // clear the provider + extProvider.setSettingEntries(null, null, null, null); + } + + { + // doublecheck it's clean + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + List actual = provider.getSettingEntries(null, null, null); + assertNull(actual); + } + { + // re-load and check language settings of the provider + LanguageSettingsProvidersSerializer.loadLanguageSettingsWorkspace(); + + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, provider.getId()); + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + } + + /** + * Check persistence of unmodified extension provider in the workspace. + */ + public void testWorkspacePersistence_UnmodifiedExtensionProvider() throws Exception { + List extensionEntries = new ArrayList(); + extensionEntries.add(EXTENSION_SERIALIZABLE_PROVIDER_ENTRY); + { + // test initial state of the extension provider + ILanguageSettingsProvider extProvider = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_SERIALIZABLE_PROVIDER_ID, true); + assertNull(extProvider); + } + { + // get the workspace provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + // check that entries match that of extension provider + assertEquals(extensionEntries, provider.getSettingEntries(null, null, null)); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertTrue(LanguageSettingsManager.isEqualExtensionProvider(rawProvider, true)); + + // serialize language settings of workspace providers + LanguageSettingsManager.serializeLanguageSettingsWorkspace(); + } + { + // re-load + LanguageSettingsProvidersSerializer.loadLanguageSettingsWorkspace(); + + // ensure the workspace provider still matches extension + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, provider.getId()); + assertEquals(extensionEntries, provider.getSettingEntries(null, null, null)); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertTrue(LanguageSettingsManager.isEqualExtensionProvider(rawProvider, true)); + + // replace entries + assertTrue(rawProvider instanceof LanguageSettingsSerializableProvider); + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + ((LanguageSettingsSerializableProvider)rawProvider).setSettingEntries(null, null, null, entries); + + // check that the extension provider is not affected + assertTrue(!LanguageSettingsManager.isEqualExtensionProvider(rawProvider, true)); + } + } + + /** + * Test persistence of global providers in the workspace. + */ + public void testWorkspacePersistence_GlobalProvider() throws Exception { + { + // get the raw extension provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + LanguageSettingsSerializableProvider rawProvider = (LanguageSettingsSerializableProvider) LanguageSettingsManager.getRawProvider(provider); + assertNotNull(rawProvider); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, rawProvider.getId()); + + // customize provider + rawProvider.setProperty(ATTR_PARAMETER, CUSTOM_PARAMETER); + assertEquals(CUSTOM_PARAMETER, rawProvider.getProperty(ATTR_PARAMETER)); + } + { + // save workspace provider (as opposed to raw provider) + List providers = new ArrayList(); + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + providers.add(provider); + LanguageSettingsManager.setWorkspaceProviders(providers); + } + { + // check that it has not cleared + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + LanguageSettingsSerializableProvider rawProvider = (LanguageSettingsSerializableProvider) LanguageSettingsManager.getRawProvider(provider); + assertEquals(CUSTOM_PARAMETER, rawProvider.getProperty(ATTR_PARAMETER)); + } + } + + /** + * Test persistence of global providers with ID matching an extension provider in the workspace. + */ + public void testWorkspacePersistence_ShadowedExtensionProvider() throws Exception { + { + // get the raw extension provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + // confirm its type and name + assertTrue(rawProvider instanceof LanguageSettingsBaseProvider); + assertEquals(EXTENSION_BASE_PROVIDER_ID, rawProvider.getId()); + assertEquals(EXTENSION_BASE_PROVIDER_NAME, rawProvider.getName()); + } + { + // replace extension provider + ILanguageSettingsProvider provider = new MockLanguageSettingsSerializableProvider(EXTENSION_BASE_PROVIDER_ID, PROVIDER_NAME_0); + List providers = new ArrayList(); + providers.add(provider); + // note that this will also serialize workspace providers + LanguageSettingsManager.setWorkspaceProviders(providers); + } + { + // doublecheck it's in the list + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertTrue(rawProvider instanceof MockLanguageSettingsSerializableProvider); + assertEquals(EXTENSION_BASE_PROVIDER_ID, rawProvider.getId()); + assertEquals(PROVIDER_NAME_0, rawProvider.getName()); + } + + { + // re-load to check serialization + LanguageSettingsProvidersSerializer.loadLanguageSettingsWorkspace(); + + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertTrue(rawProvider instanceof MockLanguageSettingsSerializableProvider); + assertEquals(EXTENSION_BASE_PROVIDER_ID, rawProvider.getId()); + assertEquals(PROVIDER_NAME_0, rawProvider.getName()); + } + + { + // reset workspace providers, that will also serialize + LanguageSettingsManager.setWorkspaceProviders(null); + } + { + // doublecheck original one is in the list + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertTrue(rawProvider instanceof LanguageSettingsBaseProvider); + assertEquals(EXTENSION_BASE_PROVIDER_ID, rawProvider.getId()); + assertEquals(EXTENSION_BASE_PROVIDER_NAME, rawProvider.getName()); + } + { + // re-load to check serialization + LanguageSettingsProvidersSerializer.loadLanguageSettingsWorkspace(); + + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + ILanguageSettingsProvider rawProvider = LanguageSettingsManager.getRawProvider(provider); + assertTrue(rawProvider instanceof LanguageSettingsBaseProvider); + assertEquals(EXTENSION_BASE_PROVIDER_ID, rawProvider.getId()); + assertEquals(EXTENSION_BASE_PROVIDER_NAME, rawProvider.getName()); + } + } + + /** + * Test serialization of providers to project storage. + */ + public void testProjectPersistence_SerializableProviderDOM() throws Exception { + Element rootElement = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + { + // create a provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + LanguageSettingsSerializableProvider serializableProvider = new LanguageSettingsSerializableProvider(PROVIDER_0, PROVIDER_NAME_0); + serializableProvider.setSettingEntries(null, null, null, entries); + LanguageSettingsManager.setStoringEntriesInProjectArea(serializableProvider, true); + + ArrayList providers = new ArrayList(); + providers.add(serializableProvider); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + // prepare DOM storage + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(rootElement, null, mockPrjDescription); + assertTrue(XmlUtil.toString(doc).contains(PROVIDER_0)); + } + { + // re-load and check language settings of the newly loaded provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(rootElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertTrue(provider instanceof LanguageSettingsSerializableProvider); + + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + } + + /** + * Test serialization of providers to project storage where the project has multiple configurations. + */ + public void testProjectPersistence_TwoConfigurationsDOM() throws Exception { + Element rootElement = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + List entries2 = new ArrayList(); + entries2.add(new CIncludePathEntry("path2", 0)); + + { + // create a project description with 2 configuration descriptions + MockProjectDescription mockPrjDescription = new MockProjectDescription( + new MockConfigurationDescription[] { + new MockConfigurationDescription(CFG_ID), + new MockConfigurationDescription(CFG_ID_2), + }); + { + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(2, cfgDescriptions.length); + { + // populate configuration 1 with provider + ICConfigurationDescription cfgDescription1 = cfgDescriptions[0]; + assertNotNull(cfgDescription1); + assertTrue(cfgDescription1 instanceof ILanguageSettingsProvidersKeeper); + + assertEquals(CFG_ID, cfgDescription1.getId()); + LanguageSettingsSerializableProvider provider1 = new LanguageSettingsSerializableProvider(PROVIDER_0, PROVIDER_NAME_0); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider1, true); + provider1.setSettingEntries(null, null, null, entries); + ArrayList providers = new ArrayList(); + providers.add(provider1); + ((ILanguageSettingsProvidersKeeper) cfgDescription1).setLanguageSettingProviders(providers); + } + { + // populate configuration 2 with provider + ICConfigurationDescription cfgDescription2 = cfgDescriptions[1]; + assertNotNull(cfgDescription2); + assertTrue(cfgDescription2 instanceof ILanguageSettingsProvidersKeeper); + + assertEquals(CFG_ID_2, cfgDescription2.getId()); + LanguageSettingsSerializableProvider provider2 = new LanguageSettingsSerializableProvider(PROVIDER_0, PROVIDER_NAME_0); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider2, true); + provider2.setSettingEntries(null, null, null, entries2); + ArrayList providers = new ArrayList(); + providers.add(provider2); + ((ILanguageSettingsProvidersKeeper) cfgDescription2).setLanguageSettingProviders(providers); + } + } + + { + // doublecheck both configuration descriptions + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(2, cfgDescriptions.length); + { + // doublecheck configuration 1 + ICConfigurationDescription cfgDescription1 = cfgDescriptions[0]; + assertNotNull(cfgDescription1); + assertTrue(cfgDescription1 instanceof ILanguageSettingsProvidersKeeper); + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription1).getLanguageSettingProviders(); + + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertNotNull(provider); + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + { + // doublecheck configuration 2 + ICConfigurationDescription cfgDescription2 = cfgDescriptions[1]; + assertNotNull(cfgDescription2); + assertTrue(cfgDescription2 instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription2).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertNotNull(provider); + List actual2 = provider.getSettingEntries(null, null, null); + assertEquals(entries2.get(0), actual2.get(0)); + assertEquals(entries2.size(), actual2.size()); + } + } + + // prepare DOM storage + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(rootElement, null, mockPrjDescription); + } + { + // re-create a project description and re-load language settings for each configuration + MockProjectDescription mockPrjDescription = new MockProjectDescription( + new MockConfigurationDescription[] { + new MockConfigurationDescription(CFG_ID), + new MockConfigurationDescription(CFG_ID_2), + }); + // load + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(rootElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(2, cfgDescriptions.length); + { + // check configuration 1 + ICConfigurationDescription cfgDescription1 = cfgDescriptions[0]; + assertNotNull(cfgDescription1); + assertTrue(cfgDescription1 instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription1).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertNotNull(provider); + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + { + // check configuration 2 + ICConfigurationDescription cfgDescription2 = cfgDescriptions[1]; + assertNotNull(cfgDescription2); + assertTrue(cfgDescription2 instanceof ILanguageSettingsProvidersKeeper); + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription2).getLanguageSettingProviders(); + + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertNotNull(provider); + List actual2 = provider.getSettingEntries(null, null, null); + assertEquals(entries2.get(0), actual2.get(0)); + assertEquals(entries2.size(), actual2.size()); + } + } + } + + /** + * Test serialization of providers subclassing {@link LanguageSettingsSerializableProvider}. + */ + public void testProjectPersistence_SubclassedSerializableProviderDOM() throws Exception { + Element rootElement = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + { + // create a provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + LanguageSettingsSerializableProvider serializableProvider = new MockLanguageSettingsSerializableProvider(PROVIDER_0, PROVIDER_NAME_0); + serializableProvider.setSettingEntries(null, null, null, entries); + LanguageSettingsManager.setStoringEntriesInProjectArea(serializableProvider, true); + + ArrayList providers = new ArrayList(); + providers.add(serializableProvider); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + // prepare DOM storage + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(rootElement, null, mockPrjDescription); + } + { + // re-load and check language settings of the newly loaded provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(rootElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertTrue(provider instanceof MockLanguageSettingsSerializableProvider); + + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + } + + /** + * Serialization of providers exactly equal extension providers. + */ + public void testProjectPersistence_ReferenceExtensionProviderDOM() throws Exception { + Element rootElement = null; + + // provider of other type (not LanguageSettingsSerializableProvider) defined as an extension + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + + { + // create cfg description + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + // populate with provider defined as extension + List providers = new ArrayList(); + providers.add(providerExt); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + // prepare DOM storage + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(rootElement, null, mockPrjDescription); + } + { + // re-load + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(rootElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + // and check the newly loaded provider which should be workspace provider + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertNotNull(provider); + assertTrue(LanguageSettingsManager.isWorkspaceProvider(provider)); + } + } + + /** + * Test serialization of providers overriding/shadowing extension providers. + */ + public void testProjectPersistence_OverrideExtensionProviderDOM() throws Exception { + Element rootElement = null; + + // provider set on workspace level overriding an extension + String idExt = EXTENSION_BASE_PROVIDER_ID; + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getWorkspaceProvider(idExt); + assertNotNull(providerExt); + { + // create cfg description + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + // populate with provider overriding the extension (must be SerializableLanguageSettingsProvider or a class from another extension) + MockLanguageSettingsSerializableProvider providerOverride = new MockLanguageSettingsSerializableProvider(idExt, PROVIDER_NAME_0); + LanguageSettingsManager.setStoringEntriesInProjectArea(providerOverride, true); + List providers = new ArrayList(); + providers.add(providerOverride); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + + // prepare DOM storage + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(rootElement, null, mockPrjDescription); + } + { + // re-load + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(rootElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + // check the newly loaded provider + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertNotNull(provider); + assertTrue(provider instanceof MockLanguageSettingsSerializableProvider); + assertEquals(idExt, provider.getId()); + assertEquals(PROVIDER_NAME_0, provider.getName()); + } + } + + + /** + * Test serialization flavors in one storage. + */ + public void testProjectPersistence_MixedProvidersDOM() throws Exception { + Element rootElement = null; + + List entries_31 = new ArrayList(); + entries_31.add(new CIncludePathEntry("path0", 0)); + + List entries_32 = new ArrayList(); + entries_32.add(new CIncludePathEntry("path2", 0)); + + ILanguageSettingsProvider providerExt; + { + // Define providers a bunch + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + { + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + // 1. Provider reference to extension from plugin.xml + providerExt = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + + // 2. Providers defined in a configuration + // 2.1 + LanguageSettingsSerializableProvider mockProvider1 = new LanguageSettingsSerializableProvider(PROVIDER_0, PROVIDER_NAME_0); + LanguageSettingsManager.setStoringEntriesInProjectArea(mockProvider1, true); + mockProvider1.setSettingEntries(null, null, null, entries_31); + // 2.2 + LanguageSettingsSerializableProvider mockProvider2 = new MockLanguageSettingsSerializableProvider(PROVIDER_2, PROVIDER_NAME_2); + LanguageSettingsManager.setStoringEntriesInProjectArea(mockProvider2, true); + mockProvider2.setSettingEntries(null, null, null, entries_32); + + ArrayList providers = new ArrayList(); + providers.add(providerExt); + providers.add(mockProvider1); + providers.add(mockProvider2); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + } + + // prepare DOM storage + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(rootElement, null, mockPrjDescription); + XmlUtil.toString(doc); + } + { + // re-load and check language settings of the newly loaded provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(rootElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + // 1. Provider reference to extension from plugin.xml + ILanguageSettingsProvider provider0 = providers.get(0); + assertTrue(LanguageSettingsManager.isWorkspaceProvider(provider0)); + + // 2. Providers defined in a configuration + // 2.1 + { + ILanguageSettingsProvider provider1 = providers.get(1); + assertTrue(provider1 instanceof LanguageSettingsSerializableProvider); + List actual = provider1.getSettingEntries(null, null, null); + assertEquals(entries_31.get(0), actual.get(0)); + assertEquals(entries_31.size(), actual.size()); + } + // 2.2 + { + ILanguageSettingsProvider provider2 = providers.get(2); + assertTrue(provider2 instanceof MockLanguageSettingsSerializableProvider); + List actual = provider2.getSettingEntries(null, null, null); + assertEquals(entries_32.get(0), actual.get(0)); + assertEquals(entries_32.size(), actual.size()); + } + assertEquals(3, providers.size()); + } + } + + /** + * Test case when the storage is split between project and workspace area. + */ + public void testProjectPersistence_SplitStorageDOM() throws Exception { + Element prjStorageElement = null; + Element wspStorageElement = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + { + // create a provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + LanguageSettingsSerializableProvider serializableProvider = new LanguageSettingsSerializableProvider(PROVIDER_0, PROVIDER_NAME_0); + serializableProvider.setSettingEntries(null, null, null, entries); + // do not store entries inside project + LanguageSettingsManager.setStoringEntriesInProjectArea(serializableProvider, false); + + ArrayList providers = new ArrayList(); + providers.add(serializableProvider); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + // prepare DOM storage + Document prjDoc = XmlUtil.newDocument(); + prjStorageElement = XmlUtil.appendElement(prjDoc, ELEM_TEST); + Document wspDoc = XmlUtil.newDocument(); + wspStorageElement = XmlUtil.appendElement(wspDoc, ELEM_TEST); + // serialize language settings to the DOM + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(prjStorageElement, wspStorageElement, mockPrjDescription); + } + { + // re-load and check language settings of the newly loaded provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(prjStorageElement, wspStorageElement, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertTrue(provider instanceof LanguageSettingsSerializableProvider); + + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + } + } + + /** + * @param store - name of the store + * @return location of the store in the plug-in state area + */ + public static String getStoreLocationInWorkspaceArea(String store) { + IPath location = CCorePlugin.getDefault().getStateLocation().append(store); + return location.toString(); + } + + /** + * Test serialization of providers referring to global shared instance. + */ + public void testProjectPersistence_ProviderExtensionReferenceDOM() throws Exception { + Document doc = XmlUtil.newDocument(); + Element storageElement = XmlUtil.appendElement(doc, ELEM_TEST); + + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_BASE_PROVIDER_ID); + assertNotNull(providerExt); + + { + // create a provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + + ArrayList providers = new ArrayList(); + providers.add(providerExt); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + // prepare DOM storage + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(storageElement, null, mockPrjDescription); + + String xml = XmlUtil.toString(doc); + assertTrue(xml.contains(ELEM_PROVIDER_REFERENCE)); + assertTrue(xml.contains(EXTENSION_BASE_PROVIDER_ID)); + assertTrue(xml.contains(LanguageSettingsProvidersSerializer.ATTR_ID)); + assertFalse(xml.contains(LanguageSettingsProvidersSerializer.ATTR_CLASS)); + } + { + // re-load and check language settings of the newly loaded provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(storageElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertEquals(providerExt, provider); + } + } + + /** + * Walk the scenario when a provider is cloned to a configuration from extension. + */ + public void testProjectPersistence_ProviderExtensionCopyDOM() throws Exception { + Document doc = XmlUtil.newDocument(); + Element storageElement = XmlUtil.appendElement(doc, ELEM_TEST); + + ILanguageSettingsProvider providerExt = LanguageSettingsManager.getExtensionProviderCopy(EXTENSION_EDITABLE_PROVIDER_ID, true); + assertNotNull(providerExt); + + { + // create a provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + + ArrayList providers = new ArrayList(); + providers.add(providerExt); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + + // prepare DOM storage + LanguageSettingsProvidersSerializer.serializeLanguageSettingsInternal(storageElement, null, mockPrjDescription); + + String xml = XmlUtil.toString(doc); + assertTrue(xml.contains(ELEM_PROVIDER)); + assertTrue(xml.contains(EXTENSION_EDITABLE_PROVIDER_ID)); + assertTrue(xml.contains(LanguageSettingsProvidersSerializer.ATTR_ID)); + assertFalse(xml.contains(LanguageSettingsProvidersSerializer.ATTR_CLASS)); + } + { + // re-load and check language settings of the newly loaded provider + MockProjectDescription mockPrjDescription = new MockProjectDescription(new MockConfigurationDescription(CFG_ID)); + LanguageSettingsProvidersSerializer.loadLanguageSettingsInternal(storageElement, null, mockPrjDescription); + + ICConfigurationDescription[] cfgDescriptions = mockPrjDescription.getConfigurations(); + assertNotNull(cfgDescriptions); + assertEquals(1, cfgDescriptions.length); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + assertNotNull(cfgDescription); + assertTrue(cfgDescription instanceof ILanguageSettingsProvidersKeeper); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + assertNotNull(providers); + assertEquals(1, providers.size()); + ILanguageSettingsProvider provider = providers.get(0); + assertEquals(providerExt, provider); + } + } + + /** + * Test that default settings do not cause the files to appear in the project or file-system. + */ + public void testProjectPersistence_Defaults() throws Exception { + IProject project = ResourceHelper.createCDTProjectWithConfig(this.getName()); + IFile xmlStorageFile = project.getFile(LANGUAGE_SETTINGS_PROJECT_XML); + assertFalse(xmlStorageFile.exists()); + + String xmlPrjWspStorageFileLocation = getStoreLocationInWorkspaceArea(project.getName()+'.'+LANGUAGE_SETTINGS_WORKSPACE_XML); + java.io.File xmlStorageFilePrjWsp = new java.io.File(xmlPrjWspStorageFileLocation); + assertFalse(xmlStorageFilePrjWsp.exists()); + } + + /** + * Test serialization of global providers exactly equal extension in workspace area. + */ + public void testWorkspacePersistence_ProviderExtensionCopy() throws Exception { + List entries = new ArrayList(); + List providers = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // get extension provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_EDITABLE_PROVIDER_ID); + LanguageSettingsSerializableProvider rawProvider = (LanguageSettingsSerializableProvider) LanguageSettingsManager.getRawProvider(provider); + assertNotNull(rawProvider); + assertEquals(EXTENSION_EDITABLE_PROVIDER_ID, rawProvider.getId()); + + // add non-modified provider to the list + providers.add(provider); + } + { + // get another extension provider + ILanguageSettingsProvider provider = LanguageSettingsManager.getWorkspaceProvider(EXTENSION_SERIALIZABLE_PROVIDER_ID); + LanguageSettingsSerializableProvider rawProvider = (LanguageSettingsSerializableProvider) LanguageSettingsManager.getRawProvider(provider); + assertNotNull(rawProvider); + assertEquals(EXTENSION_SERIALIZABLE_PROVIDER_ID, rawProvider.getId()); + // modify it and add it to the list + rawProvider.setSettingEntries(null, null, null, entries); + providers.add(rawProvider); + + // set and serialize language settings of workspace providers + LanguageSettingsManager.setWorkspaceProviders(providers); + LanguageSettingsManager.serializeLanguageSettingsWorkspace(); + } + { + String xmlWspStorageFileLocation = getStoreLocationInWorkspaceArea(LANGUAGE_SETTINGS_WORKSPACE_XML); + String xml = ResourceHelper.getContents(xmlWspStorageFileLocation); + // provider matching extension is not saved (extensions added automatically during loading providers) + assertFalse(xml.contains(EXTENSION_EDITABLE_PROVIDER_ID)); + // provider that differs is saved + assertTrue(xml.contains(EXTENSION_SERIALIZABLE_PROVIDER_ID)); + } + } + + /** + * Test that default settings do not cause the file to appear on the file-system. + */ + public void testWorkspacePersistence_Defaults() throws Exception { + // reset and serialize workspace providers + LanguageSettingsManager.setWorkspaceProviders(null); + LanguageSettingsManager.serializeLanguageSettingsWorkspace(); + + // check that XML file is not created + String xmlWspStorageFileLocation = getStoreLocationInWorkspaceArea(LANGUAGE_SETTINGS_WORKSPACE_XML); + java.io.File xmlStorageFileWsp = new java.io.File(xmlWspStorageFileLocation); + assertFalse(xmlStorageFileWsp.exists()); + } + +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProviderTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProviderTests.java new file mode 100644 index 00000000000..7e6289b8cc1 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProviderTests.java @@ -0,0 +1,1629 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestSuite; + +import org.eclipse.cdt.core.settings.model.CIncludeFileEntry; +import org.eclipse.cdt.core.settings.model.CIncludePathEntry; +import org.eclipse.cdt.core.settings.model.CLibraryFileEntry; +import org.eclipse.cdt.core.settings.model.CLibraryPathEntry; +import org.eclipse.cdt.core.settings.model.CMacroEntry; +import org.eclipse.cdt.core.settings.model.CMacroFileEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.testplugin.CModelMock; +import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Test cases testing serialization of LanguageSettingsProviders. + */ +public class LanguageSettingsSerializableProviderTests extends BaseTestCase { + // Arbitrary sample parameters used by the test + private static final String CFG_ID = "test.configuration.id"; + private static final ICConfigurationDescription MOCK_CFG = new CModelMock.DummyCConfigurationDescription(CFG_ID); + private static final IResource MOCK_RC = ResourcesPlugin.getWorkspace().getRoot(); + private static final String LANG_ID = "test.lang.id"; + private static final String LANG_ID_1 = "test.lang.id.1"; + private static final String LANG_ID_2 = "test.lang.id.2"; + private static final String PROVIDER_NULL = "test.provider.null.id"; + private static final String PROVIDER_1 = "test.provider.1.id"; + private static final String PROVIDER_2 = "test.provider.2.id"; + private static final String PROVIDER_NAME_NULL = "test.provider.null.name"; + private static final String PROVIDER_NAME_1 = "test.provider.1.name"; + private static final String PROVIDER_NAME_2 = "test.provider.2.name"; + private static final String ATTR_PARAMETER = "parameter"; + private static final String VALUE_PARAMETER = "custom.parameter"; + private static final String ELEM_TEST = "test"; + private static final String ATTR_PROPERTY = "custom-property"; + private static final String ATTR_PROPERTY_BOOL = "custom-property-bool"; + private static final String VALUE_PROPERTY = "custom.property"; + + // This value must match that of LanguageSettingsProvidersSerializer.ATTR_STORE_ENTRIES_WITH_PROJECT + private static final String ATTR_STORE_ENTRIES_WITH_PROJECT = "store-entries-with-project"; + + /** + * Constructor. + * @param name - name of the test. + */ + public LanguageSettingsSerializableProviderTests(String name) { + super(name); + + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); // includes ResourceHelper cleanup + } + + /** + * @return - new TestSuite. + */ + public static TestSuite suite() { + return new TestSuite(LanguageSettingsSerializableProviderTests.class); + } + + /** + * main function of the class. + * + * @param args - arguments + */ + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + /** + * Test basic methods, getters and setters. + */ + public void testProvider_SettersGetters() throws Exception { + // benchmark data + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + List languages = new ArrayList(); + languages.add(LANG_ID); + + // create a provider + LanguageSettingsSerializableProvider mockProvider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + // test isEmpty() + assertTrue(mockProvider.isEmpty()); + + // test setters and getters + mockProvider.setId(PROVIDER_2); + assertEquals(PROVIDER_2, mockProvider.getId()); + mockProvider.setName(PROVIDER_NAME_2); + assertEquals(PROVIDER_NAME_2, mockProvider.getName()); + mockProvider.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + assertEquals(VALUE_PARAMETER, mockProvider.getProperty(ATTR_PARAMETER)); + + mockProvider.setLanguageScope(languages); + assertEquals(languages, mockProvider.getLanguageScope()); + mockProvider.setLanguageScope(null); + assertEquals(null, mockProvider.getLanguageScope()); + + mockProvider.setSettingEntries(null, MOCK_RC, LANG_ID, entries); + List actual = mockProvider.getSettingEntries(null, MOCK_RC, LANG_ID); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + assertFalse(mockProvider.isEmpty()); + + // test clear() + mockProvider.clear(); + assertTrue(mockProvider.isEmpty()); + } + + /** + * Test property defining whether to store entries in project or workspace area. + */ + public void testProvider_SetStoringEntriesInProjectArea() throws Exception { + // create a provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + + assertEquals(false, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider, true); + assertEquals(true, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider, false); + assertEquals(false, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + } + + /** + * Check basic serialization. + */ + public void testProvider_RegularDOM() throws Exception { + Element elementProvider; + { + // create customized provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider, true); + provider.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + + List languageScope = new ArrayList(); + languageScope.add(LANG_ID); + provider.setLanguageScope(languageScope); + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 1)); + provider.setSettingEntries(null, null, null, entries); + + // serialize + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + // check XML + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(PROVIDER_1)); + assertTrue(xmlString.contains(PROVIDER_NAME_1)); + assertTrue(xmlString.contains(VALUE_PARAMETER)); + assertTrue(xmlString.contains(LANG_ID)); + assertTrue(xmlString.contains("path0")); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + assertEquals(true, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + assertEquals(VALUE_PARAMETER, provider.getProperty(ATTR_PARAMETER)); + assertNotNull(provider.getLanguageScope()); + assertTrue(provider.getLanguageScope().size()>0); + assertEquals(LANG_ID, provider.getLanguageScope().get(0)); + + List entries = provider.getSettingEntries(null, null, null); + assertNotNull(entries); + assertTrue(entries.size()>0); + assertEquals(new CIncludePathEntry("path0", 1), entries.get(0)); + } + } + + /** + * Test serialization of properties of the provider. + */ + public void testProvider_serializeAttributesDOM() throws Exception { + Element elementProvider; + { + // create customized provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider, true); + provider.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + + List languageScope = new ArrayList(); + languageScope.add(LANG_ID); + provider.setLanguageScope(languageScope); + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 1)); + provider.setSettingEntries(null, null, null, entries); + + // serialize + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serializeAttributes(rootElement); + // check XML + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(PROVIDER_1)); + assertTrue(xmlString.contains(PROVIDER_NAME_1)); + assertTrue(xmlString.contains(VALUE_PARAMETER)); + assertTrue(xmlString.contains(LANG_ID)); + // no entries + assertFalse(xmlString.contains("path0")); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(); + provider.loadAttributes(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + assertEquals(true, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + assertEquals(VALUE_PARAMETER, provider.getProperty(ATTR_PARAMETER)); + assertNotNull(provider.getLanguageScope()); + assertTrue(provider.getLanguageScope().size()>0); + assertEquals(LANG_ID, provider.getLanguageScope().get(0)); + // no entries should be loaded + List entries = provider.getSettingEntries(null, null, null); + assertNull(entries); + } + } + + /** + * Test serialization of entries. + */ + public void testProvider_serializeEntriesDOM() throws Exception { + Element rootElement; + { + // create customized provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider, true); + provider.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + + List languageScope = new ArrayList(); + languageScope.add(LANG_ID); + provider.setLanguageScope(languageScope); + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 1)); + provider.setSettingEntries(null, null, null, entries); + + // serialize + Document doc = XmlUtil.newDocument(); + rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + provider.serializeEntries(rootElement); + // check XML + String xmlString = XmlUtil.toString(doc); + // no attributes + assertFalse(xmlString.contains(PROVIDER_1)); + assertFalse(xmlString.contains(PROVIDER_NAME_1)); + assertFalse(xmlString.contains(VALUE_PARAMETER)); + assertFalse(xmlString.contains(LANG_ID)); + // entries should be present + assertTrue(xmlString.contains("path0")); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_2, PROVIDER_NAME_2); + provider.loadEntries(rootElement); + assertEquals(PROVIDER_2, provider.getId()); + assertEquals(PROVIDER_NAME_2, provider.getName()); + // no attributes should be loaded + assertFalse(PROVIDER_1.equals(provider.getId())); + assertFalse(PROVIDER_NAME_1.equals(provider.getName())); + assertFalse(true==LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + assertFalse(VALUE_PARAMETER.equals(provider.getProperty(ATTR_PARAMETER))); + assertNull(provider.getLanguageScope()); + // entries should be loaded + List entries = provider.getSettingEntries(null, null, null); + assertNotNull(entries); + assertTrue(entries.size()>0); + assertEquals(new CIncludePathEntry("path0", 1), entries.get(0)); + } + } + + /** + * Test serialization of empty provider. + */ + public void testProvider_EmptyDOM() throws Exception { + Element elementProvider; + { + // create null provider + LanguageSettingsSerializableProvider providerNull = new LanguageSettingsSerializableProvider(PROVIDER_NULL, PROVIDER_NAME_NULL); + assertNull(providerNull.getSettingEntries(null, null, null)); + // set and get null entries + providerNull.setSettingEntries(null, null, null, null); + assertNull(providerNull.getSettingEntries(null, null, null)); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = providerNull.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(PROVIDER_NULL)); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_NULL, provider.getId()); + List actual = provider.getSettingEntries(null, null, null); + assertNull(actual); + } + } + + /** + * Test serialization of custom parameter. + */ + public void testCustomParameterDOM() throws Exception { + Element elementProvider; + { + // create provider with custom parameter + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + assertEquals(VALUE_PARAMETER, provider.getProperty(ATTR_PARAMETER)); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(VALUE_PARAMETER)); + } + { + // re-load and check custom parameter of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(VALUE_PARAMETER, provider.getProperty(ATTR_PARAMETER)); + } + } + + /** + * Test serialization to project area storage. + */ + public void testStoreEntriesWithProjectDOM() throws Exception { + Element elementProvider; + { + // create provider storing entries in project area + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + assertEquals(false, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider, true); + assertEquals(true, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(ATTR_STORE_ENTRIES_WITH_PROJECT)); + assertTrue(xmlString.contains(ATTR_STORE_ENTRIES_WITH_PROJECT+"=\"true\"")); + } + { + // re-load and check storing mode of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(true, LanguageSettingsManager.isStoringEntriesInProjectArea(provider)); + } + } + + /** + * Test serialization of language scope. + */ + public void testLanguagesDOM() throws Exception { + List expectedLanguageIds = new ArrayList(); + expectedLanguageIds.add(LANG_ID); + expectedLanguageIds.add(LANG_ID_1); + + Element elementProvider; + { + // create provider with custom language scope + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setLanguageScope(expectedLanguageIds); + List actualIds = provider.getLanguageScope(); + assertEquals(LANG_ID, actualIds.get(0)); + assertEquals(LANG_ID_1, actualIds.get(1)); + assertEquals(2, actualIds.size()); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(LANG_ID)); + assertTrue(xmlString.contains(LANG_ID_1)); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + List actualIds = provider.getLanguageScope(); + assertEquals(expectedLanguageIds.get(0), actualIds.get(0)); + assertEquals(expectedLanguageIds.get(1), actualIds.get(1)); + assertEquals(expectedLanguageIds.size(), actualIds.size()); + } + } + + /** + * Edge cases for language scope. + */ + public void testLanguageScopeDOM() throws Exception { + // benchmark entries + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + + // define the scope + List expectedLanguageIds = new ArrayList(); + expectedLanguageIds.add(LANG_ID); + expectedLanguageIds.add(LANG_ID_1); + + Element elementProvider; + { + // create provider with no scope by default + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + // set entries for the whole language scope (now langId=null) + provider.setSettingEntries(null, null, null, entries); + { + // doublecheck for language scope itself + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries, actual); + } + { + // doublecheck for any language + List actual = provider.getSettingEntries(null, null, LANG_ID_2); + assertEquals(entries, actual); + } + + // set the scope + provider.setLanguageScope(expectedLanguageIds); + List actualIds = provider.getLanguageScope(); + assertEquals(LANG_ID, actualIds.get(0)); + assertEquals(LANG_ID_1, actualIds.get(1)); + assertEquals(2, actualIds.size()); + + { + // check for language scope itself + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries, actual); + } + { + // check for language in the language scope + List actual = provider.getSettingEntries(null, null, LANG_ID); + assertEquals(entries, actual); + } + { + // check for language not in scope + List actual = provider.getSettingEntries(null, null, LANG_ID_2); + assertNull(actual); + } + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(LANG_ID)); + assertTrue(xmlString.contains(LANG_ID_1)); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + List actualIds = provider.getLanguageScope(); + assertEquals(expectedLanguageIds.get(0), actualIds.get(0)); + assertEquals(expectedLanguageIds.get(1), actualIds.get(1)); + assertEquals(expectedLanguageIds.size(), actualIds.size()); + + { + // check for language scope itself + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries, actual); + } + { + // check for language in the language scope + List actual = provider.getSettingEntries(null, null, LANG_ID); + assertEquals(entries, actual); + } + { + // check for language not in scope + List actual = provider.getSettingEntries(null, null, LANG_ID_2); + assertNull(actual); + } + } + } + + /** + * Test serialization of entries when configuration description is null. + */ + public void testNullConfigurationDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, MOCK_RC, LANG_ID, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that "configuration" element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(null, MOCK_RC, LANG_ID); + assertEquals(entries.get(0), actual.get(0)); + } + } + + /** + * Test serialization of entries when language is null. + */ + public void testNullLanguageDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(MOCK_CFG, MOCK_RC, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that "language" element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(MOCK_CFG, MOCK_RC, null); + assertEquals(entries.get(0), actual.get(0)); + } + } + + + /** + * Test serialization of entries when language scope is null. + */ + public void testNullLanguageScopeDOM() throws Exception { + // define benchmark entries + List entriesNullLanguage = new ArrayList(); + entriesNullLanguage.add(new CIncludePathEntry("path_null", 0)); + List entriesLanguage = new ArrayList(); + entriesLanguage.add(new CIncludePathEntry("path", 0)); + + Element elementProvider; + + { + // create a provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + assertEquals(null, provider.getLanguageScope()); + + // add null language + provider.setSettingEntries(null, MOCK_RC, null, entriesNullLanguage); + assertEquals(null, provider.getLanguageScope()); + { + // getter by null language + List actual = provider.getSettingEntries(null, MOCK_RC, null); + assertEquals(entriesNullLanguage.get(0), actual.get(0)); + assertEquals(entriesNullLanguage.size(), actual.size()); + } + { + // getter by any language - should return same entries as null + List actual = provider.getSettingEntries(null, MOCK_RC, LANG_ID); + assertEquals(entriesNullLanguage.get(0), actual.get(0)); + assertEquals(entriesNullLanguage.size(), actual.size()); + } + + // add non-null language + provider.setSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID, entriesLanguage); + assertNull(provider.getLanguageScope()); + { + // getter by null language + List actual = provider.getSettingEntries(null, MOCK_RC, null); + assertEquals(entriesNullLanguage.get(0), actual.get(0)); + assertEquals(entriesNullLanguage.size(), actual.size()); + } + { + // getter by the language + List actual = provider.getSettingEntries(null, MOCK_RC, LANG_ID); + assertEquals(entriesLanguage.get(0), actual.get(0)); + assertEquals(entriesLanguage.size(), actual.size()); + } + + // provider/configuration/language/resource/settingEntry + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that "language" element is saved in XML + String xmlString = XmlUtil.toString(doc); + assertTrue(xmlString.contains(" actual = provider.getSettingEntries(null, MOCK_RC, null); + assertEquals(entriesNullLanguage.get(0), actual.get(0)); + assertEquals(entriesNullLanguage.size(), actual.size()); + } + { + // getter by the language + List actual = provider.getSettingEntries(null, MOCK_RC, LANG_ID); + assertEquals(entriesLanguage.get(0), actual.get(0)); + assertEquals(entriesLanguage.size(), actual.size()); + } + } + } + + /** + * Test serialization of entries when resource is null. + */ + public void testNullResourceDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(MOCK_CFG, null, LANG_ID, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that "resource" element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(MOCK_CFG, null, LANG_ID); + assertEquals(entries.get(0), actual.get(0)); + } + } + + /** + * Test serialization of entries when configuration and language are both null. + */ + public void testNullConfigurationLanguageDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, MOCK_RC, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(null, MOCK_RC, null); + assertEquals(entries.get(0), actual.get(0)); + } + } + + /** + * Test serialization of entries when configuration and resource are both null. + */ + public void testNullConfigurationResourceDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, LANG_ID, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(null, null, LANG_ID); + assertEquals(entries.get(0), actual.get(0)); + } + } + + /** + * Test serialization of entries when language and resource are both null. + */ + public void testNullLanguageResourceDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(MOCK_CFG, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(MOCK_CFG, null, null); + assertEquals(entries.get(0), actual.get(0)); + } + } + + /** + * Test serialization of entries when configuration, language and resource are all null. + */ + public void testNullConfigurationLanguageResourceFlagDOM() throws Exception { + // provider/configuration/language/resource/settingEntry + Element elementProvider; + List entries = new ArrayList(); + int flag = 0; + entries.add(new CIncludePathEntry("path0", flag)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + + // verify that element is collapsed and not saved in XML + String xmlString = XmlUtil.toString(doc); + assertFalse(xmlString.contains(" actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + } + } + + /** + * Serialization of include path. + */ + public void testCIncludePathEntryDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + ICLanguageSettingEntry entry = actual.get(0); + assertTrue(entry instanceof CIncludePathEntry); + + CIncludePathEntry includePathEntry = (CIncludePathEntry)entry; + assertEquals(entries.get(0).getName(), includePathEntry.getName()); + assertEquals(entries.get(0).getValue(), includePathEntry.getValue()); + assertEquals(entries.get(0).getKind(), includePathEntry.getKind()); + assertEquals(entries.get(0).getFlags(), includePathEntry.getFlags()); + assertEquals(entries.get(0), includePathEntry); + } + } + + /** + * Serialization of include file. + */ + public void testCIncludeFileEntryDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludeFileEntry("a-path", 1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + ICLanguageSettingEntry entry = actual.get(0); + assertTrue(entry instanceof CIncludeFileEntry); + CIncludeFileEntry includeFileEntry = (CIncludeFileEntry)entry; + assertEquals(entries.get(0).getName(), includeFileEntry.getName()); + assertEquals(entries.get(0).getValue(), includeFileEntry.getValue()); + assertEquals(entries.get(0).getKind(), includeFileEntry.getKind()); + assertEquals(entries.get(0).getFlags(), includeFileEntry.getFlags()); + assertEquals(entries.get(0), includeFileEntry); + } + } + + /** + * Serialization of macro. + */ + public void testCMacroEntryDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CMacroEntry("MACRO0", "value0",1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + ICLanguageSettingEntry entry = actual.get(0); + assertTrue(entry instanceof CMacroEntry); + CMacroEntry macroEntry = (CMacroEntry)entry; + assertEquals(entries.get(0).getName(), macroEntry.getName()); + assertEquals(entries.get(0).getValue(), macroEntry.getValue()); + assertEquals(entries.get(0).getKind(), macroEntry.getKind()); + assertEquals(entries.get(0).getFlags(), macroEntry.getFlags()); + assertEquals(entries.get(0), macroEntry); + } + } + + /** + * Serialization of macro file. + */ + public void testCMacroFileEntryDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CMacroFileEntry("a-path", 1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + ICLanguageSettingEntry entry = actual.get(0); + assertTrue(entry instanceof CMacroFileEntry); + CMacroFileEntry macroFileEntry = (CMacroFileEntry)entry; + assertEquals(entries.get(0).getName(), macroFileEntry.getName()); + assertEquals(entries.get(0).getValue(), macroFileEntry.getValue()); + assertEquals(entries.get(0).getKind(), macroFileEntry.getKind()); + assertEquals(entries.get(0).getFlags(), macroFileEntry.getFlags()); + assertEquals(entries.get(0), macroFileEntry); + } + } + + /** + * Serialization of library path. + */ + public void testCLibraryPathEntryDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CLibraryPathEntry("a-path", 1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + ICLanguageSettingEntry entry = actual.get(0); + assertTrue(entry instanceof CLibraryPathEntry); + CLibraryPathEntry libraryPathEntry = (CLibraryPathEntry)entry; + assertEquals(entries.get(0).getName(), libraryPathEntry.getName()); + assertEquals(entries.get(0).getValue(), libraryPathEntry.getValue()); + assertEquals(entries.get(0).getKind(), libraryPathEntry.getKind()); + assertEquals(entries.get(0).getFlags(), libraryPathEntry.getFlags()); + assertEquals(entries.get(0), libraryPathEntry); + } + } + + /** + * Serialization of library file. + */ + public void testCLibraryFileEntryDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CLibraryFileEntry("a-path", 1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + ICLanguageSettingEntry entry = actual.get(0); + assertTrue(entry instanceof CLibraryFileEntry); + CLibraryFileEntry libraryFileEntry = (CLibraryFileEntry)entry; + assertEquals(entries.get(0).getName(), libraryFileEntry.getName()); + assertEquals(entries.get(0).getValue(), libraryFileEntry.getValue()); + assertEquals(entries.get(0).getKind(), libraryFileEntry.getKind()); + assertEquals(entries.get(0).getFlags(), libraryFileEntry.getFlags()); + assertEquals(entries.get(0), libraryFileEntry); + } + } + + /** + * Serialization of entries of different types. + */ + public void testMixedSettingEntriesDOM() throws Exception { + Element elementProvider; + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 1)); + entries.add(new CIncludePathEntry("path1", 1)); + entries.add(new CMacroEntry("MACRO0", "value0",1)); + { + // create a provider and serialize its settings + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, entries); + + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = provider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(elementProvider); + assertEquals(PROVIDER_1, provider.getId()); + + List actual = provider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.get(1), actual.get(1)); + assertEquals(entries.get(2), actual.get(2)); + assertEquals(entries.size(), actual.size()); + } + } + + /** + * Serialization of entries for default and specific languages together. + */ + public void testLanguageAndNullDOM() throws Exception { + Element elementProvider = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + List entries2 = new ArrayList(); + entries2.add(new CIncludePathEntry("path2", 0)); + + { + // create a provider + LanguageSettingsSerializableProvider mockProvider = null; + mockProvider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + mockProvider.setSettingEntries(null, null, null, entries); + mockProvider.setSettingEntries(null, null, LANG_ID, entries2); + + // serialize language settings to DOM + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = mockProvider.serialize(rootElement); + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider loadedProvider = new LanguageSettingsSerializableProvider(elementProvider); + + List actual = loadedProvider.getSettingEntries(null, null, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + + List actual2 = loadedProvider.getSettingEntries(null, null, LANG_ID); + assertEquals(entries2.get(0), actual2.get(0)); + assertEquals(entries2.size(), actual2.size()); + } + } + + /** + * Serialization of entries for 2 languages. + */ + public void testTwoLanguagesDOM() throws Exception { + Element elementProvider = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + List entries2 = new ArrayList(); + entries2.add(new CIncludePathEntry("path2", 0)); + + { + // create a provider + LanguageSettingsSerializableProvider mockProvider = null; + mockProvider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + mockProvider.setSettingEntries(null, null, LANG_ID_1, entries); + mockProvider.setSettingEntries(null, null, LANG_ID_2, entries2); + + // serialize language settings to DOM + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = mockProvider.serialize(rootElement); + String xml = XmlUtil.toString(elementProvider.getOwnerDocument()); +// fail(xml); // for debugging + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider loadedProvider = new LanguageSettingsSerializableProvider(elementProvider); + + List actual = loadedProvider.getSettingEntries(null, null, LANG_ID_1); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + + List actual2 = loadedProvider.getSettingEntries(null, null, LANG_ID_2); + assertEquals(entries2.get(0), actual2.get(0)); + assertEquals(entries2.size(), actual2.size()); + } + } + + /** + * Serialization of entries for different resources. + */ + public void testTwoResourcesDOM() throws Exception { + // Create resources + IProject project = ResourceHelper.createCDTProjectWithConfig(this.getName()); + IFile rc1 = ResourceHelper.createFile(project, "rc1"); + assertNotNull(rc1); + IFile rc2 = ResourceHelper.createFile(project, "rc2"); + assertNotNull(rc2); + assertFalse(rc1.getFullPath().equals(rc2.getFullPath())); + + Element elementProvider = null; + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + List entries2 = new ArrayList(); + entries2.add(new CIncludePathEntry("path2", 0)); + + { + // create a provider + LanguageSettingsSerializableProvider mockProvider = null; + mockProvider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + mockProvider.setSettingEntries(null, rc1, null, entries); + mockProvider.setSettingEntries(null, rc2, null, entries2); + + // serialize language settings to DOM + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_TEST); + elementProvider = mockProvider.serialize(rootElement); + String xml = XmlUtil.toString(elementProvider.getOwnerDocument()); +// fail(xml); // for debugging + } + { + // re-load and check language settings of the newly loaded provider + LanguageSettingsSerializableProvider loadedProvider = new LanguageSettingsSerializableProvider(elementProvider); + + List actual = loadedProvider.getSettingEntries(null, rc1, null); + assertEquals(entries.get(0), actual.get(0)); + assertEquals(entries.size(), actual.size()); + + List actual2 = loadedProvider.getSettingEntries(null, rc2, null); + assertEquals(entries2.get(0), actual2.get(0)); + assertEquals(entries2.size(), actual2.size()); + } + } + + /** + * Serialization of entries for resource hierarchy. + */ + public void testParentFolder() throws Exception { + // Create model project and accompanied descriptions + IProject project = ResourceHelper.createCDTProjectWithConfig(this.getName()); + + // Create resources + IFolder parentFolder = ResourceHelper.createFolder(project, "/ParentFolder/"); + assertNotNull(parentFolder); + IFile emptySettingsPath = ResourceHelper.createFile(project, "/ParentFolder/Subfolder/empty"); + assertNotNull(emptySettingsPath); + + // Create provider + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + + // store the entries in parent folder + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path0", 0)); + provider.setSettingEntries(null, parentFolder, LANG_ID, entries); + provider.setSettingEntries(null, emptySettingsPath, LANG_ID, new ArrayList()); + + { + // retrieve entries for a parent folder itself + List actual = provider.getSettingEntries(null, parentFolder, LANG_ID); + assertEquals(entries,actual); + assertEquals(entries.size(), actual.size()); + } + + { + // retrieve entries for a derived resource (in a subfolder) + IFile derived = ResourceHelper.createFile(project, "/ParentFolder/Subfolder/resource"); + List actual = provider.getSettingEntries(null, derived, LANG_ID); + // NOT taken from parent folder + assertEquals(null,actual); + } + + { + // retrieve entries for not related resource + IFile notRelated = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path("/AnotherFolder/Subfolder/resource")); + List actual = provider.getSettingEntries(null, notRelated, LANG_ID); + assertEquals(null,actual); + } + + { + // test distinction between no settings and empty settings + List actual = provider.getSettingEntries(null, emptySettingsPath, LANG_ID); + // NOT taken from parent folder and not null + assertEquals(0, actual.size()); + } + } + + /** + * Test equals() and hashCode(). + */ + public void testEquals() throws Exception { + // create sample entries + List sampleEntries_1 = new ArrayList(); + sampleEntries_1.add(new CMacroEntry("MACRO0", "value0",1)); + sampleEntries_1.add(new CIncludePathEntry("path0", 1)); + sampleEntries_1.add(new CIncludePathEntry("path1", 1)); + + List sampleEntries_2 = new ArrayList(); + sampleEntries_2.add(new CIncludePathEntry("path0", 1)); + + // create sample languages + List sampleLanguages = new ArrayList(); + sampleLanguages.add(LANG_ID); + + // create a model provider + LanguageSettingsSerializableProvider provider1 = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider1.setLanguageScope(sampleLanguages); + provider1.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + assertEquals(false, LanguageSettingsManager.isStoringEntriesInProjectArea(provider1)); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider1, true); + provider1.setSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID, sampleEntries_1); + provider1.setSettingEntries(null, null, LANG_ID, sampleEntries_2); + + // create another provider with the same data + LanguageSettingsSerializableProvider provider2 = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + { + provider2.setLanguageScope(sampleLanguages); + provider2.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider2, true); + provider2.setSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID, sampleEntries_1); + provider2.setSettingEntries(null, null, LANG_ID, sampleEntries_2); + // All set now, so they should be equal + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + } + + { + // start with provider with the same data + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + // replace languages + List sampleLanguages2 = new ArrayList(); + sampleLanguages2.add(LANG_ID_1); + provider2.setLanguageScope(sampleLanguages2); + assertFalse(provider1.hashCode()==provider2.hashCode()); + assertFalse(provider1.equals(provider2)); + // restore provider + provider2.setLanguageScope(sampleLanguages); + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + } + + { + // start with provider with the same data + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + // replace property + provider2.setProperty(ATTR_PARAMETER, "changed-parameter"); + // hash is not calculated for properties + assertFalse(provider1.equals(provider2)); + // restore provider + provider2.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + } + { + // start with provider with the same data + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + // replace property + LanguageSettingsManager.setStoringEntriesInProjectArea(provider2, false); + // hash is not calculated for properties + assertFalse(provider1.equals(provider2)); + // restore provider + LanguageSettingsManager.setStoringEntriesInProjectArea(provider2, true); + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + } + { + // start with provider with the same data + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + // replace entries + List changedEntries = new ArrayList(); + changedEntries.add(new CMacroEntry("MACROX", "valueX",1)); + provider2.setSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID, changedEntries); + assertFalse(provider1.hashCode()==provider2.hashCode()); + assertFalse(provider1.equals(provider2)); + // restore provider + provider2.setSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID, sampleEntries_1); + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + } + { + // start with provider with the same data + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + // replace default entries + List changedEntries = new ArrayList(); + changedEntries.add(new CIncludePathEntry("pathX", 1)); + provider2.setSettingEntries(null, null, LANG_ID, changedEntries); + assertFalse(provider1.hashCode()==provider2.hashCode()); + assertFalse(provider1.equals(provider2)); + // restore provider + provider2.setSettingEntries(null, null, LANG_ID, sampleEntries_2); + assertTrue(provider1.hashCode()==provider2.hashCode()); + assertTrue(provider1.equals(provider2)); + } + + { + // check that subclasses are not equal + LanguageSettingsSerializableProvider providerSub1 = new LanguageSettingsSerializableProvider() {}; + LanguageSettingsSerializableProvider providerSub2 = new LanguageSettingsSerializableProvider() {}; + assertFalse(providerSub1.hashCode()==providerSub2.hashCode()); + assertFalse(providerSub1.equals(providerSub2)); + } + } + + /** + * Test equality for properties. + */ + public void testEquals_DefaultProperties() throws Exception { + // create model providers + LanguageSettingsSerializableProvider provider1 = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + LanguageSettingsSerializableProvider provider2 = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + + // equality for setProperty(String, String) + { + // equality for missing property + assertTrue(provider1.equals(provider2)); + // equality for default empty value (missing in provider2) + provider1.setProperty(ATTR_PROPERTY, ""); + assertTrue(provider1.equals(provider2)); + // just for kicks disturb equality + provider1.setProperty(ATTR_PROPERTY, VALUE_PROPERTY); + assertFalse(provider1.equals(provider2)); + // equality for default null value (missing in provider2) + provider1.setProperty(ATTR_PROPERTY, null); + assertTrue(provider1.equals(provider2)); + } + + // equality for setPropertyBool(String, boolean) + { + // equality for missing property + assertEquals(false, provider1.getPropertyBool(ATTR_PROPERTY_BOOL)); + assertTrue(provider1.equals(provider2)); + // equality for default empty value (missing in provider2) + provider1.setPropertyBool(ATTR_PROPERTY_BOOL, false); + assertEquals(false, provider1.getPropertyBool(ATTR_PROPERTY_BOOL)); + assertTrue(provider1.equals(provider2)); + // just for kicks disturb equality + provider1.setPropertyBool(ATTR_PROPERTY_BOOL, true); + assertEquals(true, provider1.getPropertyBool(ATTR_PROPERTY_BOOL)); + assertFalse(provider1.equals(provider2)); + // equality for true value in both + provider2.setPropertyBool(ATTR_PROPERTY_BOOL, true); + assertEquals(true, provider2.getPropertyBool(ATTR_PROPERTY_BOOL)); + assertTrue(provider1.equals(provider2)); + // switch provider1 back to false + provider1.setPropertyBool(ATTR_PROPERTY_BOOL, false); + provider1.setPropertyBool(ATTR_PROPERTY_BOOL, false); + assertFalse(provider1.equals(provider2)); + } + } + + /** + * Test cloning of provider. + */ + public void testClone() throws Exception { + // define sample data + List sampleEntries_1 = new ArrayList(); + sampleEntries_1.add(new CIncludePathEntry("path0", 1)); + sampleEntries_1.add(new CIncludePathEntry("path1", 1)); + sampleEntries_1.add(new CMacroEntry("MACRO0", "value0",1)); + + List sampleEntries_2 = new ArrayList(); + sampleEntries_2.add(new CIncludePathEntry("path0", 1)); + + List sampleLanguages = new ArrayList(); + sampleLanguages.add(LANG_ID); + + // create a model provider + class MockSerializableProvider extends LanguageSettingsSerializableProvider implements Cloneable { + public MockSerializableProvider(String id, String name) { + super(id, name); + } + @Override + public MockSerializableProvider clone() throws CloneNotSupportedException { + return (MockSerializableProvider) super.clone(); + } + + } + MockSerializableProvider provider1 = new MockSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider1.setLanguageScope(sampleLanguages); + provider1.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + assertEquals(false, LanguageSettingsManager.isStoringEntriesInProjectArea(provider1)); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider1, true); + provider1.setSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID, sampleEntries_1); + provider1.setSettingEntries(null, null, LANG_ID, sampleEntries_2); + + // clone provider + MockSerializableProvider providerClone = provider1.clone(); + assertNotSame(provider1, providerClone); + assertTrue(provider1.equals(providerClone)); + assertTrue(provider1.getClass()==providerClone.getClass()); + + assertEquals(provider1.getProperty(ATTR_PARAMETER), providerClone.getProperty(ATTR_PARAMETER)); + // ensure we did not clone reference + provider1.setProperty(ATTR_PARAMETER, ""); + assertFalse(provider1.getProperty(ATTR_PARAMETER).equals(providerClone.getProperty(ATTR_PARAMETER))); + + assertEquals(LanguageSettingsManager.isStoringEntriesInProjectArea(provider1), LanguageSettingsManager.isStoringEntriesInProjectArea(providerClone)); + // ensure we did not clone reference + LanguageSettingsManager.setStoringEntriesInProjectArea(provider1, !LanguageSettingsManager.isStoringEntriesInProjectArea(providerClone)); + assertFalse(LanguageSettingsManager.isStoringEntriesInProjectArea(provider1) == LanguageSettingsManager.isStoringEntriesInProjectArea(providerClone)); + + assertEquals(provider1.getLanguageScope().get(0), providerClone.getLanguageScope().get(0)); + + List actual1 = providerClone.getSettingEntries(MOCK_CFG, MOCK_RC, LANG_ID); + assertNotSame(sampleEntries_1, actual1); + assertEquals(sampleEntries_1.get(0), actual1.get(0)); + assertEquals(sampleEntries_1.get(1), actual1.get(1)); + assertEquals(sampleEntries_1.get(2), actual1.get(2)); + assertEquals(sampleEntries_1.size(), actual1.size()); + + List actual2 = providerClone.getSettingEntries(null, null, LANG_ID); + assertNotSame(sampleEntries_2, actual2); + assertEquals(sampleEntries_2.get(0), actual2.get(0)); + assertEquals(sampleEntries_2.size(), actual2.size()); + } + + /** + * Test shallow clone. + */ + public void testCloneShallow() throws Exception { + // define sample data + List sampleLanguages = new ArrayList(); + sampleLanguages.add(LANG_ID); + + // create a model provider + class MockSerializableProvider extends LanguageSettingsSerializableProvider implements Cloneable { + public MockSerializableProvider(String id, String name) { + super(id, name); + } + @Override + public MockSerializableProvider cloneShallow() throws CloneNotSupportedException { + return (MockSerializableProvider) super.cloneShallow(); + } + + } + MockSerializableProvider provider1 = new MockSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider1.setLanguageScope(sampleLanguages); + provider1.setProperty(ATTR_PARAMETER, VALUE_PARAMETER); + assertEquals(false, LanguageSettingsManager.isStoringEntriesInProjectArea(provider1)); + LanguageSettingsManager.setStoringEntriesInProjectArea(provider1, true); + + List entries = new ArrayList(); + entries.add(new CIncludePathEntry("path", 1)); + provider1.setSettingEntries(null, null, null, entries); + + // clone provider + MockSerializableProvider providerClone = provider1.cloneShallow(); + assertNotSame(provider1, providerClone); + assertFalse(provider1.equals(providerClone)); + assertTrue(provider1.getClass()==providerClone.getClass()); + assertEquals(provider1.getProperty(ATTR_PARAMETER), providerClone.getProperty(ATTR_PARAMETER)); + assertEquals(LanguageSettingsManager.isStoringEntriesInProjectArea(provider1), LanguageSettingsManager.isStoringEntriesInProjectArea(providerClone)); + assertEquals(provider1.getLanguageScope().get(0), providerClone.getLanguageScope().get(0)); + + List actual = providerClone.getSettingEntries(null, null, null); + assertNull(actual); + } + + /** + * Verify that entries are sorted by kinds. + */ + public void testSort_Kinds() throws Exception { + // create sample entries + CIncludePathEntry includePathEntry1 = new CIncludePathEntry("path1", 0); + CIncludePathEntry includePathEntry2 = new CIncludePathEntry("path2", 0); + CMacroEntry macroEntry1 = new CMacroEntry("MACRO1", null, 0); + CMacroEntry macroEntry2 = new CMacroEntry("MACRO2", null, 0); + CIncludeFileEntry includeFileEntry1 = new CIncludeFileEntry("file1", 0); + CIncludeFileEntry includeFileEntry2 = new CIncludeFileEntry("file2", 0); + CMacroFileEntry macroFileEntry1 = new CMacroFileEntry("file1", 0); + CMacroFileEntry macroFileEntry2 = new CMacroFileEntry("file2", 0); + CLibraryPathEntry libraryPathEntry1 = new CLibraryPathEntry("lib1", 0); + CLibraryPathEntry libraryPathEntry2 = new CLibraryPathEntry("lib2", 0); + CLibraryFileEntry libraryFileEntry1 = new CLibraryFileEntry("file1", 0); + CLibraryFileEntry libraryFileEntry2 = new CLibraryFileEntry("file2", 0); + + // place entries in unsorted list + List unsortedEntries = new ArrayList(); + unsortedEntries.add(macroEntry1); + unsortedEntries.add(macroFileEntry1); + unsortedEntries.add(macroEntry2); + unsortedEntries.add(includePathEntry1); + unsortedEntries.add(includeFileEntry1); + unsortedEntries.add(macroFileEntry2); + unsortedEntries.add(libraryFileEntry1); + unsortedEntries.add(includeFileEntry2); + unsortedEntries.add(libraryFileEntry2); + unsortedEntries.add(libraryPathEntry1); + unsortedEntries.add(includePathEntry2); + unsortedEntries.add(libraryPathEntry2); + + // create a provider and set the entries + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, unsortedEntries); + + // retrieve and check that language settings got sorted properly + int i=0; + List actual = provider.getSettingEntries(null, null, null); + assertEquals(includePathEntry1, actual.get(i++)); + assertEquals(includePathEntry2, actual.get(i++)); + assertEquals(includeFileEntry1, actual.get(i++)); + assertEquals(includeFileEntry2, actual.get(i++)); + assertEquals(macroEntry1, actual.get(i++)); + assertEquals(macroEntry2, actual.get(i++)); + assertEquals(macroFileEntry1, actual.get(i++)); + assertEquals(macroFileEntry2, actual.get(i++)); + assertEquals(libraryPathEntry1, actual.get(i++)); + assertEquals(libraryPathEntry2, actual.get(i++)); + assertEquals(libraryFileEntry1, actual.get(i++)); + assertEquals(libraryFileEntry2, actual.get(i++)); + + assertEquals(unsortedEntries.size(), actual.size()); + } + + /** + * Check how entries are sorted inside a kind. + */ + public void testSort_Entries() throws Exception { + // create sample entries + CIncludePathEntry includePathEntry1 = new CIncludePathEntry("path_B", 0); + CIncludePathEntry includePathEntry2 = new CIncludePathEntry("path_A", 0); + CMacroEntry macroEntry1 = new CMacroEntry("MACRO_A", null, 0); + CMacroEntry macroEntry2 = new CMacroEntry("MACRO_B", null, 0); + CIncludeFileEntry includeFileEntry1 = new CIncludeFileEntry("file_B", 0); + CIncludeFileEntry includeFileEntry2 = new CIncludeFileEntry("file_A", 0); + CMacroFileEntry macroFileEntry1 = new CMacroFileEntry("file_B", 0); + CMacroFileEntry macroFileEntry2 = new CMacroFileEntry("file_A", 0); + CLibraryPathEntry libraryPathEntry1 = new CLibraryPathEntry("lib_B", 0); + CLibraryPathEntry libraryPathEntry2 = new CLibraryPathEntry("lib_A", 0); + CLibraryFileEntry libraryFileEntry1 = new CLibraryFileEntry("file_B", 0); + CLibraryFileEntry libraryFileEntry2 = new CLibraryFileEntry("file_A", 0); + + // place entries in unsorted list + List unsortedEntries = new ArrayList(); + // macros will be sorted by name + unsortedEntries.add(macroEntry2); + unsortedEntries.add(macroEntry1); + // paths are not sorted only grouped by kind + unsortedEntries.add(macroFileEntry1); + unsortedEntries.add(macroFileEntry2); + unsortedEntries.add(includePathEntry1); + unsortedEntries.add(includePathEntry2); + unsortedEntries.add(includeFileEntry1); + unsortedEntries.add(includeFileEntry2); + unsortedEntries.add(libraryFileEntry1); + unsortedEntries.add(libraryFileEntry2); + unsortedEntries.add(libraryPathEntry1); + unsortedEntries.add(libraryPathEntry2); + + // create a provider and set the entries + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, unsortedEntries); + + // retrieve and check that language settings got sorted properly + int i=0; + List actual = provider.getSettingEntries(null, null, null); + assertEquals(includePathEntry1, actual.get(i++)); + assertEquals(includePathEntry2, actual.get(i++)); + assertEquals(includeFileEntry1, actual.get(i++)); + assertEquals(includeFileEntry2, actual.get(i++)); + assertEquals(macroEntry1, actual.get(i++)); + assertEquals(macroEntry2, actual.get(i++)); + assertEquals(macroFileEntry1, actual.get(i++)); + assertEquals(macroFileEntry2, actual.get(i++)); + assertEquals(libraryPathEntry1, actual.get(i++)); + assertEquals(libraryPathEntry2, actual.get(i++)); + assertEquals(libraryFileEntry1, actual.get(i++)); + assertEquals(libraryFileEntry2, actual.get(i++)); + + assertEquals(unsortedEntries.size(), actual.size()); + } + + /** + * Sorting including undefined entries. + */ + public void testSort_Undef() throws Exception { + // create sample entries + CMacroEntry macroEntry1 = new CMacroEntry("MACRO_1", null, 0); + CMacroEntry macroEntry2A = new CMacroEntry("MACRO_2", null, ICSettingEntry.UNDEFINED); + CMacroEntry macroEntry2B = new CMacroEntry("MACRO_2", null, 0); + CMacroEntry macroEntry2C = new CMacroEntry("MACRO_2", null, ICSettingEntry.BUILTIN); + CMacroEntry macroEntry3 = new CMacroEntry("MACRO_3", null, 0); + + // place entries in unsorted list + List unsortedEntries = new ArrayList(); + // macros will be sorted by name and keep order for the same name + unsortedEntries.add(macroEntry2A); + unsortedEntries.add(macroEntry3); + unsortedEntries.add(macroEntry2B); + unsortedEntries.add(macroEntry1); + unsortedEntries.add(macroEntry2C); + + // create a provider and set the entries + LanguageSettingsSerializableProvider provider = new LanguageSettingsSerializableProvider(PROVIDER_1, PROVIDER_NAME_1); + provider.setSettingEntries(null, null, null, unsortedEntries); + + // retrieve and check that language settings got sorted properly + int i=0; + List actual = provider.getSettingEntries(null, null, null); + assertEquals(macroEntry1, actual.get(i++)); + assertEquals(macroEntry2A, actual.get(i++)); + assertEquals(macroEntry2B, actual.get(i++)); + assertEquals(macroEntry2C, actual.get(i++)); + assertEquals(macroEntry3, actual.get(i++)); + + assertEquals(unsortedEntries.size(), actual.size()); + } + +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsBaseProvider.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsBaseProvider.java new file mode 100644 index 00000000000..2f818f2e0b6 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsBaseProvider.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +/** + * Mock of {@link LanguageSettingsBaseProvider} for testing. + */ +public class MockLanguageSettingsBaseProvider extends LanguageSettingsBaseProvider { + private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ + + /** + * @return the custom parameter defined in the extension in {@code plugin.xml}. + */ + public String getCustomParameter() { + return getProperty(ATTR_PARAMETER); + } + +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsEditableProvider.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsEditableProvider.java new file mode 100644 index 00000000000..ccf4103932b --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsEditableProvider.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + + +/** + * Mock of {@link ILanguageSettingsEditableProvider} for testing. + */ +public class MockLanguageSettingsEditableProvider extends LanguageSettingsSerializableProvider implements ILanguageSettingsEditableProvider { + public MockLanguageSettingsEditableProvider() { + super(); + } + + public MockLanguageSettingsEditableProvider(String id, String name) { + super(id, name); + } + + @Override + public MockLanguageSettingsEditableProvider cloneShallow() throws CloneNotSupportedException { + return (MockLanguageSettingsEditableProvider) super.cloneShallow(); + } + + @Override + public MockLanguageSettingsEditableProvider clone() throws CloneNotSupportedException { + return (MockLanguageSettingsEditableProvider) super.clone(); + } +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsProvider.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsProvider.java new file mode 100644 index 00000000000..ca1dd40b390 --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsProvider.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.List; + +import org.eclipse.cdt.core.AbstractExecutableExtensionBase; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.core.resources.IResource; + +/** + * Mock of {@link ILanguageSettingsProvider} for testing. + */ +public class MockLanguageSettingsProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsProvider { + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + return null; + } +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsSerializableProvider.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsSerializableProvider.java new file mode 100644 index 00000000000..3c344176a8e --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockLanguageSettingsSerializableProvider.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +/** + * Mock of {@link LanguageSettingsSerializableProvider} for testing. + */ +public class MockLanguageSettingsSerializableProvider extends LanguageSettingsSerializableProvider { + public MockLanguageSettingsSerializableProvider() { + super(); + } + + public MockLanguageSettingsSerializableProvider(String id, String name) { + super(id, name); + } +} diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockListenerRegisterer.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockListenerRegisterer.java new file mode 100644 index 00000000000..c39835629ec --- /dev/null +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/language/settings/providers/MockListenerRegisterer.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.core.runtime.Assert; + +/** + * Mock Language Settings Provider that keeps count how many times it has been registered. + */ +public class MockListenerRegisterer extends LanguageSettingsSerializableProvider implements ILanguageSettingsEditableProvider, ICListenerAgent { + private static MockListenerManager mockListenerManager = new MockListenerManager(); + + private static class MockListenerManager { + private class ListenerCount { + private MockListenerRegisterer listener; + private int count; + public ListenerCount(MockListenerRegisterer l, int cnt) { + listener = l; + count = cnt; + } + } + private List register = new ArrayList(); + + public void registerListener(MockListenerRegisterer listener) { + for (ListenerCount lc : register) { + if (lc.listener == listener) { + lc.count++; + return; + } + } + + register.add(new ListenerCount(listener, 1)); + } + + public void unregisterListener(MockListenerRegisterer listener) { + for (ListenerCount lc : register) { + if (lc.listener == listener) { + lc.count--; + Assert.isTrue(lc.count>=0); + return; + } + } + + // attempt to unregister non-registered listener + Assert.isTrue(false); + } + + /** + * Note that that count includes all listeners with that id. + */ + public int getCount(String id) { + int count = 0; + + for (ListenerCount lc : register) { + if (lc.listener.getId().equals(id)) { + count = count + lc.count; + } + } + + return count; + } + } + + public MockListenerRegisterer() { + super(); + } + public MockListenerRegisterer(String id, String name) { + super(id, name); + } + @Override + public void registerListener(ICConfigurationDescription cfgDescription) { + mockListenerManager.registerListener(this); + } + + @Override + public void unregisterListener() { + mockListenerManager.unregisterListener(this); + } + @Override + public MockListenerRegisterer cloneShallow() throws CloneNotSupportedException { + return (MockListenerRegisterer) super.cloneShallow(); + } + @Override + public MockListenerRegisterer clone() throws CloneNotSupportedException { + return (MockListenerRegisterer) super.clone(); + } + + public static int getCount(String id) { + return mockListenerManager.getCount(id); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/model/tests/AllCoreTests.java b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/model/tests/AllCoreTests.java index b6738d6a07d..f9f98edac7a 100644 --- a/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/model/tests/AllCoreTests.java +++ b/core/org.eclipse.cdt.core.tests/model/org/eclipse/cdt/core/model/tests/AllCoreTests.java @@ -14,6 +14,7 @@ package org.eclipse.cdt.core.model.tests; import junit.framework.Test; import junit.framework.TestSuite; +import org.eclipse.cdt.core.language.settings.providers.AllLanguageSettingsProvidersCoreTests; import org.eclipse.cdt.core.settings.model.AllCProjectDescriptionTests; import org.eclipse.cdt.core.settings.model.PathSettingsContainerTests; @@ -60,6 +61,8 @@ public class AllCoreTests { suite.addTest(AsmModelBuilderTest.suite()); suite.addTest(CModelBuilderBugsTest.suite()); suite.addTest(Bug311189.suite()); + + suite.addTest(AllLanguageSettingsProvidersCoreTests.suite()); return suite; } diff --git a/core/org.eclipse.cdt.core.tests/plugin.xml b/core/org.eclipse.cdt.core.tests/plugin.xml index 4e9b99437e5..8565b1a88c6 100644 --- a/core/org.eclipse.cdt.core.tests/plugin.xml +++ b/core/org.eclipse.cdt.core.tests/plugin.xml @@ -183,6 +183,168 @@ scheme="EFSExtensionProviderTestsScheme"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + getReferenceInfo() { + return null; + } + + @Override + public void setReferenceInfo(Map refs) throws WriteAccessException { + } + + @Override + public ICExternalSetting[] getExternalSettings() { + return null; + } + + @Override + public ICExternalSetting createExternalSetting(String[] languageIDs, + String[] contentTypeIds, String[] extensions, + ICSettingEntry[] entries) throws WriteAccessException { + return null; + } + + @Override + public void removeExternalSetting(ICExternalSetting setting) throws WriteAccessException { + } + + @Override + public void removeExternalSettings() throws WriteAccessException { + } + + @Override + public ICBuildSetting getBuildSetting() { + return null; + } + + @Override + public ICdtVariablesContributor getBuildVariablesContributor() { + return null; + } + + @Override + public Object getSessionProperty(QualifiedName name) { + return null; + } + + @Override + public void setSessionProperty(QualifiedName name, Object value) { + } + + @Override + public void setName(String name) throws WriteAccessException { + } + + @Override + public ICConfigExtensionReference[] get(String extensionPointID) { + return null; + } + + @Override + public ICConfigExtensionReference create(String extensionPoint, String extension) throws CoreException { + return null; + } + + @Override + public void remove(ICConfigExtensionReference ext) throws CoreException { + } + + @Override + public void remove(String extensionPoint) throws CoreException { + } + + @Override + public boolean isPreferenceConfiguration() { + return false; + } + + @Override + public ICLanguageSetting getLanguageSettingForFile(IPath path, boolean ignoreExludeStatus) { + return null; + } + + @Override + public void setExternalSettingsProviderIds(String[] ids) { + } + + @Override + public String[] getExternalSettingsProviderIds() { + return null; + } + + @Override + public void updateExternalSettingsProviders(String[] ids) throws WriteAccessException { + } + + @Override + public CConfigurationStatus getConfigurationStatus() { + return null; + } + + } +} diff --git a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/ResourceHelper.java b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/ResourceHelper.java index 3804d529014..4b4e7361183 100644 --- a/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/ResourceHelper.java +++ b/core/org.eclipse.cdt.core.tests/suite/org/eclipse/cdt/core/testplugin/ResourceHelper.java @@ -15,11 +15,14 @@ package org.eclipse.cdt.core.testplugin; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.Charset; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -66,7 +69,7 @@ import org.eclipse.core.runtime.jobs.Job; public class ResourceHelper { private final static IProgressMonitor NULL_MONITOR = new NullProgressMonitor(); private static final int MAX_RETRY= 5; - + private final static Set externalFilesCreated = new HashSet(); private final static Set resourcesCreated = new HashSet(); @@ -190,7 +193,7 @@ public class ResourceHelper { /** * Create a plain Eclipse project. - * + * * @param projectName * @return the project handle * @throws CoreException if project could not be created @@ -202,17 +205,17 @@ public class ResourceHelper { project.create(NULL_MONITOR); else project.refreshLocal(IResource.DEPTH_INFINITE, null); - + if (!project.isOpen()) project.open(NULL_MONITOR); - + resourcesCreated.add(project); return project; } - + /** * Delete project by name. - * + * * @param projectName * @throws CoreException */ @@ -222,20 +225,20 @@ public class ResourceHelper { if (project.exists()) delete(project); } - + /** * Delete given project with content. - * + * * @param project * @throws CoreException */ public static void delete(final IProject project) throws CoreException { delete(project, true); } - + /** * Delete project. - * + * * @param project * @param deleteContent whether to delete project content * @throws CoreException @@ -252,7 +255,7 @@ public class ResourceHelper { try { Thread.sleep(1000); // sleep a second } catch (InterruptedException e) { - } + } } } } @@ -297,7 +300,7 @@ public class ResourceHelper { * can include relative path as a part of the name but the the path * has to be present on disk. * The intention of the method is to create files which do not belong to any project. - * + * * @param name - filename. * @return full path of the created file. * @@ -375,7 +378,7 @@ public class ResourceHelper { public static IPath createTemporaryFolder() throws CoreException, IOException { return ResourceHelper.createWorkspaceFolder("tmp/"+System.currentTimeMillis()+'.'+UUID.randomUUID()); } - + /** * Creates new eclipse file-link from project root to file system file. The filename * can include relative path as a part of the name but the the path @@ -514,7 +517,7 @@ public class ResourceHelper { * Checks if symbolic links are supported on the system. * Used in particular by method {@link #createSymbolicLink(IPath, IPath)} * and other flavors to create symbolic links. - * + * * Note that Windows links .lnk are not supported here. * @return {@code true} if symbolic links are suppoted, {@code false} otherwise. */ @@ -675,6 +678,42 @@ public class ResourceHelper { return windowsPath.trim(); } + /** + * Get contents of file on file-system. + * + * @param fullPath - full path to the file on the file-system. + * @return contents of the file. + * @throws IOException on IO problem. + */ + public static String getContents(IPath fullPath) throws IOException { + FileInputStream stream = new FileInputStream(fullPath.toFile()); + try { + // Avoid using java.nio.channels.FileChannel, + // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154 + Reader reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset())); + StringBuilder builder = new StringBuilder(); + char[] buffer = new char[8192]; + int read; + while ((read = reader.read(buffer, 0, buffer.length)) > 0) { + builder.append(buffer, 0, read); + } + return builder.toString(); + } finally { + stream.close(); + } + } + + /** + * Get contents of file on file-system. + * + * @param fullPath - full path to the file on the file-system. + * @return contents of the file. + * @throws IOException on IO problem. + */ + public static String getContents(String fullPath) throws IOException { + return getContents(new Path(fullPath)); + } + /** * Clean-up any files created as part of a unit test. * This method removes *all* Workspace IResources and any external diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index 69e4f77e5dc..c81a487398c 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -27,6 +27,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.core.index.export, org.eclipse.cdt.core.index.provider, org.eclipse.cdt.core.language, + org.eclipse.cdt.core.language.settings.providers, org.eclipse.cdt.core.model, org.eclipse.cdt.core.model.util, org.eclipse.cdt.core.parser, @@ -66,6 +67,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.internal.core.index.provider;x-internal:=true, org.eclipse.cdt.internal.core.indexer;x-internal:=true, org.eclipse.cdt.internal.core.language;x-friends:="org.eclipse.cdt.ui", + org.eclipse.cdt.internal.core.language.settings.providers;x-internal:=true, org.eclipse.cdt.internal.core.model;x-friends:="org.eclipse.cdt.ui,org.eclipse.cdt.debug.core,org.eclipse.cdt.debug.ui", org.eclipse.cdt.internal.core.model.ext;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.parser;x-internal:=true, diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ICListenerAgent.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ICListenerAgent.java new file mode 100644 index 00000000000..ca5368fbf5b --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ICListenerAgent.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; + + +/** + * Helper class to allow listeners of arbitrary events self-register/dispose. + * + * Called by CDT core when {@linkplain ICListenerAgent} added/removed to + * the list of {@link ILanguageSettingsProvider}s managed by the model. + * {@linkplain ICListenerAgent} would commonly be implemented by a language + * settings provider. + *

+ * Implementers are to create a specific listener and register it to + * appropriate event manager in {@link #registerListener(ICConfigurationDescription)} + * then unregister and dispose in {@link #unregisterListener()}. + * + * @since 5.4 + */ +public interface ICListenerAgent { + /** + * Registers a specific listener. + * + * @param cfgDescription - configuration description for the listener. + */ + public void registerListener(ICConfigurationDescription cfgDescription); + + /** + * Unregister listener and dispose all resources. + */ + public void unregisterListener(); +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java new file mode 100644 index 00000000000..143be533f12 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsBroadcastingProvider.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.List; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.core.resources.IResource; + +/** + * This interface is to be implemented by providers which want to broadcast the changes in their setting entries + * with {@link ILanguageSettingsChangeEvent}. + * + * @since 5.4 + */ +public interface ILanguageSettingsBroadcastingProvider extends ILanguageSettingsProvider { + @Override + public String getId(); + @Override + public String getName(); + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId); + + /** + * Return a copy of internal storage. This should be a deep copy/clone of the storage. + * It is used to calculate the delta and being kept in the last state object of configuration + * description to compare to a new state later. + * + * @return a copy of internal storage. + */ + public LanguageSettingsStorage copyStorage(); +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsChangeEvent.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsChangeEvent.java new file mode 100644 index 00000000000..8bcb1046571 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsChangeEvent.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; + +/** + * Contains the details of changes that occurred as a result of modifying + * language settings entries {@link ICLanguageSettingEntry}. This event is + * intended to be fired for changes in entries, not necessarily providers. + * The event is associated with a project. + * + *

+ * EXPERIMENTAL. This class interface is not stable yet as + * it is not currently clear how it may need to be used in future. Only bare + * minimum is provided here at this point (CDT 9.0, Juno). + * There is no guarantee that this API will work or that it will remain the same. + * Please do not use this API without consulting with the CDT team. + *

+ * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * + * @since 5.4 + */ +public interface ILanguageSettingsChangeEvent { + /** + * @return project name where the event occurred. + */ + public String getProjectName(); + + /** + * @return configuration IDs which are affected by the language settings changes. + */ + public String[] getConfigurationDescriptionIds(); + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsChangeListener.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsChangeListener.java new file mode 100644 index 00000000000..4f5d5697efe --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsChangeListener.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; + +/** + * An interface for listeners to changes in language settings {@link ICLanguageSettingEntry}. + * + * @see LanguageSettingsManager#registerLanguageSettingsChangeListener(ILanguageSettingsChangeListener) + * @see LanguageSettingsManager#unregisterLanguageSettingsChangeListener(ILanguageSettingsChangeListener) + * + * @since 5.4 + */ +public interface ILanguageSettingsChangeListener { + /** + * Indicates that language settings have been changed. + * + * @param event - details of the event. + */ + public void handleEvent(ILanguageSettingsChangeEvent event); +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java new file mode 100644 index 00000000000..fe282cb6360 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsEditableProvider.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.List; + +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.core.resources.IResource; + +/** + * This interface is used in UI to identify classes allowing user to modify settings externally + * contrary to some subclasses of {@link LanguageSettingsSerializableProvider} managing + * their settings themselves and not providing such option to the user. + * + * @since 5.4 + * + */ +public interface ILanguageSettingsEditableProvider extends ILanguageSettingsBroadcastingProvider, Cloneable { + @Override + public String getId(); + @Override + public String getName(); + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId); + + /** + * Sets language settings entries for the provider. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. If {@code null} the entries are + * considered to be being defined as default entries for resources. + * @param languageId - language id. If {@code null}, then entries are considered + * to be defined as default entries for languages. + * @param entries - language settings entries to set. + */ + public void setSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId, List entries); + + /** + * Shallow clone of the provider. "Shallow" is defined here as the exact copy except that + * the copy will have zero language settings entries. + * + * @return shallow copy of the provider. + * @throws CloneNotSupportedException in case {@link #clone()} throws the exception. + */ + public ILanguageSettingsEditableProvider cloneShallow() throws CloneNotSupportedException; + + /* + * @see Object#clone() + */ + public ILanguageSettingsEditableProvider clone() throws CloneNotSupportedException; +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsProvider.java new file mode 100644 index 00000000000..f60b313bd6e --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsProvider.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.List; + +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.ICSettingEntry; +import org.eclipse.core.resources.IResource; + +/** + * Base interface to provide list of {@link ICLanguageSettingEntry}. + * This interface is used to deliver additions to compiler options such as + * include paths (-I) or preprocessor defines (-D) and others (see + * {@link ICSettingEntry#INCLUDE_PATH} and other kinds). + *

+ * To define a provider like that use extension point + * {@code org.eclipse.cdt.core.LanguageSettingsProvider} and implement this + * interface. The recommended way of implementing is to extend + * {@link LanguageSettingsSerializableProvider} and implement {@link ILanguageSettingsEditableProvider}. + * That will give the ability to persist and edit/clean entries by user in UI. + * The clone methods defined by {@link ILanguageSettingsEditableProvider} should be + * chained as done for example by {@link LanguageSettingsGenericProvider}. + *

+ * CDT provides a few general use implementations in the core such as {@link LanguageSettingsBaseProvider} + * or {@link LanguageSettingsSerializableProvider} or {@link LanguageSettingsGenericProvider} + * which could be used out of the box or built upon. There are also abstract classes in build + * plugins {@code AbstractBuildCommandParser} and {@code AbstractBuiltinSpecsDetector} which + * serve as a base for output parsers and built-in compiler language settings detectors. + * See also extension point schema description LanguageSettingsProvider.exsd. + * + * @since 5.4 + */ +public interface ILanguageSettingsProvider { + /** + * Id is used to keep track of the providers internally. Use unique id + * to represent the provider. + * + * @return Id of the provider. + */ + public String getId(); + + /** + * Name is used to present the provider to the end user in UI. + * + * @return name of the provider. + */ + public String getName(); + + /** + * Returns the list of setting entries for the given configuration description, + * resource and language. + *

+ * Note to implementers - this method should not be used to do any long running + * operations such as extensive calculations or reading files. If you need to do + * so, the recommended way is to do the calculations outside of + * this function call - in advance and on appropriate event. For example, Build + * Output Parser prepares the list and stores it in internal cache while parsing output. + * {@link #getSettingEntries(ICConfigurationDescription, IResource, String)} will + * return cached entries when asked. You can also implement {@link ICListenerAgent} + * interface to get registered and listen to arbitrary events. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * If {@code null}, the default entries for all resources are returned. + * @param languageId - language id. + * If {@code null}, the default entries for all languages are returned. + * (see {@link LanguageManager#getLanguageForFile(org.eclipse.core.resources.IFile, ICConfigurationDescription)}). + * + * @return the list of setting entries or {@code null} if no settings defined. + * The list needs to be a pooled list created by {@link LanguageSettingsStorage#getPooledList(List)} + * to save memory and avoid deep equality comparisons. + */ + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId); +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsProvidersKeeper.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsProvidersKeeper.java new file mode 100644 index 00000000000..d159e785a4c --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ILanguageSettingsProvidersKeeper.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.List; + +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +/** + * Interface to express ability (of a configuration description) to handle Language Settings + * Providers. + * @see ILanguageSettingsProvider + * + * @since 5.4 + * + */ +public interface ILanguageSettingsProvidersKeeper { + /** + * Sets the list of language settings providers. Language settings providers are + * used to supply language settings {@link ICLanguageSettingEntry} such as include paths + * or preprocessor macros. + * + * @param providers - the list of providers to assign to the owner (configuration description). + * This method clones the internal list or otherwise ensures immutability of the internal + * list before actual addition to the project model. That is to ensure that there is no + * back-door access and all changes in the list done by this method which fires notifications + * to the registered listeners about the accompanied changes in settings entries, see + * {@link LanguageSettingsManager#registerLanguageSettingsChangeListener(ILanguageSettingsChangeListener)}. + */ + public void setLanguageSettingProviders(List providers); + + /** + * Returns the list of language settings providers. Language settings providers are + * used to supply language settings {@link ICLanguageSettingEntry} such as include paths + * or preprocessor macros. + * + * @return the list of providers to assign to the owner (configuration description). This + * returns immutable list. Use {@link #setLanguageSettingProviders(List)} to change. + * This method does not return {@code null}. + */ + public List getLanguageSettingProviders(); + + /** + * Sets the list of IDs of default language settings providers. + *

+ * The method is intended to be used by MBS to set the list from tool-chain definition. + * The default list from the tool-chain is used, for example, while resetting + * configuration providers to default in UI. + * + * @param ids - default provider IDs specified in the tool-chain. + */ + public void setDefaultLanguageSettingsProvidersIds(String[] ids); + + /** + * Retrieve the list of IDs of default language settings providers. + * Normally the list would come from the tool-chain definition. + * + * @return default provider IDs or {@code null} if default providers are not defined. + */ + public String[] getDefaultLanguageSettingsProvidersIds(); + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java new file mode 100644 index 00000000000..b66cafff9ff --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsBaseProvider.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.core.AbstractExecutableExtensionBase; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; +import org.eclipse.core.resources.IResource; + +/** + * {@code LanguageSettingsBaseProvider} is a basic implementation of {@link ILanguageSettingsProvider} + * for the extensions defined by {@code org.eclipse.cdt.core.LanguageSettingsProvider} extension point. + * + * This implementation supports "static" list of entries for languages specified in + * the extension point. + * + * @since 5.4 + */ +public class LanguageSettingsBaseProvider extends AbstractExecutableExtensionBase implements ILanguageSettingsProvider { + /** Language scope, i.e. list of languages the entries will be provided for. */ + protected List languageScope = null; + + /** Provider-specific properties */ + protected Map properties = new HashMap(); + + /** List of entries defined by this provider. */ + private List entries = null; + + /** + * Default constructor. + */ + public LanguageSettingsBaseProvider() { + super(); + } + + /** + * Constructor. Creates an "empty" non-configured provider. + * + * @param id - id of the provider. + * @param name - name of the provider to be presented to a user. + */ + public LanguageSettingsBaseProvider(String id, String name) { + super(id, name); + } + + /** + * Constructor. + * + * @param id - id of the provider. + * @param name - name of the provider to be presented to a user. + * @param languages - list of languages the {@code entries} provided for. + * {@code languages} can be {@code null}, in this case the {@code entries} + * are provided for any language. + * @param entries - the list of language settings entries this provider provides. + * If {@code null} is passed, the provider creates an empty list. + */ + public LanguageSettingsBaseProvider(String id, String name, List languages, + List entries) { + super(id, name); + this.languageScope = languages!=null ? new ArrayList(languages) : null; + this.entries = getPooledList(entries); + } + + /** + * Constructor. + * + * @param id - id of the provider. + * @param name - name of the provider to be presented to a user. + * @param languages - list of languages the {@code entries} provided for. + * {@code languages} can be {@code null}, in this case the {@code entries} + * are provided for any language. + * @param entries - the list of language settings entries this provider provides. + * If {@code null} is passed, the provider creates an empty list. + * @param properties - custom properties as the means to customize providers. + */ + public LanguageSettingsBaseProvider(String id, String name, List languages, + List entries, Map properties) { + super(id, name); + this.languageScope = languages!=null ? new ArrayList(languages) : null; + this.entries = getPooledList(entries); + if (properties != null) + this.properties = new HashMap(properties); + } + + /** + * A method to configure the provider. The initialization of provider from + * the extension point is done in 2 steps. First, the class is created as + * an executable extension using the default provider. Then this method is + * used to configure the provider. + *

+ * It is not allowed to reconfigure the provider. + * + * @param id - id of the provider. + * @param name - name of the provider to be presented to a user. + * @param languages - list of languages the {@code entries} provided for. + * {@code languages} can be {@code null}, in this case the {@code entries} + * are provided for any language. + * @param entries - the list of language settings entries this provider provides. + * If {@code null} is passed, the provider creates an empty list. + * @param properties - custom properties as the means to customize providers. + * + * @throws UnsupportedOperationException if an attempt to reconfigure provider is made. + */ + public void configureProvider(String id, String name, List languages, + List entries, Map properties) { + if (this.entries!=null || !this.properties.isEmpty()) + throw new UnsupportedOperationException(SettingsModelMessages.getString("LanguageSettingsBaseProvider.CanBeConfiguredOnlyOnce")); //$NON-NLS-1$ + + setId(id); + setName(name); + this.languageScope = languages!=null ? new ArrayList(languages) : null; + this.entries = getPooledList(entries); + if (properties != null) + this.properties = new HashMap(properties); + } + + /** + * {@code LanguageSettingsBaseProvider} keeps the list of key-value pairs + * so extenders of this class can customize the provider. The properties + * of {@code LanguageSettingsBaseProvider} come from the extension in plugin.xml + * although the extenders can provide their own method. + *

+ * Please note that empty string value is treated as "default" value and + * the same as {@code null} and the same as missing property, which allows + * {@link #equals(Object)} evaluate the property as equal while comparing providers. + * + * @param key - property to check the value. + * @return value of the property. If the property is missing returns empty string. + */ + public String getProperty(String key) { + String value = properties.get(key); + if (value == null) { + value = ""; //$NON-NLS-1$ + } + return value; + } + + /** + * Convenience method to get boolean property. + * @see #getProperty(String) + * + * @param key - property to check the value. + * @return boolean value of the property. If the property is missing or cannot be + * interpreted as boolean returns {@code false}. + */ + public boolean getPropertyBool(String key) { + return Boolean.parseBoolean(properties.get(key)); + } + + private List getPooledList(List entries) { + if (entries != null) { + return LanguageSettingsStorage.getPooledList(entries); + } + return null; + } + + /** + * {@inheritDoc} + * + * @param languageId - language id. If {@code null}, then entries defined for + * the language scope are returned. See {@link #getLanguageScope()} + * + * @return unmodifiable list of setting entries or {@code null} if no settings defined. + * the list is internally pooled and guaranteed to be the same object for equal + * lists. + */ + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, + IResource rc, String languageId) { + if (languageScope == null) { + return entries; + } + for (String lang : languageScope) { + if (lang.equals(languageId)) { + return entries; + } + } + return null; + } + + /** + * @return the unmodifiable list of languages this provider provides for. + * If {@code null}, the provider provides for any language. + */ + public List getLanguageScope() { + if (languageScope==null) + return null; + return Collections.unmodifiableList(languageScope); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); + result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); + result = prime * result + ((entries == null) ? 0 : entries.hashCode()); + result = prime * result + ((languageScope == null) ? 0 : languageScope.hashCode()); + // exclude field "properties" because of special rules for equals() + result = prime * result + getClass().hashCode(); + return result; + } + + /** + * @return {@code true} if the objects are equal, {@code false } otherwise. + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LanguageSettingsBaseProvider other = (LanguageSettingsBaseProvider) obj; + + String id = getId(); + String otherId = other.getId(); + if (id == null) { + if (otherId != null) + return false; + } else if (!id.equals(otherId)) + return false; + + String name = getName(); + String otherName = other.getName(); + if (name == null) { + if (otherName != null) + return false; + } else if (!name.equals(otherName)) + return false; + + if (entries == null) { + if (other.entries != null) + return false; + } else if (!entries.equals(other.entries)) + return false; + + if (languageScope == null) { + if (other.languageScope != null) + return false; + } else if (!languageScope.equals(other.languageScope)) + return false; + + if (properties == null) { + if (other.properties != null) + return false; + } else if (other.properties == null) { + return false; + } else { + // The trouble to ensure default properties are equal to missing ones. + Set keys = new HashSet(properties.keySet()); + keys.addAll(other.properties.keySet()); + for (String key : keys) { + String value = properties.get(key); + if (value == null || value.equals(Boolean.FALSE.toString())) + value = ""; //$NON-NLS-1$ + String otherValue = other.properties.get(key); + if (otherValue == null || otherValue.equals(Boolean.FALSE.toString())) + otherValue = ""; //$NON-NLS-1$ + if (!value.equals(otherValue)) + return false; + } + } + + return true; + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsGenericProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsGenericProvider.java new file mode 100644 index 00000000000..74f2bfc12ce --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsGenericProvider.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +/** + * Generic implementation of language settings provider which can be edited in UI + * with entries persisted between eclipse sessions. + * The instances of this class can be used in plugin.xml to create a new provider + * but this class is not intended to be extended. For more details how to create a + * language settings provider see the description of {@link ILanguageSettingsProvider}. + * + * @since 5.4 + */ +final public class LanguageSettingsGenericProvider extends LanguageSettingsSerializableProvider + implements ILanguageSettingsEditableProvider { + @Override + public LanguageSettingsGenericProvider clone() throws CloneNotSupportedException { + return (LanguageSettingsGenericProvider) super.clone(); + } + @Override + public LanguageSettingsGenericProvider cloneShallow() throws CloneNotSupportedException { + return (LanguageSettingsGenericProvider) super.cloneShallow(); + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsManager.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsManager.java new file mode 100644 index 00000000000..69a2bd1e4a7 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsManager.java @@ -0,0 +1,338 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.cdt.core.CCorePlugin; +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.ICFileDescription; +import org.eclipse.cdt.core.settings.model.ICFolderDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSetting; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICResourceDescription; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsExtensionManager; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; + +/** + * A collection of utility methods to manage language settings providers. + * See {@link ILanguageSettingsProvider}. + * + * @since 5.4 + */ +public class LanguageSettingsManager { + /** + * Returns the list of setting entries of the given provider + * for the given configuration description, resource and language. + * This method reaches to the parent folder of the resource recursively + * if the resource does not define the entries for the given provider. + * + * @param provider - language settings provider. + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. + * + * @return the list of setting entries. Never returns {@code null} + * although individual providers return {@code null} if no settings defined. + */ + public static List getSettingEntriesUpResourceTree(ILanguageSettingsProvider provider, ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + return LanguageSettingsProvidersSerializer.getSettingEntriesUpResourceTree(provider, cfgDescription, rc, languageId); + } + + /** + * Get Language Settings Provider from the list of workspace providers, + * see {@link #getWorkspaceProviders()}. + * + * @param id - id of provider to find. + * @return the workspace provider. If workspace provider is not defined + * a new instance is created and returned. + */ + public static ILanguageSettingsProvider getWorkspaceProvider(String id) { + return LanguageSettingsProvidersSerializer.getWorkspaceProvider(id); + } + + /** + * Get Language Settings Providers defined in the workspace. That includes + * user-defined providers and after that providers defined as extensions via + * {@code org.eclipse.cdt.core.LanguageSettingsProvider} extension point. + * Note that this returns wrappers around workspace provider so underlying + * provider could be replaced internally without need to change configuration. + * See also {@link #getRawProvider(ILanguageSettingsProvider)}. + * + * @return list of workspace providers. + */ + public static List getWorkspaceProviders() { + return LanguageSettingsProvidersSerializer.getWorkspaceProviders(); + } + + /** + * Checks if the provider is a workspace level provider. + * This method is intended to check providers retrieved from a configuration. + * Raw providers from {@link #getRawProvider(ILanguageSettingsProvider)} + * are not considered as workspace providers. + * + * @param provider - provider to check. + * @return {@code true} if the given provider is workspace provider, {@code false} otherwise. + */ + public static boolean isWorkspaceProvider(ILanguageSettingsProvider provider) { + return LanguageSettingsProvidersSerializer.isWorkspaceProvider(provider); + } + + /** + * Helper method to get to real underlying provider collecting entries as opposed to wrapper + * which is normally used for workspace provider. + * @see LanguageSettingsProvidersSerializer#isWorkspaceProvider(ILanguageSettingsProvider) + * + * @param provider - the provider to get raw provider for. Can be either workspace provider + * or regular one. + * @return raw underlying provider for workspace provider or provider itself if no wrapper is used. + */ + public static ILanguageSettingsProvider getRawProvider(ILanguageSettingsProvider provider) { + if (LanguageSettingsManager.isWorkspaceProvider(provider)) { + provider = LanguageSettingsProvidersSerializer.getRawWorkspaceProvider(provider.getId()); + } + return provider; + } + + + /** + * Set and store in workspace area user defined providers. + * + * @param providers - array of user defined workspace providers. + * Note that those providers will shadow extension providers with the same ID. + * All not shadowed extension providers will be added to the list to be present + * as workspace providers. {@code null} is equivalent to passing an empty array + * and so will reset workspace providers to match extension providers. + * @throws CoreException in case of problems (such as problems with persistence). + */ + public static void setWorkspaceProviders(List providers) throws CoreException { + LanguageSettingsProvidersSerializer.setWorkspaceProviders(providers); + } + + /** + * Copy language settings provider. It is different from clone() methods in that + * it does not throw {@code CloneNotSupportedException} but returns {@code null} + * instead. + * + * @param provider - language settings provider to copy. + * @param deep - {@code true} to request deep copy including copying settings entries + * or {@code false} to return shallow copy with no settings entries. + * + * @return a copy of the provider or null if copying is not possible. + */ + public static ILanguageSettingsEditableProvider getProviderCopy(ILanguageSettingsEditableProvider provider, boolean deep) { + return LanguageSettingsExtensionManager.getProviderCopy(provider, deep); + } + + /** + * Returns list of provider id-s contributed by all extensions. + * @return the provider id-s. + */ + public static Set getExtensionProviderIds() { + return LanguageSettingsExtensionManager.getExtensionProviderIds(); + } + + /** + * Get language settings provider defined via extension point + * {@code org.eclipse.cdt.core.LanguageSettingsProvider}. + * A new copy of the extension provider is returned. + * + * @param id - ID of the extension provider. + * @param deep - {@code true} to request deep copy including copying settings entries + * or {@code false} to return shallow copy with no settings entries. + * @return the copy of the extension provider if possible (i.e. for {@link ILanguageSettingsEditableProvider}) + * or {@code null} if provider is not copyable. + */ + public static ILanguageSettingsProvider getExtensionProviderCopy(String id, boolean deep) { + return LanguageSettingsExtensionManager.getExtensionProviderCopy(id, deep); + } + + /** + * Test if the provider is equal to the one defined via extension point + * {@code org.eclipse.cdt.core.LanguageSettingsProvider}. + * + * @param provider - the provider to test. + * @param deep - {@code true} to check for deep equality testing also settings entries + * or {@code false} to test shallow copy with no settings entries. + * @return - {@code true} if the provider matches the extension or {@code false} otherwise. + */ + public static boolean isEqualExtensionProvider(ILanguageSettingsProvider provider, boolean deep) { + return LanguageSettingsExtensionManager.isEqualExtensionProvider(provider, deep); + } + + /** + * Find language IDs for the resource represented by resource description. + * Under the hood build component is inquired and the language IDs would + * commonly come from the input type(s). + * + * @param rcDescription - resource description + * @return list of language IDs for the resource. + * Never returns {@code null} but empty list if no languages can be found. + * + */ + public static List getLanguages(ICResourceDescription rcDescription) { + ICLanguageSetting[] languageSettings = null; + if (rcDescription instanceof ICFileDescription) { + ICLanguageSetting languageSetting = ((ICFileDescription)rcDescription).getLanguageSetting(); + if (languageSetting != null) { + languageSettings = new ICLanguageSetting[] {languageSetting}; + } + } else if (rcDescription instanceof ICFolderDescription) { + languageSettings = ((ICFolderDescription)rcDescription).getLanguageSettings(); + } + + List languageIds = new ArrayList(); + if (languageSettings != null) { + for (ICLanguageSetting languageSetting : languageSettings) { + if (languageSetting!=null) { + String languageId = languageSetting.getLanguageId(); + if (languageId != null && !languageId.isEmpty()) { + languageIds.add(languageId); + } + } + } + } + + return languageIds; + } + + /** + * Find language IDs for the resource in given build configuration. + * Under the hood build component is inquired and the language IDs would + * commonly come from the input type(s). + * + * @param resource - the resource to find languages for. + * @param cfgDescription + * @return list of language IDs for the resource. + * Never returns {@code null} but empty list if no languages can be found. + */ + public static List getLanguages(IResource resource, ICConfigurationDescription cfgDescription) { + List languageIds = new ArrayList(); + IPath prjRelPath = resource.getProjectRelativePath(); + if (resource instanceof IFile) { + String langId = null; + if (cfgDescription != null) { + // Inquire MBS + ICLanguageSetting ls = cfgDescription.getLanguageSettingForFile(prjRelPath, true); + if (ls != null) { + langId = ls.getLanguageId(); + } + } + if (langId == null) { + // Try getting language from content types + try { + ILanguage lang = LanguageManager.getInstance().getLanguageForFile((IFile) resource, cfgDescription); + if (lang != null) { + langId = lang.getId(); + } + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + if (langId != null) { + languageIds.add(langId); + } + } else { + ICResourceDescription rcDes = cfgDescription.getResourceDescription(prjRelPath, false); + if (rcDes == null) { + rcDes = cfgDescription.getRootFolderDescription(); + } + languageIds = getLanguages(rcDes); + } + + return languageIds; + } + + /** + * Adds a listener that will be notified of changes in language settings. + * + * @param listener the listener to add + */ + public static void registerLanguageSettingsChangeListener(ILanguageSettingsChangeListener listener) { + LanguageSettingsProvidersSerializer.registerLanguageSettingsChangeListener(listener); + } + + /** + * Removes a language settings change listener. + * + * @param listener the listener to remove. + */ + public static void unregisterLanguageSettingsChangeListener(ILanguageSettingsChangeListener listener) { + LanguageSettingsProvidersSerializer.unregisterLanguageSettingsChangeListener(listener); + } + + /** + * Tells if the provider is meant to be shared between projects in workspace + * or belong to a specific configuration. This attribute is defined in + * {@code org.eclipse.cdt.core.LanguageSettingsProvider} extension point. + *
Note that only {@link ILanguageSettingsEditableProvider} can be owned by + * a configuration. + * + * @param id - ID of the provider to inquire. + * @return {@code true} if the provider is designed to be shared, + * {@code false} if configuration-owned. + */ + public static boolean isPreferShared(String id) { + return LanguageSettingsExtensionManager.isPreferShared(id); + } + + /** + * Tells if language settings entries of the provider are persisted with the project + * (under .settings/ folder) or in workspace area. Persistence in the project area lets + * the entries migrate with the project. + * + * @param provider - provider to check the persistence mode. + * @return {@code true} if LSE persisted with the project or {@code false} if in the workspace. + */ + public static boolean isStoringEntriesInProjectArea(LanguageSettingsSerializableProvider provider) { + return LanguageSettingsProvidersSerializer.isStoringEntriesInProjectArea(provider); + } + + /** + * Define where language settings are persisted for the provider. + * + * @param provider - provider to set the persistence mode. + * @param storeEntriesWithProject - {@code true} if with the project, + * {@code false} if in workspace area. + */ + public static void setStoringEntriesInProjectArea(LanguageSettingsSerializableProvider provider, boolean storeEntriesWithProject) { + LanguageSettingsProvidersSerializer.setStoringEntriesInProjectArea(provider, storeEntriesWithProject); + } + + /** + * Save language settings providers of a project to persistent storage. + * + * @param prjDescription - project description of the project. + * @throws CoreException if something goes wrong. + */ + public static void serializeLanguageSettings(ICProjectDescription prjDescription) throws CoreException { + LanguageSettingsProvidersSerializer.serializeLanguageSettings(prjDescription); + } + + /** + * Save language settings providers of the workspace (global providers) to persistent storage. + * + * @throws CoreException + */ + public static void serializeLanguageSettingsWorkspace() throws CoreException { + LanguageSettingsProvidersSerializer.serializeLanguageSettingsWorkspace(); + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java new file mode 100644 index 00000000000..afcb5429f1d --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsSerializableProvider.java @@ -0,0 +1,434 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsProvidersSerializer; +import org.eclipse.cdt.internal.core.language.settings.providers.LanguageSettingsSerializableStorage; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * This class is the base class for language settings providers able to serialize + * into XML storage. + * Although this class has setter methods, its instances are not editable in UI by + * design. Implement {@link ILanguageSettingsEditableProvider} interface for that. + * For more on the suggested way of extending this class see the description of + * {@link ILanguageSettingsProvider}. + * + * @since 5.4 + */ +public class LanguageSettingsSerializableProvider extends LanguageSettingsBaseProvider implements ILanguageSettingsBroadcastingProvider { + protected static final String ATTR_ID = LanguageSettingsProvidersSerializer.ATTR_ID; + protected static final String ATTR_NAME = LanguageSettingsProvidersSerializer.ATTR_NAME; + protected static final String ATTR_CLASS = LanguageSettingsProvidersSerializer.ATTR_CLASS; + protected static final String ELEM_PROVIDER = LanguageSettingsProvidersSerializer.ELEM_PROVIDER; + protected static final String ELEM_LANGUAGE_SCOPE = LanguageSettingsProvidersSerializer.ELEM_LANGUAGE_SCOPE; + + private LanguageSettingsSerializableStorage fStorage = new LanguageSettingsSerializableStorage(); + + /** + * Default constructor. This constructor has to be always followed with setting id and name of the provider. + * This constructor is necessary to instantiate the class via the extension point in plugin.xml. + */ + public LanguageSettingsSerializableProvider() { + super(); + } + + /** + * Constructor. + * + * @param id - id of the provider. + * @param name - name of the provider. Note that this name shows up in UI. + */ + public LanguageSettingsSerializableProvider(String id, String name) { + super(id, name); + } + + /** + * Constructor which allows to instantiate provider defined via XML markup. + * + * @param elementProvider + */ + public LanguageSettingsSerializableProvider(Element elementProvider) { + super(); + load(elementProvider); + } + + @Override + public void configureProvider(String id, String name, List languages, List entries, Map properties) { + // do not pass entries to super, keep them in local storage + super.configureProvider(id, name, languages, null, properties); + + fStorage.clear(); + + if (entries!=null) { + // note that these entries are intended to be retrieved by LanguageSettingsManager.getSettingEntriesUpResourceTree() + // when the whole resource hierarchy has been traversed up + setSettingEntries(null, null, null, entries); + } + } + + /** + * @return {@code true} if the provider does not keep any settings yet or {@code false} if there are some. + */ + public boolean isEmpty() { + return fStorage.isEmpty(); + } + + /** + * Set the language scope of the provider. + * + * @param languages - the list of languages this provider provides for. + * If {@code null}, the provider provides for any language. + * + * @see #getLanguageScope() + */ + public void setLanguageScope(List languages) { + if (languages==null) + this.languageScope = null; + else + this.languageScope = new ArrayList(languages); + } + + /** + * Clear all the entries for all configurations, all resources and all languages. + */ + public void clear() { + fStorage.clear(); + } + + /** + * Sets language settings entries for the provider. + * Note that the entries are not persisted at that point. Use this method to set + * the entries for all resources one by one and after all done persist in one shot + * using {@link #serializeLanguageSettings(ICConfigurationDescription)}. + * See for example {@code AbstractBuildCommandParser} and {@code AbstractBuiltinSpecsDetector} + * in build plugins. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. If {@code null} the entries are + * considered to be being defined as default entries for resources. + * @param languageId - language id. If {@code null}, then entries are considered + * to be defined for the language scope. See {@link #getLanguageScope()} + * @param entries - language settings entries to set. + */ + public void setSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId, List entries) { + String rcProjectPath = rc!=null ? rc.getProjectRelativePath().toString() : null; + fStorage.setSettingEntries(rcProjectPath, languageId, entries); + } + + /** + * {@inheritDoc} + *
+ * Note that this list is unmodifiable. To modify the list copy it, change and use + * {@link #setSettingEntries(ICConfigurationDescription, IResource, String, List)}. + *

+ * Note also that you can compare these lists with simple equality operator ==, + * as the lists themselves are backed by WeakHashSet> where + * identical copies (deep comparison is used) are replaced with the same one instance. + */ + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + String rcProjectPath = rc!=null ? rc.getProjectRelativePath().toString() : null; + List entries = fStorage.getSettingEntries(rcProjectPath, languageId); + if (entries == null) { + if (languageId != null && (languageScope == null || languageScope.contains(languageId))) { + entries = fStorage.getSettingEntries(rcProjectPath, null); + } + } + + return entries; + } + + /** + * Serialize the provider under parent XML element. + * This is convenience method not intended to be overridden on purpose. + * Override {@link #serializeAttributes(Element)} or + * {@link #serializeEntries(Element)} instead. + * + * @param parentElement - element where to serialize. + * @return - newly created "provider" element. That element will already be + * attached to the parent element. + */ + final public Element serialize(Element parentElement) { + /* + + + + + + + + + */ + Element elementProvider = serializeAttributes(parentElement); + serializeEntries(elementProvider); + return elementProvider; + } + + /** + * Serialize the provider attributes under parent XML element. That is + * equivalent to serializing everything (including language scope) except entries. + * + * @param parentElement - element where to serialize. + * @return - newly created "provider" element. That element will already be + * attached to the parent element. + */ + public Element serializeAttributes(Element parentElement) { + // Keeps pairs: key, value. See JavaDoc XmlUtil.appendElement(Node, String, String[]). + List attributes = new ArrayList(); + + attributes.add(ATTR_ID); + attributes.add(getId()); + attributes.add(ATTR_NAME); + attributes.add(getName()); + attributes.add(ATTR_CLASS); + attributes.add(getClass().getCanonicalName()); + for (Entry entry : properties.entrySet()) { + attributes.add(entry.getKey()); + attributes.add(entry.getValue()); + } + + Element elementProvider = XmlUtil.appendElement(parentElement, ELEM_PROVIDER, attributes.toArray(new String[0])); + + if (languageScope!=null) { + for (String langId : languageScope) { + XmlUtil.appendElement(elementProvider, ELEM_LANGUAGE_SCOPE, new String[] {ATTR_ID, langId}); + } + } + return elementProvider; + } + + /** + * Serialize the provider entries under parent XML element. + * @param elementProvider - element where to serialize the entries. + */ + public void serializeEntries(Element elementProvider) { + fStorage.serializeEntries(elementProvider); + } + + /** + * Convenience method to persist language settings entries for the project or + * workspace as often-used operation. + * Note that configuration description is passed as an argument but the + * current implementation saves all configurations. + * + * @param cfgDescription - configuration description. + * If not {@code null}, all providers of the project are serialized. + * If {@code null}, global workspace providers are serialized. + * + * @return - status of operation. + */ + public IStatus serializeLanguageSettings(ICConfigurationDescription cfgDescription) { + IStatus status = Status.OK_STATUS; + try { + if (cfgDescription != null) { + LanguageSettingsManager.serializeLanguageSettings(cfgDescription.getProjectDescription()); + } else { + LanguageSettingsManager.serializeLanguageSettingsWorkspace(); + } + } catch (CoreException e) { + status = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, IStatus.ERROR, "Error serializing language settings", e); //$NON-NLS-1$ + CCorePlugin.log(status); + } + return status; + } + + /** + * Load provider from XML provider element. + * This is convenience method not intended to be overridden on purpose. + * Override {@link #loadAttributes(Element)} or + * {@link #loadEntries(Element)} instead. + * + * @param providerNode - XML element "provider" to load provider from. + */ + final public void load(Element providerNode) { + fStorage.clear(); + languageScope = null; + + // provider/configuration/language/resource/entry + if (providerNode!=null) { + loadAttributes(providerNode); + loadEntries(providerNode); + } + } + + /** + * Determine and set language scope from given XML node. + */ + private void loadLanguageScopeElement(Node parentNode) { + if (languageScope==null) { + languageScope = new ArrayList(); + } + String id = XmlUtil.determineAttributeValue(parentNode, ATTR_ID); + languageScope.add(id); + + } + + /** + * Load attributes from XML provider element. + * @param providerNode - XML element "provider" to load attributes from. + */ + public void loadAttributes(Element providerNode) { + String providerId = XmlUtil.determineAttributeValue(providerNode, ATTR_ID); + String providerName = XmlUtil.determineAttributeValue(providerNode, ATTR_NAME); + + properties.clear(); + NamedNodeMap attrs = providerNode.getAttributes(); + for (int i=0; i
+ * A note of caution - do not use default values for a provider which are different + * from empty or {@code null} value. When providers are checked for equality + * (during internal operations in core) the missing properties are evaluated as + * empty ones. + * + * @see LanguageSettingsBaseProvider#getProperty(String) + * + * @param key - name of the property. + * @param value - value of the property. + * If value is {@code null} the property is removed from the list. + */ + public void setProperty(String key, String value) { + properties.put(key, value); + } + + /** + * Set a custom boolean property of the provider. + *
Please, note that default value is always {@code false}. + * @see LanguageSettingsBaseProvider#getProperty(String) + * + * @param key - name of the property. + * @param value - {@code boolean} value of the property. + */ + public void setPropertyBool(String key, boolean value) { + properties.put(key, Boolean.toString(value)); + } + + /** + * See {@link #cloneShallow()}. This method is extracted to avoid expressing + * {@link #clone()} via {@link #cloneShallow()}. Do not inline to "optimize"! + */ + private LanguageSettingsSerializableProvider cloneShallowInternal() throws CloneNotSupportedException { + LanguageSettingsSerializableProvider clone = (LanguageSettingsSerializableProvider)super.clone(); + if (languageScope!=null) + clone.languageScope = new ArrayList(languageScope); + clone.properties = new HashMap(properties); + + clone.fStorage = new LanguageSettingsSerializableStorage(); + return clone; + } + + /** + * Shallow clone of the provider. "Shallow" is defined here as the exact copy except that + * the copy will have zero language settings entries. + * + * @return shallow copy of the provider. + * @throws CloneNotSupportedException in case {@link #clone()} throws the exception. + */ + protected LanguageSettingsSerializableProvider cloneShallow() throws CloneNotSupportedException { + return cloneShallowInternal(); + } + + @Override + protected LanguageSettingsSerializableProvider clone() throws CloneNotSupportedException { + LanguageSettingsSerializableProvider clone = cloneShallowInternal(); + clone.fStorage = fStorage.clone(); + return clone; + } + + @Override + public LanguageSettingsStorage copyStorage() { + try { + return fStorage.clone(); + } catch (CloneNotSupportedException e) { + CCorePlugin.log(e); + } + return null; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((fStorage == null) ? 0 : fStorage.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + LanguageSettingsSerializableProvider other = (LanguageSettingsSerializableProvider) obj; + + if (fStorage == null) { + if (other.fStorage != null) + return false; + } else if (!fStorage.equals(other.fStorage)) + return false; + return true; + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsStorage.java new file mode 100644 index 00000000000..72f72333e41 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/LanguageSettingsStorage.java @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.internal.core.parser.util.WeakHashSet; + +/** + * The class representing the (in-memory) storage for language settings entries {@link ICLanguageSettingEntry}. + * + * @since 5.4 + */ +public class LanguageSettingsStorage implements Cloneable { + /** Storage to keep settings entries. */ + protected Map>> fStorage = new HashMap>>(); + + /** + * Pool of LSE lists implemented as WeakHashSet. That allows to gain memory savings + * at the expense of CPU time. WeakHashSet handles garbage collection when a list is not + * referenced anywhere else. See JavaDoc {@link java.lang.ref.WeakReference} about weak reference objects. + */ + private static WeakHashSet> listPool = new WeakHashSet>() { + @Override + public synchronized List add(List list) { + return super.add(list); + } + }; + + /** + * Returns the list of setting entries for the given resource and language. + *
Note that this list is unmodifiable. + * + * @param rcProjectPath - path to the resource relative to the project. + * @param languageId - language id. + * + * @return the list of setting entries or {@code null} if no settings defined. + */ + public List getSettingEntries(String rcProjectPath, String languageId) { + List entries = null; + Map> langMap = fStorage.get(languageId); + if (langMap!=null) { + entries = langMap.get(rcProjectPath); + } + return entries; + } + + /** + * Some providers may collect entries in pretty much random order. For the intent of + * predictability, UI usability and efficient storage the entries are sorted by kinds + * and secondary by name for kinds where the secondary order is not significant. + * + * @param entries - list of entries to sort. + * @return - sorted entries. + */ + private List sortEntries(List entries) { + List sortedEntries = new ArrayList(entries); + Collections.sort(sortedEntries, new Comparator() { + /** + * This comparator sorts by kinds first and the macros are sorted additionally by name. + */ + @Override + public int compare(ICLanguageSettingEntry entry0, ICLanguageSettingEntry entry1) { + int kind0 = entry0.getKind(); + int kind1 = entry1.getKind(); + if (kind0==ICSettingEntry.MACRO && kind1==ICSettingEntry.MACRO) { + return entry0.getName().compareTo(entry1.getName()); + } + + return kind0 - kind1; + }}); + + return sortedEntries; + } + + /** + * Sets language settings entries for the resource and language. + * + * @param rcProjectPath - path to the resource relative to the project. + * @param languageId - language id. + * @param entries - language settings entries to set. + */ + public void setSettingEntries(String rcProjectPath, String languageId, List entries) { + synchronized (fStorage) { + if (entries!=null) { + Map> langMap = fStorage.get(languageId); + if (langMap==null) { + langMap = new HashMap>(); + fStorage.put(languageId, langMap); + } + List sortedEntries = getPooledList(sortEntries(entries), false); + langMap.put(rcProjectPath, sortedEntries); + } else { + // reduct the empty maps in the tables + Map> langMap = fStorage.get(languageId); + if (langMap!=null) { + langMap.remove(rcProjectPath); + if (langMap.size()==0) { + fStorage.remove(languageId); + } + } + } + } + } + + /** + * @return {@code true} if the storage is empty or {@code false} otherwise. + */ + public boolean isEmpty() { + return fStorage.isEmpty(); + } + + /** + * Clear all the entries for all resources and all languages. + */ + public void clear() { + synchronized (fStorage) { + fStorage.clear(); + } + } + + /** + * Find and return the equal list of entries from the pool. + * + * @param entries - list of entries to pool. + * @param copy - specify {@code true} to copy the list in order to prevent + * back-door modification on the original list changes. + * @return returns the list of entries from the pool. + */ + private static List getPooledList(List entries, boolean copy) { + if (entries == null) + return null; + + List pooledList = listPool.get(entries); + if (pooledList != null) { + return pooledList; + } + + if (entries.size() == 0) { + return getPooledEmptyList(); + } + + if (copy) { + entries = new ArrayList(entries); + } + pooledList = Collections.unmodifiableList(entries); + return listPool.add(pooledList); + } + + /** + * Find and return the equal list of entries from the pool to conserve the memory. + * + * @param entries - list of entries to pool. + * @return returns the list of entries from the pool. + */ + public static List getPooledList(List entries) { + return getPooledList(entries, true); + } + + /** + * @return Returns the empty immutable list which is pooled. Use this call rather than creating + * new empty array to ensure that faster shallow operator '==' can be used instead of equals() + * which goes deep on HashMaps. + */ + public static List getPooledEmptyList() { + List pooledEmptyList = Collections.emptyList(); + return listPool.add(pooledEmptyList); + } + + /** + * Clone storage for the entries. Copies references for lists of entries as a whole. + * Note that that is OK as the lists kept in storage are unmodifiable and pooled. + */ + @Override + public LanguageSettingsStorage clone() throws CloneNotSupportedException { + LanguageSettingsStorage storageClone = (LanguageSettingsStorage) super.clone(); + storageClone.fStorage = new HashMap>>(); + synchronized (fStorage) { + Set>>> entrySetLang = fStorage.entrySet(); + for (Entry>> entryLang : entrySetLang) { + String langId = entryLang.getKey(); + Map> mapRc = entryLang.getValue(); + Map> mapRcClone = new HashMap>(); + Set>> entrySetRc = mapRc.entrySet(); + for (Entry> entryRc : entrySetRc) { + String rcProjectPath = entryRc.getKey(); + List lsEntries = entryRc.getValue(); + // don't need to clone entries, they are from the LSE lists pool + mapRcClone.put(rcProjectPath, lsEntries); + } + storageClone.fStorage.put(langId, mapRcClone); + } + } + return storageClone; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((fStorage == null) ? 0 : fStorage.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LanguageSettingsStorage other = (LanguageSettingsStorage) obj; + if (fStorage == null) { + if (other.fStorage != null) + return false; + } else if (!fStorage.equals(other.fStorage)) + return false; + return true; + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ScannerDiscoveryLegacySupport.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ScannerDiscoveryLegacySupport.java new file mode 100644 index 00000000000..55fe7f124be --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/language/settings/providers/ScannerDiscoveryLegacySupport.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core.language.settings.providers; + +import java.util.List; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.internal.core.LocalProjectScope; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.prefs.Preferences; + +/** + * Collection of utilities for legacy support of older Scanner Discovery functionality. + * This class is temporary and not intended to be used by clients. + * + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + * + * @since 5.4 + */ +public class ScannerDiscoveryLegacySupport { + /** ID of User language settings provider (from org.eclipse.cdt.ui) */ + public static final String USER_LANGUAGE_SETTINGS_PROVIDER_ID = "org.eclipse.cdt.ui.UserLanguageSettingsProvider"; //$NON-NLS-1$ + /** ID of MBS language settings provider (from org.eclipse.cdt.managedbuilder.core) */ + public static final String MBS_LANGUAGE_SETTINGS_PROVIDER_ID = "org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider"; //$NON-NLS-1$ + + private static String USE_LANGUAGE_SETTINGS_PROVIDERS_PREFERENCE = "enabled"; //$NON-NLS-1$ +// the default needs to be "false" for legacy projects to be open with old SD enabled for MBS provider + private static boolean USE_LANGUAGE_SETTINGS_PROVIDERS_DEFAULT = false; + private static final String PREFERENCES_QUALIFIER = CCorePlugin.PLUGIN_ID; + private static final String LANGUAGE_SETTINGS_PROVIDERS_NODE = "languageSettingsProviders"; //$NON-NLS-1$ + + private static Preferences getPreferences(IProject project) { + if (project == null) + return InstanceScope.INSTANCE.getNode(PREFERENCES_QUALIFIER).node(LANGUAGE_SETTINGS_PROVIDERS_NODE); + else + return new LocalProjectScope(project).getNode(PREFERENCES_QUALIFIER).node(LANGUAGE_SETTINGS_PROVIDERS_NODE); + } + + /** + * Checks if Language Settings functionality is enabled for given project. + * + * @param project - project to check the preference + * @return {@code true} if functionality is enabled + * + * @noreference This method is temporary and not intended to be referenced by clients. + */ + public static boolean isLanguageSettingsProvidersFunctionalityEnabled(IProject project) { + Preferences pref = getPreferences(project); + return pref.getBoolean(USE_LANGUAGE_SETTINGS_PROVIDERS_PREFERENCE, USE_LANGUAGE_SETTINGS_PROVIDERS_DEFAULT); + } + + /** + * Enable/disable Language Settings functionality for the given project. + * + * @param project + * @param value {@code true} to enable or {@code false} to disable the functionality. + * + * @noreference This method is temporary and not intended to be referenced by clients. + */ + public static void setLanguageSettingsProvidersFunctionalityEnabled(IProject project, boolean value) { + Preferences pref = getPreferences(project); + pref.putBoolean(USE_LANGUAGE_SETTINGS_PROVIDERS_PREFERENCE, value); + try { + pref.flush(); + } catch (BackingStoreException e) { + CCorePlugin.log(e); + } + } + + /** + * @noreference This is internal helper method to support compatibility with previous versions + * which is not intended to be referenced by clients. + */ + public static boolean isMbsLanguageSettingsProviderOn(ICConfigurationDescription cfgDescription) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + List lsProviders = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + for (ILanguageSettingsProvider lsp : lsProviders) { + if (MBS_LANGUAGE_SETTINGS_PROVIDER_ID.equals(lsp.getId())) { + return true; + } + } + } + return false; + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/LanguageSettingEntriesSerializer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/LanguageSettingEntriesSerializer.java index 67c88706764..6a1ac0045b1 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/LanguageSettingEntriesSerializer.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/settings/model/util/LanguageSettingEntriesSerializer.java @@ -40,10 +40,10 @@ public class LanguageSettingEntriesSerializer { public static final String ATTRIBUTE_SOURCE_ATTACHMENT_PATH = "srcPath"; //$NON-NLS-1$ public static final String ATTRIBUTE_SOURCE_ATTACHMENT_ROOT_PATH = "srcRootPath"; //$NON-NLS-1$ public static final String ATTRIBUTE_SOURCE_ATTACHMENT_PREFIX_MAPPING = "srcPrefixMapping"; //$NON-NLS-1$ - + // public static final String ATTRIBUTE_FULL_PATH = "fullPath"; //$NON-NLS-1$ // public static final String ATTRIBUTE_LOCATION = "location"; //$NON-NLS-1$ - + public static final String INCLUDE_PATH = "includePath"; //$NON-NLS-1$ public static final String INCLUDE_FILE = "includeFile"; //$NON-NLS-1$ @@ -59,7 +59,9 @@ public class LanguageSettingEntriesSerializer { public static final String LOCAL = "LOCAL"; //$NON-NLS-1$ public static final String VALUE_WORKSPACE_PATH = "VALUE_WORKSPACE_PATH"; //$NON-NLS-1$ public static final String RESOLVED = "RESOLVED"; //$NON-NLS-1$ - + private static final String UNDEFINED = "UNDEFINED"; //$NON-NLS-1$ + private static final String FRAMEWORK = "FRAMEWORK"; //$NON-NLS-1$ + public static final String FLAGS_SEPARATOR = "|"; //$NON-NLS-1$ public static ICSettingEntry[] loadEntries(ICStorageElement el){ @@ -84,7 +86,7 @@ public class LanguageSettingEntriesSerializer { child = children[i]; if(ELEMENT_ENTRY.equals(child.getName())){ entry = loadEntry(child); - if(entry != null + if(entry != null && (kindFilter == 0 || (kindFilter & entry.getKind()) != 0)) list.add(entry); @@ -97,11 +99,11 @@ public class LanguageSettingEntriesSerializer { int kind = stringToKind(el.getAttribute(ATTRIBUTE_KIND)); if(kind == 0) return null; - + int flags = composeFlags(el.getAttribute(ATTRIBUTE_FLAGS)); String name = el.getAttribute(ATTRIBUTE_NAME); - + switch(kind){ case ICSettingEntry.INCLUDE_PATH: return new CIncludePathEntry(name, flags); @@ -126,7 +128,7 @@ public class LanguageSettingEntriesSerializer { } return null; } - + private static IPath loadPath(ICStorageElement el, String attr){ String value = el.getAttribute(attr); if(value != null) @@ -149,18 +151,18 @@ public class LanguageSettingEntriesSerializer { } return paths; } - return null; + return null; } private static void storeExclusions(ICStorageElement el, IPath[] paths){ if(paths == null || paths.length == 0) return; - + String[] strs = new String[paths.length]; for(int i = 0; i < strs.length; i++){ strs[i] = paths[i].toString(); } - + String attr = CDataUtil.arrayToString(strs, FLAGS_SEPARATOR); el.setAttribute(ATTRIBUTE_EXCLUDING, attr); } @@ -174,7 +176,7 @@ public class LanguageSettingEntriesSerializer { } } } - + public static void serializeEntry(ICSettingEntry entry, ICStorageElement element){ String kind = kindToString(entry.getKind()); String flags = composeFlagsString(entry.getFlags()); @@ -197,7 +199,7 @@ public class LanguageSettingEntriesSerializer { IPath path = libFile.getSourceAttachmentPath(); if(path != null) element.setAttribute(ATTRIBUTE_SOURCE_ATTACHMENT_PATH, path.toString()); - + path = libFile.getSourceAttachmentRootPath(); if(path != null) element.setAttribute(ATTRIBUTE_SOURCE_ATTACHMENT_ROOT_PATH, path.toString()); @@ -207,7 +209,7 @@ public class LanguageSettingEntriesSerializer { element.setAttribute(ATTRIBUTE_SOURCE_ATTACHMENT_PREFIX_MAPPING, path.toString()); } } - + public static String kindToString(int kind){ switch(kind){ case ICSettingEntry.INCLUDE_PATH: @@ -259,34 +261,49 @@ public class LanguageSettingEntriesSerializer { if((flags & ICSettingEntry.READONLY) != 0){ if(buf.length() != 0) buf.append(FLAGS_SEPARATOR); - + buf.append(READONLY); } if((flags & ICSettingEntry.LOCAL) != 0){ if(buf.length() != 0) buf.append(FLAGS_SEPARATOR); - + buf.append(LOCAL); } if((flags & ICSettingEntry.VALUE_WORKSPACE_PATH) != 0){ if(buf.length() != 0) buf.append(FLAGS_SEPARATOR); - + buf.append(VALUE_WORKSPACE_PATH); } if((flags & ICSettingEntry.RESOLVED) != 0){ if(buf.length() != 0) buf.append(FLAGS_SEPARATOR); - + buf.append(RESOLVED); } + if((flags & ICLanguageSettingEntry.UNDEFINED) != 0){ + if(buf.length() != 0) + buf.append(FLAGS_SEPARATOR); + + buf.append(UNDEFINED); + } + if((flags & ICLanguageSettingEntry.FRAMEWORKS_MAC) != 0){ + if(buf.length() != 0) + buf.append(FLAGS_SEPARATOR); + + buf.append(FRAMEWORK); + } return buf.toString(); } - - private static int composeFlags(String flagsString){ + + /** + * @since 5.4 + */ + public static int composeFlags(String flagsString){ if(flagsString == null || flagsString.length() == 0) return 0; - + StringTokenizer tokenizer = new StringTokenizer(flagsString, FLAGS_SEPARATOR); int flags = 0; String f; @@ -301,9 +318,13 @@ public class LanguageSettingEntriesSerializer { if(VALUE_WORKSPACE_PATH.equals(f)) flags |= ICSettingEntry.VALUE_WORKSPACE_PATH; if(RESOLVED.equals(f)) - flags |= ICSettingEntry.RESOLVED; + flags |= ICSettingEntry.RESOLVED; + if(UNDEFINED.equals(f)) + flags |= ICSettingEntry.UNDEFINED; + if(FRAMEWORK.equals(f)) + flags |= ICSettingEntry.FRAMEWORKS_MAC; } - + return flags; } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java new file mode 100644 index 00000000000..5fd372612aa --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsDelta.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.language.settings.providers; + +import java.util.LinkedHashMap; + +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; + + +/** + * Contains the delta of changes that occurred as a result of modifying + * language settings entries {@link ICLanguageSettingEntry}. The delta is + * associated with a configuration description. + * + *

+ * EXPERIMENTAL. This class interface is not stable yet as + * it is not currently clear how it may need to be used in future. Only bare + * minimum is provided here at this point (CDT 9.0, Juno). + * There is no guarantee that this API will work or that it will remain the same. + * Please do not use this API without consulting with the CDT team. + *

+ * + * @noextend This interface is not intended to be extended by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class LanguageSettingsDelta { + // maps need to be ordered by providers + @SuppressWarnings("unused") + private LinkedHashMap oldLanguageSettingsState; + @SuppressWarnings("unused") + private LinkedHashMap newLanguageSettingsState; + + /** + * Constructor. + * + * @param oldState - old language settings storage state. + * @param newState - new language settings storage state. + */ + public LanguageSettingsDelta(LinkedHashMap oldState, LinkedHashMap newState) { + oldLanguageSettingsState = oldState; + newLanguageSettingsState = newState; + } + +} 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 new file mode 100644 index 00000000000..9fda16c0ca5 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsExtensionManager.java @@ -0,0 +1,396 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.cdt.core.AbstractExecutableExtensionBase; +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsEditableProvider; +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.LanguageSettingsGenericProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializableProvider; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.core.settings.model.util.LanguageSettingEntriesSerializer; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +/** + * Class {@code LanguageSettingsExtensionManager} manages {@link ILanguageSettingsProvider} extensions + */ +public class LanguageSettingsExtensionManager { + /** Name of the extension point for contributing language settings */ + static final String PROVIDER_EXTENSION_POINT_ID = "org.eclipse.cdt.core.LanguageSettingsProvider"; //$NON-NLS-1$ + static final String PROVIDER_EXTENSION_SIMPLE_ID = "LanguageSettingsProvider"; //$NON-NLS-1$ + static final String ATTR_ID = "id"; //$NON-NLS-1$ + static final String ATTR_NAME = "name"; //$NON-NLS-1$ + static final String ATTR_CLASS = "class"; //$NON-NLS-1$ + static final String ATTR_PREFER_NON_SHARED = "prefer-non-shared"; //$NON-NLS-1$ + + static final String ELEM_PROVIDER = "provider"; //$NON-NLS-1$ + static final String ELEM_LANGUAGE_SCOPE = "language-scope"; //$NON-NLS-1$ + + static final String ELEM_ENTRY = "entry"; //$NON-NLS-1$ + static final String ATTR_ENTRY_NAME = "name"; //$NON-NLS-1$ + static final String ATTR_ENTRY_KIND = "kind"; //$NON-NLS-1$ + static final String ATTR_ENTRY_VALUE = "value"; //$NON-NLS-1$ + static final String ELEM_ENTRY_FLAG = "flag"; //$NON-NLS-1$ + + /** + * Extension providers loaded once and used for equality only. + * Those who request extension provider will get copy rather than real instance. + */ + private static final LinkedHashMap fExtensionProviders = new LinkedHashMap(); + + /** + * Providers loaded initially via static initializer. + */ + static { + try { + loadProviderExtensions(); + } catch (Throwable e) { + CCorePlugin.log("Error loading language settings providers extensions", e); //$NON-NLS-1$ + } + } + + /** + * Load language settings providers contributed via the extension point. + */ + synchronized private static void loadProviderExtensions() { + // sort by name - the providers defined via extensions are kept in separate list sorted by name + Set sortedProviders = new TreeSet( + new Comparator() { + @Override + public int compare(ILanguageSettingsProvider pr1, ILanguageSettingsProvider pr2) { + return pr1.getName().compareTo(pr2.getName()); + } + } + ); + + loadProviderExtensions(Platform.getExtensionRegistry(), sortedProviders); + + fExtensionProviders.clear(); + for (ILanguageSettingsProvider provider : sortedProviders) { + fExtensionProviders.put(provider.getId(), provider); + } + } + + /** + * Load contributed extensions from extension registry. + * + * @param registry - extension registry + * @param providers - resulting set of providers + */ + private static void loadProviderExtensions(IExtensionRegistry registry, Set providers) { + providers.clear(); + IExtensionPoint extension = registry.getExtensionPoint(CCorePlugin.PLUGIN_ID, PROVIDER_EXTENSION_SIMPLE_ID); + if (extension != null) { + IExtension[] extensions = extension.getExtensions(); + for (IExtension ext : extensions) { + for (IConfigurationElement cfgEl : ext.getConfigurationElements()) { + ILanguageSettingsProvider provider = null; + String id = null; + try { + if (cfgEl.getName().equals(ELEM_PROVIDER)) { + id = determineAttributeValue(cfgEl, ATTR_ID); + provider = createExecutableExtension(cfgEl); + configureExecutableProvider(provider, cfgEl); + providers.add(provider); + } + } catch (Throwable e) { + CCorePlugin.log("Cannot load LanguageSettingsProvider extension id=" + id, e); //$NON-NLS-1$ + } + } + } + } + } + + + private static String determineAttributeValue(IConfigurationElement ce, String attr) { + String value = ce.getAttribute(attr); + return value != null ? value : ""; //$NON-NLS-1$ + } + + /** + * Creates empty non-configured provider as executable extension from extension point definition. + * If "class" attribute is empty {@link LanguageSettingsBaseProvider} is created. + * + * @param ce - configuration element with provider definition + * @return new non-configured provider + * @throws CoreException in case of failure + */ + private static ILanguageSettingsProvider createExecutableExtension(IConfigurationElement ce) throws CoreException { + String ceClass = ce.getAttribute(ATTR_CLASS); + ILanguageSettingsProvider provider = null; + if (ceClass==null || ceClass.trim().length()==0 || ceClass.equals(LanguageSettingsBaseProvider.class.getCanonicalName())) { + provider = new LanguageSettingsBaseProvider(); + } else { + provider = (ILanguageSettingsProvider)ce.createExecutableExtension(ATTR_CLASS); + } + + return provider; + } + + + /** + * Configure language settings provider with parameters defined in XML metadata. + * + * @param provider - empty non-configured provider. + * @param ce - configuration element from registry representing XML. + */ + private static void configureExecutableProvider(ILanguageSettingsProvider provider, IConfigurationElement ce) { + String ceId = determineAttributeValue(ce, ATTR_ID); + String ceName = determineAttributeValue(ce, ATTR_NAME); + Map ceAttributes = new HashMap(); + List languages = null; + List entries = null; + + for (String attr : ce.getAttributeNames()) { + if (!attr.equals(ATTR_ID) && !attr.equals(ATTR_NAME) && !attr.equals(ATTR_CLASS)) { + ceAttributes.put(attr, determineAttributeValue(ce, attr)); + } + } + + for (IConfigurationElement ceLang : ce.getChildren(ELEM_LANGUAGE_SCOPE)) { + String langId = determineAttributeValue(ceLang, ATTR_ID); + if (langId.length() > 0) { + if (languages == null) { + languages = new ArrayList(); + } + languages.add(langId); + } + } + + for (IConfigurationElement ceEntry : ce.getChildren(ELEM_ENTRY)) { + try { + int entryKind = LanguageSettingEntriesSerializer.stringToKind(determineAttributeValue(ceEntry, ATTR_ENTRY_KIND)); + String entryName = determineAttributeValue(ceEntry, ATTR_ENTRY_NAME); + String entryValue = determineAttributeValue(ceEntry, ATTR_ENTRY_VALUE); + + int flags = 0; + for (IConfigurationElement ceFlags : ceEntry.getChildren(ELEM_ENTRY_FLAG)) { + int bitFlag = LanguageSettingEntriesSerializer.composeFlags(determineAttributeValue(ceFlags, ATTR_ENTRY_VALUE)); + flags |= bitFlag; + } + + ICLanguageSettingEntry entry = (ICLanguageSettingEntry) CDataUtil.createEntry( + entryKind, entryName, entryValue, null, flags); + + if (entries == null) { + entries = new ArrayList(); + } + entries.add(entry); + + } catch (Exception e) { + CCorePlugin.log("Error creating language settings entry ", e); //$NON-NLS-1$ + } + } + + if (provider instanceof LanguageSettingsBaseProvider) { + ((LanguageSettingsBaseProvider) provider).configureProvider(ceId, ceName, languages, entries, ceAttributes); + } else if (provider instanceof AbstractExecutableExtensionBase) { + ((AbstractExecutableExtensionBase) provider).setId(ceId); + ((AbstractExecutableExtensionBase) provider).setName(ceName); + } + } + + /** + * Creates provider from extension point definition which matches value of the given attribute. + * The method will inspect extension registry for extension point "org.eclipse.cdt.core.LanguageSettingsProvider" + * to determine bundle and instantiate the class. + * + * @param attr - attribute to match. + * @param attrValue - value of the attribute to match. + * @param registry - extension registry. + * @param configure - flag which indicates if provider needs to be configured. + * @return new instance of the provider + */ + private static ILanguageSettingsProvider loadProviderFromRegistry(String attr, String attrValue, + IExtensionRegistry registry, boolean configure) { + try { + IExtensionPoint extension = registry.getExtensionPoint(CCorePlugin.PLUGIN_ID, PROVIDER_EXTENSION_SIMPLE_ID); + if (extension != null) { + IExtension[] extensions = extension.getExtensions(); + for (IExtension ext : extensions) { + for (IConfigurationElement cfgEl : ext.getConfigurationElements()) { + if (cfgEl.getName().equals(ELEM_PROVIDER) && attrValue.equals(cfgEl.getAttribute(attr))) { + ILanguageSettingsProvider provider = createExecutableExtension(cfgEl); + if (configure) { + configureExecutableProvider(provider, cfgEl); + } + return provider; + } + } + } + } + } catch (Exception e) { + CCorePlugin.log("Error creating language settings provider.", e); //$NON-NLS-1$ + } + return null; + } + + /** + * Create an instance of non-configured language settings provider of given class name. + * The class should be known to this method or registered with the extension point. + * + * @param className - class name to instantiate. + * @return new instance of language settings provider. + * + * @throws CoreException if not able to create a new instance. + */ + /*package*/ static ILanguageSettingsProvider instantiateProviderClass(String className) throws CoreException { + if (className==null || className.equals(LanguageSettingsSerializableProvider.class.getName())) { + return new LanguageSettingsSerializableProvider(); + } + if (className.equals(LanguageSettingsGenericProvider.class.getName())) { + return new LanguageSettingsGenericProvider(); + } + + // Create it as executable extension from the extension registry. + ILanguageSettingsProvider provider = loadProviderFromRegistry(ATTR_CLASS, className, Platform.getExtensionRegistry(), false); + if (provider == null) { + String msg = "Not able to load provider class=" + className; //$NON-NLS-1$ + throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg)); + } + return provider; + } + + /** + * Load an instance of language settings provider of given id from the extension point. + * The class should be registered with the extension point. + * + * @param id - class name to instantiate. + * @return new instance of language settings provider. + */ + /*package*/ static ILanguageSettingsProvider loadProvider(String id) { + if (id==null) { + return null; + } + + // Create it as executable extension from the extension registry. + ILanguageSettingsProvider provider = loadProviderFromRegistry(ATTR_ID, id, Platform.getExtensionRegistry(), true); + if (provider == null) { + String msg = "Not able to load provider id=" + id; //$NON-NLS-1$ + CCorePlugin.log(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg, new Exception(msg))); + } + return provider; + } + + /** + * Returns list of provider id-s contributed by all extensions. + * @return the provider id-s. + */ + public static Set getExtensionProviderIds() { + return fExtensionProviders.keySet(); + } + + /** + * Copy language settings provider. It is different from clone() methods in that + * it does not throw {@code CloneNotSupportedException} but returns {@code null} + * instead. + * + * @param provider - language settings provider to copy. + * @param deep - {@code true} to request deep copy including copying settings entries + * or {@code false} to return shallow copy with no settings entries. + * + * @return a copy of the provider or {@code null} if copying is not possible. + */ + public static ILanguageSettingsEditableProvider getProviderCopy(ILanguageSettingsEditableProvider provider, boolean deep) { + try { + if (deep) { + return provider.clone(); + } else { + return provider.cloneShallow(); + } + } catch (CloneNotSupportedException e) { + CCorePlugin.log("Error cloning provider " + provider.getId() + ", class " + provider.getClass(), e); //$NON-NLS-1$ //$NON-NLS-2$ + } + return null; + } + + /** + * Get language settings provider defined via extension point + * {@code org.eclipse.cdt.core.LanguageSettingsProvider}. + * A new copy of the extension provider is returned. + * + * @param id - ID of the extension provider. + * @param deep - {@code true} to request deep copy including copying settings entries + * or {@code false} to return shallow copy with no settings entries. + * @return the copy of the extension provider if possible (i.e. for {@link ILanguageSettingsEditableProvider}) + * or {@code null} if provider is not copyable. + */ + public static ILanguageSettingsProvider getExtensionProviderCopy(String id, boolean deep) { + ILanguageSettingsProvider provider = fExtensionProviders.get(id); + if (provider instanceof ILanguageSettingsEditableProvider) { + return getProviderCopy((ILanguageSettingsEditableProvider) provider, deep); + } + + return null; + } + + /** + * Test if the provider is equal to the one defined via extension point + * {@code org.eclipse.cdt.core.LanguageSettingsProvider}. + * + * @param provider - the provider to test. + * @param deep - {@code true} to check for deep equality testing also settings entries + * or {@code false} to test shallow copy with no settings entries. + * Shallow equality is applicable only for {@link ILanguageSettingsEditableProvider}. + * @return - {@code true} if the provider matches the extension or {@code false} otherwise. + */ + public static boolean isEqualExtensionProvider(ILanguageSettingsProvider provider, boolean deep) { + String id = provider.getId(); + if (deep || !(provider instanceof ILanguageSettingsEditableProvider)) { + ILanguageSettingsProvider extensionProvider = fExtensionProviders.get(id); + return provider.equals(extensionProvider); + } else { + ILanguageSettingsEditableProvider providerShallow = getProviderCopy((ILanguageSettingsEditableProvider) provider, false); + ILanguageSettingsProvider extensionProviderShallow = getExtensionProviderCopy(id, false); + return providerShallow == extensionProviderShallow + || (providerShallow != null && providerShallow.equals(extensionProviderShallow)); + } + } + + /** + * Tells if the provider is meant to be shared between projects in workspace + * or belong to a specific configuration. This attribute is defined in + * {@code org.eclipse.cdt.core.LanguageSettingsProvider} extension point. + *
Note that only {@link ILanguageSettingsEditableProvider} can be owned by + * a configuration. + * + * @param id - ID of the provider to inquire. + * @return {@code true} if the provider is designed to be shared, + * {@code false} if configuration-owned. + */ + public static boolean isPreferShared(String id) { + ILanguageSettingsProvider provider = fExtensionProviders.get(id); + if (provider instanceof LanguageSettingsBaseProvider && provider instanceof ILanguageSettingsEditableProvider) { + return ! ((LanguageSettingsBaseProvider) provider).getPropertyBool(ATTR_PREFER_NON_SHARED); + } + return true; + } +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java new file mode 100644 index 00000000000..af1908b9aed --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsProvidersSerializer.java @@ -0,0 +1,1488 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.language.settings.providers; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.language.settings.providers.ICListenerAgent; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeEvent; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeListener; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsEditableProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsSerializableProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; +import org.eclipse.cdt.core.language.settings.providers.ScannerDiscoveryLegacySupport; +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.ICStorageElement; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.settings.model.CConfigurationSpecSettings; +import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo; +import org.eclipse.core.filesystem.URIUtil; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +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.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ILock; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Helper class handling serialization and notifications for language settings entries {@link ICLanguageSettingEntry}. + */ +public class LanguageSettingsProvidersSerializer { + public static final String PROVIDER_EXTENSION_POINT_ID = LanguageSettingsExtensionManager.PROVIDER_EXTENSION_POINT_ID; + public static final String ATTR_ID = LanguageSettingsExtensionManager.ATTR_ID; + public static final String ATTR_NAME = LanguageSettingsExtensionManager.ATTR_NAME; + public static final String ATTR_CLASS = LanguageSettingsExtensionManager.ATTR_CLASS; + public static final String ELEM_PROVIDER = LanguageSettingsExtensionManager.ELEM_PROVIDER; + public static final String ELEM_LANGUAGE_SCOPE = LanguageSettingsExtensionManager.ELEM_LANGUAGE_SCOPE; + + private static final String PREFERENCE_WORSPACE_PROVIDERS_SET = "language.settings.providers.workspace.prefs.toggle"; //$NON-NLS-1$ + private static final String CPROJECT_STORAGE_MODULE = "org.eclipse.cdt.core.LanguageSettingsProviders"; //$NON-NLS-1$ + private static final String STORAGE_WORKSPACE_LANGUAGE_SETTINGS = "language.settings.xml"; //$NON-NLS-1$ + private static final String STORAGE_PROJECT_PATH = ".settings/language.settings.xml"; //$NON-NLS-1$ + + private static final String ELEM_PLUGIN = "plugin"; //$NON-NLS-1$ + private static final String ELEM_EXTENSION = "extension"; //$NON-NLS-1$ + private static final String ATTR_EXTENSION_POINT = "point"; //$NON-NLS-1$ + private static final String ELEM_PROJECT = "project"; //$NON-NLS-1$ + private static final String ELEM_CONFIGURATION = "configuration"; //$NON-NLS-1$ + private static final String ELEM_PROVIDER_REFERENCE = "provider-reference"; //$NON-NLS-1$ + private static final String ATTR_STORE_ENTRIES_WITH_PROJECT = "store-entries-with-project"; //$NON-NLS-1$ + + // those are for readability of xml only + private static final String ATTR_REF = "ref"; //$NON-NLS-1$ + private static final String VALUE_REF_SHARED_PROVIDER = "shared-provider"; //$NON-NLS-1$ + private static final String ATTR_COPY_OF = "copy-of"; //$NON-NLS-1$ + private static final String VALUE_COPY_OF_EXTENSION = "extension"; //$NON-NLS-1$ + + /** Cache of true (raw) workspace providers */ + private static Map rawGlobalWorkspaceProviders = new HashMap(); + /** Cache of workspace providers wrappers */ + private static Map globalWorkspaceProviders = new HashMap(); + + private static ListenerList fLanguageSettingsChangeListeners = new ListenerList(ListenerList.IDENTITY); + private static ILock serializingLock = Job.getJobManager().newLock(); + + /** + * Dummy class to represent ill-defined provider. + */ + private static class NotAccessibleProvider implements ILanguageSettingsProvider { + private final String id; + private NotAccessibleProvider(String providerId) { + this.id = providerId; + } + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + return null; + } + @Override + public String getName() { + return null; + } + @Override + public String getId() { + return id; + } + } + + /** + * language settings provider listener-cfgDescription association + */ + private static class ListenerAssociation { + private ICListenerAgent listener; + private ICConfigurationDescription cfgDescription; + + public ListenerAssociation(ICListenerAgent la, ICConfigurationDescription cfgd) { + listener = la; + cfgDescription = cfgd; + } + } + + /** + * Wrapper for workspace providers to ensure level of indirection. That way workspace providers + * can be changed/replaced without notifying/changing the configurations which keep the providers + * in their lists. + */ + private static class LanguageSettingsWorkspaceProvider implements ILanguageSettingsProvider, ICListenerAgent { + private String providerId; + private int projectCount = 0; + + private LanguageSettingsWorkspaceProvider(String id) { + Assert.isNotNull(id); + providerId = id; + } + + @Override + public String getId() { + return providerId; + } + + @Override + public String getName() { + ILanguageSettingsProvider rawProvider = getRawProvider(); + String name = rawProvider!=null ? rawProvider.getName() : null; + return name; + } + + @Override + public List getSettingEntries(ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + ILanguageSettingsProvider rawProvider = getRawProvider(); + List entries = rawProvider!=null ? rawProvider.getSettingEntries(cfgDescription, rc, languageId) : null; + return entries; + } + + /** + * Do not cache the "raw" provider as workspace provider can be changed at any time. + */ + private ILanguageSettingsProvider getRawProvider() { + return LanguageSettingsProvidersSerializer.getRawWorkspaceProvider(providerId); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof LanguageSettingsWorkspaceProvider) { + LanguageSettingsWorkspaceProvider that = (LanguageSettingsWorkspaceProvider) obj; + return providerId.equals(that.providerId); + } + return false; + } + + /** + * Method toString() for debugging purposes. + */ + @SuppressWarnings("nls") + @Override + public String toString() { + return "id="+getId()+", name="+getName(); + } + + /** + * We count number of times workspace provider (not the raw one!) associated + * with a project. If a project includes it multiple times via different configurations + * it still counts as 1. + */ + private int getProjectCount() { + return projectCount; + } + + private synchronized int incrementProjectCount() { + projectCount++; + return projectCount; + } + + private synchronized int decrementProjectCount() { + projectCount--; + return projectCount; + } + + @Override + public void registerListener(ICConfigurationDescription cfgDescription) { + // keep in mind that rawProvider can change externally + ILanguageSettingsProvider rawProvider = getRawProvider(); + if (rawProvider instanceof ICListenerAgent) { + ((ICListenerAgent) rawProvider).registerListener(null); + } + } + + @Override + public void unregisterListener() { + // keep in mind that rawProvider can change externally + ILanguageSettingsProvider rawProvider = getRawProvider(); + if (rawProvider instanceof ICListenerAgent) { + ((ICListenerAgent) rawProvider).unregisterListener(); + } + } + } + + /** + * Language Settings Change Event implementation. + */ + private static class LanguageSettingsChangeEvent implements ILanguageSettingsChangeEvent { + private String projectName = null; + private Map deltaMap = new HashMap(); + + /** + * The act of creating event resets internal delta count in configuration state. + * That implies that when the event is retrieved it must be fired or delta will go missing. + * That side effect is here to ensure atomic processing of firing & resetting the delta. + */ + public LanguageSettingsChangeEvent(ICProjectDescription prjDescription) { + if (!prjDescription.isReadOnly()) { + // The logic goes that we send notifications only for acting description but not for currently being prepared to set + String msg = "Project description " + prjDescription.getName() + " is expected to be read-only"; //$NON-NLS-1$ //$NON-NLS-2$ + CCorePlugin.log(new Status(IStatus.WARNING, CCorePlugin.PLUGIN_ID, msg, new Exception(msg))); + } + + projectName = prjDescription.getName(); + ICConfigurationDescription[] cfgDescriptions = prjDescription.getConfigurations(); + for (ICConfigurationDescription cfgDescription : cfgDescriptions) { + if (cfgDescription instanceof IInternalCCfgInfo) { + CConfigurationSpecSettings specSettings = null; + try { + specSettings = ((IInternalCCfgInfo) cfgDescription).getSpecSettings(); + } catch (CoreException e) { + CCorePlugin.log(e); + } + if (specSettings != null) { + LanguageSettingsDelta delta = specSettings.dropDelta(); + if (delta != null) + deltaMap.put(cfgDescription.getId(), delta); + } else { + IStatus ss = new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, "Internal error: Missing specSettings for " //$NON-NLS-1$ + + cfgDescription.getClass().getSimpleName()); + CCorePlugin.log(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, ss.getMessage(), new CoreException(ss))); + } + } + } + } + + @Override + public String getProjectName() { + return projectName; + } + + @Override + public String[] getConfigurationDescriptionIds() { + return deltaMap.keySet().toArray(new String[deltaMap.size()]); + } + + @SuppressWarnings("nls") + @Override + public String toString() { + return "LanguageSettingsChangeEvent for project=[" + getProjectName() + "]" + + ", configurations=" + deltaMap.keySet(); + } + } + + /** static initializer */ + static { + try { + loadLanguageSettingsWorkspace(); + } catch (Throwable e) { + // log but swallow any exception + CCorePlugin.log("Error loading workspace language settings providers", e); //$NON-NLS-1$ + } + } + + /** + * Tells if language settings entries of the provider are persisted with the project + * (under .settings/ folder) or in workspace area. Persistence in the project area lets + * the entries migrate with the project. + * + * @param provider - provider to check the persistence mode. + * @return {@code true} if LSE persisted with the project or {@code false} if in the workspace. + */ + public static boolean isStoringEntriesInProjectArea(LanguageSettingsSerializableProvider provider) { + return provider.getPropertyBool(ATTR_STORE_ENTRIES_WITH_PROJECT); + } + + /** + * Define where language settings are persisted for the provider. + * + * @param provider - provider to set the persistence mode. + * @param storeEntriesWithProject - {@code true} if with the project, + * {@code false} if in workspace area. + */ + public static void setStoringEntriesInProjectArea(LanguageSettingsSerializableProvider provider, boolean storeEntriesWithProject) { + provider.setPropertyBool(ATTR_STORE_ENTRIES_WITH_PROJECT, storeEntriesWithProject); + } + + /** + * Determine location of the project store of language settings providers in the plug-in state area. + * + * @param store - name of the store. + * @return location of the store in the plug-in state area. + */ + private static IFile getStoreInProjectArea(IProject project) { + return project.getFile(STORAGE_PROJECT_PATH); + } + + /** + * Determine location of the store in the plug-in state area. + * + * @param store - name of the store. + * @return location of the store in the plug-in state area. + */ + private static URI getStoreInWorkspaceArea(String store) { + IPath location = CCorePlugin.getDefault().getStateLocation().append(store); + return URIUtil.toURI(location); + } + + /** + * Set and store user defined providers in workspace area. + * + * @param providers - array of user defined providers + * @throws CoreException in case of problems + */ + public static void setWorkspaceProviders(List providers) throws CoreException { + setWorkspaceProvidersInternal(providers); + serializeLanguageSettingsWorkspace(); + // generate preference change event for preference change listeners (value is not intended to be used) + IEclipsePreferences prefs = InstanceScope.INSTANCE.getNode(CCorePlugin.PLUGIN_ID); + prefs.putBoolean(PREFERENCE_WORSPACE_PROVIDERS_SET, ! prefs.getBoolean(PREFERENCE_WORSPACE_PROVIDERS_SET, false)); + } + + /** + * Internal method to set user defined providers in memory. + * + * @param providers - list of user defined providers. If {@code null} + * is passed user defined providers are cleared. + */ + private static void setWorkspaceProvidersInternal(List providers) { + Map rawNewProviders = new HashMap(); + + // add given providers + if (providers != null) { + for (ILanguageSettingsProvider provider : providers) { + if (isWorkspaceProvider(provider)) { + provider = rawGlobalWorkspaceProviders.get(provider.getId()); + } + if (provider != null) { + rawNewProviders.put(provider.getId(), provider); + } + } + } + + // fill the rest from extension registry + // this list is independent from the internal list of extensions in LanguageSettingsExtensionManager + for (String id : LanguageSettingsExtensionManager.getExtensionProviderIds()) { + if (!rawNewProviders.containsKey(id)) { + ILanguageSettingsProvider provider = LanguageSettingsExtensionManager.getExtensionProviderCopy(id, true); + if (provider == null) { + provider = LanguageSettingsExtensionManager.loadProvider(id); + } + if (provider != null) { + rawNewProviders.put(provider.getId(), provider); + } + } + } + + // register listeners + List oldListeners = selectListeners(rawGlobalWorkspaceProviders.values()); + List newListeners = selectListeners(rawNewProviders.values()); + + for (ICListenerAgent oldListener : oldListeners) { + if (!isInList(newListeners, oldListener)) { + LanguageSettingsWorkspaceProvider wspProvider = (LanguageSettingsWorkspaceProvider) globalWorkspaceProviders.get(((ILanguageSettingsProvider)oldListener).getId()); + if (wspProvider != null && wspProvider.getProjectCount() > 0) { + oldListener.unregisterListener(); + } + } + } + + for (ICListenerAgent newListener : newListeners) { + if (!isInList(oldListeners, newListener)) { + LanguageSettingsWorkspaceProvider wspProvider = (LanguageSettingsWorkspaceProvider) globalWorkspaceProviders.get(((ILanguageSettingsProvider)newListener).getId()); + if (wspProvider != null && wspProvider.getProjectCount() > 0) { + newListener.registerListener(null); + } + } + } + + rawGlobalWorkspaceProviders = rawNewProviders; + } + + /** + * Create event for language settings changes of workspace providers in a project. + */ + private static LanguageSettingsChangeEvent createEvent(ICProjectDescription prjDescription, List providerIds) { + ICConfigurationDescription[] cfgDescriptions = prjDescription.getConfigurations(); + for (ICConfigurationDescription cfgDescription : cfgDescriptions) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + for (ILanguageSettingsProvider provider : ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders()) { + if (isWorkspaceProvider(provider) && providerIds.contains(provider.getId())) { + LanguageSettingsChangeEvent event = new LanguageSettingsChangeEvent(prjDescription); + if (event.getConfigurationDescriptionIds().length > 0) { + return event; + } + return null; + } + } + } + } + + return null; + } + + /** + * Compute events for language settings changes in workspace. + */ + private static List createLanguageSettingsChangeEvents(List providers) { + List events = new ArrayList(); + + List providerIds = new ArrayList(); + for (LanguageSettingsSerializableProvider provider : providers) { + providerIds.add(provider.getId()); + } + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IProject[] projects = root.getProjects(); + + for (IProject project : projects) { + if (project.isAccessible()) { + ICProjectDescription prjDescription = CCorePlugin.getDefault().getProjectDescription(project, false); + if (prjDescription != null) { + try { + LanguageSettingsChangeEvent event = createEvent(prjDescription, providerIds); + if (event != null) { + events.add(event); + } + } catch (Throwable e) { + // log and swallow any exception + CCorePlugin.log("Error creating event about changes in workspace language settings providers, " //$NON-NLS-1$ + + "project=" + project.getName(), e); //$NON-NLS-1$ + } + } + } + + } + + return events; + } + + /** + * Save language settings providers of the workspace (global providers) to persistent storage. + * @throws CoreException + */ + public static void serializeLanguageSettingsWorkspace() throws CoreException { + URI uriStoreWsp = getStoreInWorkspaceArea(STORAGE_WORKSPACE_LANGUAGE_SETTINGS); + List serializableWorkspaceProviders = new ArrayList(); + for (ILanguageSettingsProvider provider : rawGlobalWorkspaceProviders.values()) { + if (provider instanceof LanguageSettingsSerializableProvider) { + if (!LanguageSettingsManager.isEqualExtensionProvider(provider, true)) { + serializableWorkspaceProviders.add((LanguageSettingsSerializableProvider)provider); + } + } + } + try { + List events = null; + if (serializableWorkspaceProviders.isEmpty()) { + java.io.File fileStoreWsp = new java.io.File(uriStoreWsp); + try { + serializingLock.acquire(); + fileStoreWsp.delete(); + // manufacture events while inside the lock + events = createLanguageSettingsChangeEvents(serializableWorkspaceProviders); + } finally { + serializingLock.release(); + } + } else { + Document doc = XmlUtil.newDocument(); + Element rootElement = XmlUtil.appendElement(doc, ELEM_PLUGIN); + Element elementExtension = XmlUtil.appendElement(rootElement, ELEM_EXTENSION, + new String[] {ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + + for (LanguageSettingsSerializableProvider provider : serializableWorkspaceProviders) { + provider.serialize(elementExtension); + } + + try { + serializingLock.acquire(); + XmlUtil.serializeXml(doc, uriStoreWsp); + // manufacture events while inside the lock + events = createLanguageSettingsChangeEvents(serializableWorkspaceProviders); + } finally { + serializingLock.release(); + } + } + // notify the listeners outside the lock + for (LanguageSettingsChangeEvent event : events) { + notifyLanguageSettingsChangeListeners(event); + } + + } catch (Exception e) { + String msg = "Internal error while trying to serialize language settings"; //$NON-NLS-1$ + CCorePlugin.log(msg, e); + throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg, e)); + } + } + + /** + * Load language settings for workspace. + */ + public static void loadLanguageSettingsWorkspace() { + List providers = null; + + URI uriStoreWsp = getStoreInWorkspaceArea(STORAGE_WORKSPACE_LANGUAGE_SETTINGS); + + Document doc = null; + try { + serializingLock.acquire(); + doc = XmlUtil.loadXml(uriStoreWsp); + } catch (Exception e) { + CCorePlugin.log("Can't load preferences from file " + uriStoreWsp, e); //$NON-NLS-1$ + } finally { + serializingLock.release(); + } + + if (doc != null) { + Element rootElement = doc.getDocumentElement(); + NodeList providerNodes = rootElement.getElementsByTagName(ELEM_PROVIDER); + + List userDefinedProvidersIds = new ArrayList(providerNodes.getLength()); + providers = new ArrayList(providerNodes.getLength()); + + for (int i = 0; i < providerNodes.getLength(); i++) { + Node providerNode = providerNodes.item(i); + final String providerId = XmlUtil.determineAttributeValue(providerNode, ATTR_ID); + if (userDefinedProvidersIds.contains(providerId)) { + String msg = "Ignored an attempt to persist duplicate language settings provider, id=" + providerId; //$NON-NLS-1$ + CCorePlugin.log(new Status(IStatus.WARNING, CCorePlugin.PLUGIN_ID, msg, new Exception())); + continue; + } + userDefinedProvidersIds.add(providerId); + + ILanguageSettingsProvider provider = null; + try { + provider = loadProvider(providerNode); + } catch (Exception e) { + CCorePlugin.log("Error initializing workspace language settings providers", e); //$NON-NLS-1$ + } + if (provider == null) { + provider = new NotAccessibleProvider(providerId); + } + providers.add(provider); + } + } + setWorkspaceProvidersInternal(providers); + } + + /** + * @noreference This method is not intended to be referenced by clients. + * It is public solely for benefit of JUnit testing. + */ + public static void serializeLanguageSettingsInternal(Element projectElementPrjStore, Element projectElementWspStore, ICProjectDescription prjDescription) { + ICConfigurationDescription[] cfgDescriptions = prjDescription.getConfigurations(); + for (ICConfigurationDescription cfgDescription : cfgDescriptions) { + if (!(cfgDescription instanceof ILanguageSettingsProvidersKeeper)) + continue; + + // no lazy initialization as we may need to save 0 providers when it is different from default + Element elementConfiguration = XmlUtil.appendElement(projectElementPrjStore, ELEM_CONFIGURATION, new String[] { + ATTR_ID, cfgDescription.getId(), + ATTR_NAME, cfgDescription.getName(), + }); + Element elementConfigurationWsp = null; + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + if (providers.size() > 0) { + Element elementExtension = null; + Element elementExtensionWsp = null; + + for (ILanguageSettingsProvider provider : providers) { + if (isWorkspaceProvider(provider)) { + if (elementExtension == null) { + elementExtension = XmlUtil.appendElement(elementConfiguration, ELEM_EXTENSION, new String[] { + ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + } + // Element elementProviderReference = + XmlUtil.appendElement(elementExtension, ELEM_PROVIDER_REFERENCE, new String[] { + ATTR_ID, provider.getId(), + ATTR_REF, VALUE_REF_SHARED_PROVIDER, + }); + continue; + } + if (!(provider instanceof LanguageSettingsSerializableProvider)) { + if (elementExtension == null) { + elementExtension = XmlUtil.appendElement(elementConfiguration, ELEM_EXTENSION, new String[] { + ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + } + // Element elementProvider = + XmlUtil.appendElement(elementExtension, ELEM_PROVIDER, new String[] { + ATTR_ID, provider.getId(), + ATTR_NAME, provider.getName(), + ATTR_CLASS, provider.getClass().getCanonicalName(), + }); + } else if (LanguageSettingsManager.isEqualExtensionProvider(provider, true)) { + if (elementExtension == null) { + elementExtension = XmlUtil.appendElement(elementConfiguration, ELEM_EXTENSION, new String[] { + ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + } + // Element elementProvider = + XmlUtil.appendElement(elementExtension, ELEM_PROVIDER, new String[] { + ATTR_ID, provider.getId(), + ATTR_COPY_OF, VALUE_COPY_OF_EXTENSION, + }); + } else { + try { + LanguageSettingsSerializableProvider lss = (LanguageSettingsSerializableProvider) provider; + + boolean isWspStorageAvailable = (projectElementWspStore != null) && (projectElementPrjStore != projectElementWspStore); + if (isStoringEntriesInProjectArea(lss) || !isWspStorageAvailable) { + if (elementExtension == null) { + elementExtension = XmlUtil.appendElement(elementConfiguration, ELEM_EXTENSION, new String[] { + ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + } + lss.serialize(elementExtension); + } else { + if (elementExtension == null) { + elementExtension = XmlUtil.appendElement(elementConfiguration, ELEM_EXTENSION, new String[] { + ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + } + if (elementExtensionWsp == null) { + if (elementConfigurationWsp == null) { + elementConfigurationWsp = XmlUtil.appendElement(projectElementWspStore, ELEM_CONFIGURATION, new String[] { + ATTR_ID, cfgDescription.getId(), + ATTR_NAME, cfgDescription.getName(), + }); + } + elementExtensionWsp = XmlUtil.appendElement(elementConfigurationWsp, ELEM_EXTENSION, new String[] { + ATTR_EXTENSION_POINT, PROVIDER_EXTENSION_POINT_ID}); + } + Element elementProviderWsp = XmlUtil.appendElement(elementExtensionWsp, ELEM_PROVIDER, new String[] { + ATTR_ID, provider.getId() }); // no attributes kept in workspace storage + + // split storage + lss.serializeAttributes(elementExtension); + lss.serializeEntries(elementProviderWsp); + } + } catch (Throwable e) { + // protect from any exceptions from implementers + CCorePlugin.log("Exception trying serialize provider "+provider.getId(), e); //$NON-NLS-1$ + } + } + } + } + } + } + + /** + * Check if all the language settings providers in the project match defaults. + */ + private static boolean isEqualToDefaultProviders(ICProjectDescription prjDescription) { + ICConfigurationDescription[] cfgDescriptions = prjDescription.getConfigurations(); + for (ICConfigurationDescription cfgDescription : cfgDescriptions) { + if (!(cfgDescription instanceof ILanguageSettingsProvidersKeeper)) { + continue; + } + + String[] defaultIds = ((ILanguageSettingsProvidersKeeper) cfgDescription).getDefaultLanguageSettingsProvidersIds(); + if (defaultIds == null) { + defaultIds = new String[0]; + } + + // check size + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + if (providers.size() != defaultIds.length) { + return false; + } + + // check ids + for (int i = 0; i < defaultIds.length; i++) { + ILanguageSettingsProvider provider = providers.get(i); + if (!provider.getId().equals(defaultIds[i])) { + return false; + } + } + + // check equality (most expensive, so check last) + for (ILanguageSettingsProvider provider : providers) { + if (LanguageSettingsManager.isPreferShared(provider.getId())) { + if (!isWorkspaceProvider(provider)) { + return false; + } + } else { + if (!LanguageSettingsManager.isEqualExtensionProvider(provider, true)) { + return false; + } + } + } + } + + return true; + } + + /** + * Save language settings providers of a project to persistent storage. + * + * @param prjDescription - project description of the project. + * @throws CoreException if something goes wrong. + */ + public static void serializeLanguageSettings(ICProjectDescription prjDescription) throws CoreException { + IProject project = prjDescription.getProject(); + try { + // Using side effect of adding the module to the storage + prjDescription.getStorage(CPROJECT_STORAGE_MODULE, true); + } catch (CoreException e) { + CCorePlugin.log("Internal error while trying to serialize language settings", e); //$NON-NLS-1$ + } + + try { + // The storage could be split in two, one for provider properties, another one for entries, + // depending on provider flag + + // Document to store in project area + Document docStorePrj = XmlUtil.newDocument(); + Element projectElementStorePrj = XmlUtil.appendElement(docStorePrj, ELEM_PROJECT); + // Document to store in workspace area + Document docStoreWsp = XmlUtil.newDocument(); + Element projectElementStoreWsp = XmlUtil.appendElement(docStoreWsp, ELEM_PROJECT); + + URI uriStoreWsp = getStoreInWorkspaceArea(project.getName()+'.'+STORAGE_WORKSPACE_LANGUAGE_SETTINGS); + LanguageSettingsChangeEvent event = null; + + try { + // Note that need for serialization may exist even if LS *entries* event delta is empty, + // as set of providers or their properties may differ + + serializingLock.acquire(); + + if (!isEqualToDefaultProviders(prjDescription)) { + serializeLanguageSettingsInternal(projectElementStorePrj, projectElementStoreWsp, prjDescription); + } + + // Absent store means default providers as specified in the toolchain + IFile fileStorePrj = getStoreInProjectArea(project); + boolean isProjectStoreEmpty = projectElementStorePrj.getChildNodes().getLength() == 0; + if (isProjectStoreEmpty) { + if (fileStorePrj.exists()) { + fileStorePrj.delete(true, null); + } + } else { + IContainer folder = fileStorePrj.getParent(); + if (folder instanceof IFolder && !folder.exists()) { + ((IFolder) folder).create(true, true, null); + } + XmlUtil.serializeXml(docStorePrj, fileStorePrj); + } + + // project-specific location in workspace area + boolean isWorkspaceStoreEmpty = projectElementStoreWsp.getChildNodes().getLength() == 0; + if (isWorkspaceStoreEmpty) { + new java.io.File(uriStoreWsp).delete(); + } else { + XmlUtil.serializeXml(docStoreWsp, uriStoreWsp); + } + + // manufacture the event only if serialization was successful + event = new LanguageSettingsChangeEvent(prjDescription); + } finally { + serializingLock.release(); + } + // notify the listeners outside the lock + if (event.getConfigurationDescriptionIds().length > 0) { + notifyLanguageSettingsChangeListeners(event); + } + + } catch (Exception e) { + String msg = "Internal error while trying to serialize language settings"; //$NON-NLS-1$ + CCorePlugin.log(msg, e); + throw new CoreException(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg, e)); + } + } + + /** + * Load language settings to the project description from XML. + * + * @noreference This method is not intended to be referenced by clients. + * It is public solely for benefit of JUnit testing. + */ + public static void loadLanguageSettingsInternal(Element projectElementPrj, Element projectElementWsp, ICProjectDescription prjDescription) { + /* + + + + + + + + + */ + NodeList configurationNodes = projectElementPrj.getChildNodes(); + for (int ic = 0; ic < configurationNodes.getLength(); ic++) { + Node cfgNode = configurationNodes.item(ic); + if (!isElementWithName(cfgNode, ELEM_CONFIGURATION)) { + continue; + } + + List providers = new ArrayList(); + String cfgId = XmlUtil.determineAttributeValue(cfgNode, ATTR_ID); + + NodeList extensionNodes = cfgNode.getChildNodes(); + for (int ie = 0; ie < extensionNodes.getLength(); ie++) { + Node extNode = extensionNodes.item(ie); + if (!isElementWithName(extNode, ELEM_EXTENSION)) { + continue; + } + + NodeList providerNodes = extNode.getChildNodes(); + for (int ip = 0; ip < providerNodes.getLength(); ip++) { + ILanguageSettingsProvider provider = null; + + Node providerNode = providerNodes.item(ip); + if (isElementWithName(providerNode, ELEM_PROVIDER_REFERENCE)) { + String providerId = XmlUtil.determineAttributeValue(providerNode, ATTR_ID); + provider = getWorkspaceProvider(providerId); + } else if (isElementWithName(providerNode, ELEM_PROVIDER)) { + String providerClass = XmlUtil.determineAttributeValue(providerNode, ATTR_CLASS); + if (providerClass == null || providerClass.isEmpty()) { + // provider is copied from extension if "class" is not supplied + String providerId = XmlUtil.determineAttributeValue(providerNode, ATTR_ID); + provider = LanguageSettingsManager.getExtensionProviderCopy(providerId, true); + + if (provider == null) { + String msg = "Internal Error trying to retrieve copy of extension provider id=" + providerId; //$NON-NLS-1$ + CCorePlugin.log(new Status(IStatus.ERROR, CCorePlugin.PLUGIN_ID, msg, new Exception(msg))); + + provider = LanguageSettingsManager.getWorkspaceProvider(providerId); + } + } else { + try { + provider = loadProvider(providerNode); + } catch (CoreException e) { + @SuppressWarnings("nls") + String msg = "Error loading provider class=[" + providerClass + "] " + + "in project=" + prjDescription.getProject().getName() + ", cfg=[" + cfgId + "]"; + CCorePlugin.log(msg, e); + } + if (provider instanceof LanguageSettingsSerializableProvider) { + LanguageSettingsSerializableProvider lss = (LanguageSettingsSerializableProvider) provider; + if (!isStoringEntriesInProjectArea(lss) && projectElementWsp != null) { + loadProviderEntries(lss, cfgId, projectElementWsp); + } + } + } + } + if (provider != null) { + providers.add(provider); + } + } + } + + ICConfigurationDescription cfgDescription = prjDescription.getConfigurationById(cfgId); + setProvidersWithoutNotification(cfgDescription, providers); + } + } + + /** + * Set providers into configuration description avoiding triggering an event. + */ + private static void setProvidersWithoutNotification(ICConfigurationDescription cfgDescription, List providers) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + if (cfgDescription instanceof IInternalCCfgInfo) { + try { + // swallow delta + ((IInternalCCfgInfo) cfgDescription).getSpecSettings().dropDelta(); + } catch (CoreException e) { + CCorePlugin.log(e); + } + } + } + } + + /** + * Check if the node is {@code Element} and that the name matches. + */ + private static boolean isElementWithName(Node cfgNode, String name) { + return cfgNode instanceof Element && cfgNode.getNodeName().equals(name); + } + + /** + * Load provider entries for the given configuration from XML Element. + */ + private static void loadProviderEntries(LanguageSettingsSerializableProvider provider, + String cfgId, Element projectElement) { + /* + + + + + + + + + */ + NodeList configurationNodes = projectElement.getChildNodes(); + for (int ic = 0; ic < configurationNodes.getLength(); ic++) { + Node cfgNode = configurationNodes.item(ic); + if (!isElementWithName(cfgNode, ELEM_CONFIGURATION)) { + continue; + } + String cfgIdXml = XmlUtil.determineAttributeValue(cfgNode, ATTR_ID); + if (!cfgId.equals(cfgIdXml)) { + continue; + } + + NodeList extensionNodes = cfgNode.getChildNodes(); + for (int ie = 0; ie < extensionNodes.getLength(); ie++) { + Node extNode = extensionNodes.item(ie); + if (!isElementWithName(extNode, ELEM_EXTENSION)) { + continue; + } + + NodeList providerNodes = extNode.getChildNodes(); + for (int ip = 0; ip < providerNodes.getLength(); ip++) { + Node providerNode = providerNodes.item(ip); + if (!isElementWithName(providerNode, ELEM_PROVIDER)) { + continue; + } + + String id = XmlUtil.determineAttributeValue(providerNode, ATTR_ID); + if (provider.getId().equals(id)) { + provider.loadEntries((Element) providerNode); + return; + } + } + } + } + } + + /** + * Load provider from provider node. + */ + private static ILanguageSettingsProvider loadProvider(Node providerNode) throws CoreException { + String attrClass = XmlUtil.determineAttributeValue(providerNode, ATTR_CLASS); + ILanguageSettingsProvider provider = LanguageSettingsExtensionManager.instantiateProviderClass(attrClass); + + if (provider instanceof LanguageSettingsSerializableProvider) { + ((LanguageSettingsSerializableProvider)provider).load((Element) providerNode); + } + return provider; + } + + /** + * Load language settings from workspace and project storages for the given project description. + * @param prjDescription - project description to load language settings. + */ + public static void loadLanguageSettings(ICProjectDescription prjDescription) { + IProject project = prjDescription.getProject(); + IFile storeInPrjArea = getStoreInProjectArea(project); + try { + Job currentJob = Job.getJobManager().currentJob(); + ISchedulingRule currentRule = (currentJob != null) ? currentJob.getRule() : null; + if ((currentRule == null || currentRule.contains(storeInPrjArea)) && !ResourcesPlugin.getWorkspace().isTreeLocked()) { + storeInPrjArea.refreshLocal(IResource.DEPTH_ZERO, null); + } + } catch (Throwable e) { + // ignore failure + CCorePlugin.log("Internal Error trying to call IResourse.refreshLocal()", e); //$NON-NLS-1$ + } + if (storeInPrjArea.exists()) { + Document doc = null; + try { + doc = XmlUtil.loadXml(storeInPrjArea); + Element rootElementPrj = doc.getDocumentElement(); // + + URI uriStoreWsp = getStoreInWorkspaceArea(project.getName()+'.'+STORAGE_WORKSPACE_LANGUAGE_SETTINGS); + Document docWsp = null; + try { + serializingLock.acquire(); + docWsp = XmlUtil.loadXml(uriStoreWsp); + } finally { + serializingLock.release(); + } + + Element rootElementWsp = null; // + if (docWsp != null) { + rootElementWsp = docWsp.getDocumentElement(); + } + + loadLanguageSettingsInternal(rootElementPrj, rootElementWsp, prjDescription); + } catch (Exception e) { + CCorePlugin.log("Can't load preferences from file " + storeInPrjArea.getLocation(), e); //$NON-NLS-1$ + } + + } else { + // Storage in project area does not exist + ICStorageElement storageElement = null; + try { + storageElement = prjDescription.getStorage(CPROJECT_STORAGE_MODULE, false); + } catch (CoreException e) { + String msg = "Internal error while trying to load language settings"; //$NON-NLS-1$ + CCorePlugin.log(msg, e); + } + + if (storageElement != null) { + // set default providers defined in the tool-chain + for (ICConfigurationDescription cfgDescription : prjDescription.getConfigurations()) { + String[] ids = ((ILanguageSettingsProvidersKeeper) cfgDescription).getDefaultLanguageSettingsProvidersIds(); + if (ids != null) { + List providers = new ArrayList(ids.length); + for (String id : ids) { + if (LanguageSettingsExtensionManager.isPreferShared(id)) { + providers.add(LanguageSettingsManager.getWorkspaceProvider(id)); + } else { + providers.add(LanguageSettingsManager.getExtensionProviderCopy(id, true)); + } + + } + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + } + } + + } else { + // Older existing legacy projects unaware of Language Settings Providers and their persistence store + ICConfigurationDescription[] cfgDescriptions = prjDescription.getConfigurations(); + for (ICConfigurationDescription cfgDescription : cfgDescriptions) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + List providers = new ArrayList(2); + providers.add(LanguageSettingsExtensionManager.getExtensionProviderCopy((ScannerDiscoveryLegacySupport.USER_LANGUAGE_SETTINGS_PROVIDER_ID), true)); + providers.add(getWorkspaceProvider(ScannerDiscoveryLegacySupport.MBS_LANGUAGE_SETTINGS_PROVIDER_ID)); + ((ILanguageSettingsProvidersKeeper) cfgDescription).setLanguageSettingProviders(providers); + } + } + } + + } + } + + /** + * Get Language Settings Provider from the list of workspace providers, + * see {@link #getWorkspaceProviders()}. + * + * @param id - ID of provider to find. + * @return the workspace provider. If provider is not defined - still workspace + * provider wrapper is created and returned. + */ + public static ILanguageSettingsProvider getWorkspaceProvider(String id) { + ILanguageSettingsProvider provider = globalWorkspaceProviders.get(id); + if (provider == null) { + provider = new LanguageSettingsWorkspaceProvider(id); + globalWorkspaceProviders.put(id, provider); + } + return provider; + } + + /** + * Helper method to get to real underlying provider collecting entries as opposed + * to wrapper which is normally used for workspace provider. + * @see #isWorkspaceProvider(ILanguageSettingsProvider) + * + * @param id - ID of the provider. + * @return raw underlying provider. + */ + public static ILanguageSettingsProvider getRawWorkspaceProvider(String id) { + return rawGlobalWorkspaceProviders.get(id); + } + + /** + * Get Language Settings Providers defined in the workspace. That includes + * user-defined providers and after that providers defined as extensions via + * {@code org.eclipse.cdt.core.LanguageSettingsProvider} extension point. + * Note that this returns wrappers around workspace provider so underlying + * provider could be replaced internally without need to change configuration. + * See also {@link #getRawWorkspaceProvider(String)}. + * + * @return list of workspace providers. + */ + public static List getWorkspaceProviders() { + ArrayList workspaceProviders = new ArrayList(); + for (ILanguageSettingsProvider rawProvider : rawGlobalWorkspaceProviders.values()) { + workspaceProviders.add(getWorkspaceProvider(rawProvider.getId())); + } + return workspaceProviders; + } + + /** + * Checks if the provider is a workspace level provider. + * This method is intended to check providers retrieved from a configuration. + * Raw providers from {@link #getRawWorkspaceProvider(String)} + * are not considered as workspace providers. + * + * @param provider - provider to check. + * @return {@code true} if the given provider is workspace provider, {@code false} otherwise. + */ + public static boolean isWorkspaceProvider(ILanguageSettingsProvider provider) { + return provider instanceof LanguageSettingsWorkspaceProvider; + } + + /** + * Check that this particular element is in the list. + */ + private static boolean isInList(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; + } + + /** + * Check that this particular element is in the association list. + */ + private static boolean isListenerInTheListOfAssociations(Collection list, ICListenerAgent element) { + for (ListenerAssociation la : list) { + // we are interested in exact object, not in equal object + if (la.listener == element) + return true; + } + return false; + } + + /** + * Get a providers list including only providers of type {@link ICListenerAgent} + * for a given project description - collecting from all configurations. + */ + private static List getListeners(ICProjectDescription prjDescription) { + List listeners = new ArrayList(); + if (prjDescription != null) { + for (ICConfigurationDescription cfgDescription : prjDescription.getConfigurations()) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + for (ILanguageSettingsProvider provider : providers) { + if (provider instanceof ICListenerAgent) { + ICListenerAgent listener = (ICListenerAgent) provider; + if (!isInList(listeners, listener)) { + listeners.add(listener); + } + } + } + } + } + } + return listeners; + } + + /** + * Pick from the list providers which are listeners, i.e. instances of type {@link ICListenerAgent}. + */ + private static List selectListeners(Collection values) { + List listeners = new ArrayList(); + for (ILanguageSettingsProvider provider : values) { + if (provider instanceof ICListenerAgent) + listeners.add((ICListenerAgent) provider); + } + return listeners; + } + + /** + * Get a providers list including only providers of type {@link ICListenerAgent} + * for a given project description - collecting from all configurations. + */ + private static List getListenersAssociations(ICProjectDescription prjDescription) { + List associations = new ArrayList(); + if (prjDescription != null) { + for (ICConfigurationDescription cfgDescription : prjDescription.getConfigurations()) { + if (cfgDescription instanceof ILanguageSettingsProvidersKeeper) { + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + List listeners = selectListeners(providers); + for (ICListenerAgent listener : listeners) { + if (!isListenerInTheListOfAssociations(associations, listener)) { + associations.add(new ListenerAssociation(listener, cfgDescription)); + } + } + } + } + } + return associations; + } + + /** + * Unregister listeners which are not used anymore and register new listeners. + * The method is called 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) { + return; + } + + List oldListeners = getListeners(oldPrjDescription); + List newAssociations = getListenersAssociations(newPrjDescription); + + // unregister old listeners + for (ICListenerAgent oldListener : oldListeners) { + if (!isListenerInTheListOfAssociations(newAssociations, oldListener)) { + int count = 0; + if (oldListener instanceof LanguageSettingsWorkspaceProvider) { + count = ((LanguageSettingsWorkspaceProvider) oldListener).decrementProjectCount(); + } + if (count <= 0) { + try { + oldListener.unregisterListener(); + } catch (Throwable e) { + // protect from any exceptions from implementers + CCorePlugin.log("Exception trying unregister listener "+((ILanguageSettingsProvider) oldListener).getId(), e); //$NON-NLS-1$ + } + } + } + } + + // register new listeners + for (ListenerAssociation newListenerAssociation : newAssociations) { + ICListenerAgent newListener = newListenerAssociation.listener; + if (!isInList(oldListeners, newListener)) { + int count = 1; + if (newListener instanceof LanguageSettingsWorkspaceProvider) { + count = ((LanguageSettingsWorkspaceProvider) newListener).incrementProjectCount(); + } + if (count == 1) { + try { + newListener.registerListener(newListenerAssociation.cfgDescription); + } catch (Throwable e) { + // protect from any exceptions from implementers + CCorePlugin.log("Exception trying register listener "+((ILanguageSettingsProvider) newListener).getId(), e); //$NON-NLS-1$ + } + } + } + } + + } + + /** + * Adds a listener that will be notified of changes in language settings. + * + * @param listener - the listener to add + */ + public static void registerLanguageSettingsChangeListener(ILanguageSettingsChangeListener listener) { + fLanguageSettingsChangeListeners.add(listener); + } + + /** + * Removes a language settings change listener. + * + * @param listener - the listener to remove. + */ + public static void unregisterLanguageSettingsChangeListener(ILanguageSettingsChangeListener listener) { + fLanguageSettingsChangeListeners.remove(listener); + } + + /** + * Notifies all language settings change listeners of a change in language settings entries. + * + * @param event - the {@link ILanguageSettingsChangeEvent} event to be broadcast. + */ + private static void notifyLanguageSettingsChangeListeners(ILanguageSettingsChangeEvent event) { + for (Object listener : fLanguageSettingsChangeListeners.getListeners()) { + ((ILanguageSettingsChangeListener) listener).handleEvent(event); + } + } + + /** + * Get list of setting entries from the pool in {@link LanguageSettingsStorage}. + */ + private static List getSettingEntriesPooled(ILanguageSettingsProvider provider, + ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + try { + return LanguageSettingsStorage.getPooledList(provider.getSettingEntries(cfgDescription, rc, languageId)); + } catch (Throwable e) { + String cfgId = cfgDescription!=null ? cfgDescription.getId() : null; + @SuppressWarnings("nls") + String msg = "Exception in provider "+provider.getId()+": getSettingEntries("+cfgId+", "+rc+", "+languageId+")"; + CCorePlugin.log(msg, e); + // return empty list to prevent getting potentially non-empty list from up the resource tree + return LanguageSettingsStorage.getPooledEmptyList(); + } + } + + /** + * Returns the list of setting entries of the given provider + * for the given configuration description, resource and language. + * This method reaches to the parent folder of the resource recursively + * if the resource does not define the entries for the given provider. + * + * @param provider - language settings provider. + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. + * + * @return the list of setting entries which is unmodifiable. Never returns {@code null} + * although individual providers mandated to return {@code null} if no settings defined. + */ + public static List getSettingEntriesUpResourceTree(ILanguageSettingsProvider provider, ICConfigurationDescription cfgDescription, IResource rc, String languageId) { + Assert.isTrue( !(rc instanceof IWorkspaceRoot) ); + if (provider != null) { + List entries = getSettingEntriesPooled(provider, cfgDescription, rc, languageId); + if (entries != null) { + return entries; + } + if (rc != null) { + IResource parentFolder = (rc instanceof IProject) ? null : rc.getParent(); + if (parentFolder != null) { + return getSettingEntriesUpResourceTree(provider, cfgDescription, parentFolder, languageId); + } + // if out of parent resources - get default entries + entries = getSettingEntriesPooled(provider, null, null, languageId); + if (entries != null) { + return entries; + } + } + } + + return LanguageSettingsStorage.getPooledEmptyList(); + } + + /** + * Test if the binary flag contains a particular bit. + */ + private static boolean checkBit(int flags, int bit) { + return (flags & bit) == bit; + } + + /** + * Returns the list of setting entries of a certain kind (such as include paths) + * for the given configuration description, resource and language. This is a + * combined list for all providers taking into account settings of parent folder + * if settings for the given resource are not defined. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. + * @param kind - kind of language settings entries, such as + * {@link ICSettingEntry#INCLUDE_PATH} etc. This is a binary flag + * and it is possible to specify composite kind. + * Use {@link ICSettingEntry#ALL} to get all kinds. + * @param checkLocality - specifies if parameter {@code isLocal} should be considered. + * @param isLocal - {@code true} if "local" entries should be provided and + * {@code false} for "system" entries. This makes sense for include paths where + * [#include "..."] is "local" and [#include <...>] is system. + * + * @return the list of setting entries found. + */ + private static List getSettingEntriesByKind(ICConfigurationDescription cfgDescription, + IResource rc, String languageId, int kind, boolean checkLocality, boolean isLocal) { + + if (!(cfgDescription instanceof ILanguageSettingsProvidersKeeper)) { + return null; + } + + List entries = new ArrayList(); + List alreadyAdded = new ArrayList(); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + for (ILanguageSettingsProvider provider: providers) { + List providerEntries = getSettingEntriesUpResourceTree(provider, cfgDescription, rc, languageId); + for (ICLanguageSettingEntry entry : providerEntries) { + if (entry != null) { + String entryName = entry.getName(); + boolean isRightKind = checkBit(entry.getKind(), kind); + // Only first entry is considered + // Entry flagged as "UNDEFINED" prevents adding entry with the same name down the line + if (isRightKind && !alreadyAdded.contains(entryName)) { + int flags = entry.getFlags(); + boolean isRightLocal = !checkLocality || (checkBit(flags, ICSettingEntry.LOCAL) == isLocal); + if (isRightLocal) { + if (!checkBit(flags, ICSettingEntry.UNDEFINED)) { + entries.add(entry); + } + alreadyAdded.add(entryName); + } + } + } + } + } + + return entries; + } + + /** + * Returns the list of setting entries of a certain kind (such as include paths) + * for the given configuration description, resource and language. This is a + * combined list for all providers taking into account settings of parent folder + * if settings for the given resource are not defined. For include paths both + * local (#include "...") and system (#include <...>) entries are returned. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. + * @param kind - kind of language settings entries, such as + * {@link ICSettingEntry#INCLUDE_PATH} etc. This is a binary flag + * and it is possible to specify composite kind. + * Use {@link ICSettingEntry#ALL} to get all kinds. + * + * @return the list of setting entries. + */ + public static List getSettingEntriesByKind(ICConfigurationDescription cfgDescription, IResource rc, String languageId, int kind) { + return getSettingEntriesByKind(cfgDescription, rc, languageId, kind, /* checkLocality */ false, /* isLocal */ false); + } + + /** + * Returns the list of "system" (such as [#include <...>]) setting entries of a certain kind + * for the given configuration description, resource and language. This is a + * combined list for all providers taking into account settings of parent folder + * if settings for the given resource are not defined. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. + * @param kind - kind of language settings entries, such as + * {@link ICSettingEntry#INCLUDE_PATH} etc. This is a binary flag + * and it is possible to specify composite kind. + * Use {@link ICSettingEntry#ALL} to get all kinds. + * + * @return the list of setting entries. + */ + public static List getSystemSettingEntriesByKind(ICConfigurationDescription cfgDescription, IResource rc, String languageId, int kind) { + return getSettingEntriesByKind(cfgDescription, rc, languageId, kind, /* checkLocality */ true, /* isLocal */ false); + } + + /** + * Returns the list of "local" (such as [#include "..."]) setting entries of a certain kind + * for the given configuration description, resource and language. This is a + * combined list for all providers taking into account settings of parent folder + * if settings for the given resource are not defined. + * + * @param cfgDescription - configuration description. + * @param rc - resource such as file or folder. + * @param languageId - language id. + * @param kind - kind of language settings entries, such as + * {@link ICSettingEntry#INCLUDE_PATH} etc. This is a binary flag + * and it is possible to specify composite kind. + * Use {@link ICSettingEntry#ALL} to get all kinds. + * + * @return the list of setting entries. + */ + public static List getLocalSettingEntriesByKind(ICConfigurationDescription cfgDescription, IResource rc, String languageId, int kind) { + return getSettingEntriesByKind(cfgDescription, rc, languageId, kind, /* checkLocality */ true, /* isLocal */ true); + } + + /** + * 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) { + ILanguageSettingsEditableProvider newProvider = LanguageSettingsManager.getProviderCopy((ILanguageSettingsEditableProvider) provider, true); + if (newProvider != null) { + provider = newProvider; + } + } + newProviders.add(provider); + } + return new ArrayList(newProviders); + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java new file mode 100644 index 00000000000..8c4810b0a56 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsScannerInfoProvider.java @@ -0,0 +1,270 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.cdtvariables.CdtVariableException; +import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.parser.ExtendedScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfo; +import org.eclipse.cdt.core.parser.IScannerInfoChangeListener; +import org.eclipse.cdt.core.parser.IScannerInfoProvider; +import org.eclipse.cdt.core.settings.model.ACPathEntry; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICMacroEntry; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; +import org.eclipse.cdt.internal.core.settings.model.SettingsModelMessages; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +/** + * Implementation of {@link IScannerInfoProvider} backed by the list of + * language settings providers of "default settings configuration" + * (see {@link ICProjectDescription#getDefaultSettingConfiguration()}). + * + * @see IScannerInfo#getIncludePaths() + * + */ +public class LanguageSettingsScannerInfoProvider implements IScannerInfoProvider { + private static final String FRAMEWORK_PRIVATE_HEADERS_INCLUDE = "/__framework__.framework/PrivateHeaders/__header__"; //$NON-NLS-1$ + private static final String FRAMEWORK_HEADERS_INCLUDE = "/__framework__.framework/Headers/__header__"; //$NON-NLS-1$ + private static final ExtendedScannerInfo DUMMY_SCANNER_INFO = new ExtendedScannerInfo(); + + @Override + public ExtendedScannerInfo getScannerInformation(IResource rc) { + IProject project = rc.getProject(); + if (project==null) + return DUMMY_SCANNER_INFO; + + ICProjectDescription prjDescription = CProjectDescriptionManager.getInstance().getProjectDescription(project, false); + if (prjDescription==null) + return DUMMY_SCANNER_INFO; + + ICConfigurationDescription cfgDescription = prjDescription.getDefaultSettingConfiguration(); + if (cfgDescription==null) + return DUMMY_SCANNER_INFO; + + List languageIds = LanguageSettingsManager.getLanguages(rc, cfgDescription); + if (languageIds.isEmpty()) { + String msg = NLS.bind(SettingsModelMessages.getString("LanguageSettingsScannerInfoProvider.UnableToDetermineLanguage"), rc.toString()); //$NON-NLS-1$ + IStatus status = new Status(IStatus.WARNING, CCorePlugin.PLUGIN_ID, msg, new Exception()); + CCorePlugin.log(status); + return DUMMY_SCANNER_INFO; + } + + LinkedHashSet includePathEntries = new LinkedHashSet(); + LinkedHashSet includePathLocalEntries = new LinkedHashSet(); + LinkedHashSet includeFileEntries = new LinkedHashSet(); + LinkedHashSet macroFileEntries = new LinkedHashSet(); + LinkedHashSet macroEntries = new LinkedHashSet(); + + for (String langId : languageIds) { + List incSys = LanguageSettingsProvidersSerializer.getSystemSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.INCLUDE_PATH); + includePathEntries.addAll(incSys); + + List incLocal = LanguageSettingsProvidersSerializer.getLocalSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.INCLUDE_PATH); + includePathLocalEntries.addAll(incLocal); + + List incFiles = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.INCLUDE_FILE); + includeFileEntries.addAll(incFiles); + + List macroFiles = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.MACRO_FILE); + macroFileEntries.addAll(macroFiles); + + List macros = LanguageSettingsProvidersSerializer.getSettingEntriesByKind(cfgDescription, rc, langId, + ICSettingEntry.MACRO); + macroEntries.addAll(macros); + } + + String[] includePaths = convertToLocations(includePathEntries, cfgDescription); + String[] includePathsLocal = convertToLocations(includePathLocalEntries, cfgDescription); + String[] includeFiles = convertToLocations(includeFileEntries, cfgDescription); + String[] macroFiles = convertToLocations(macroFileEntries, cfgDescription); + + Map definedMacros = new HashMap(); + for (ICLanguageSettingEntry entry : macroEntries) { + ICMacroEntry macroEntry = (ICMacroEntry)entry; + String name = macroEntry.getName(); + String value = macroEntry.getValue(); + definedMacros.put(name, value); + } + + return new ExtendedScannerInfo(definedMacros, includePaths, macroFiles, includeFiles, includePathsLocal); + } + + private IPath expandVariables(IPath path, ICConfigurationDescription cfgDescription) { + ICdtVariableManager varManager = CCorePlugin.getDefault().getCdtVariableManager(); + String pathStr = path.toString(); + try { + pathStr = varManager.resolveValue(pathStr, "", null, cfgDescription); //$NON-NLS-1$ + } catch (CdtVariableException e) { + // Swallow exceptions but also log them + CCorePlugin.log(e); + } + IPath resolvedLoc = new Path(pathStr); + return resolvedLoc; + } + + /** + * Get build working directory for the provided configuration. Returns + * project location if none defined. + */ + private static IPath getBuildCWD(ICConfigurationDescription cfgDescription) { + IPath buildCWD = cfgDescription.getBuildSetting().getBuilderCWD(); + if (buildCWD==null) { + IProject project = cfgDescription.getProjectDescription().getProject(); + buildCWD = project.getLocation(); + } else { + ICdtVariableManager mngr = CCorePlugin.getDefault().getCdtVariableManager(); + try { + // Note that IPath buildCWD holding variables is mis-constructed, + // i.e. ${workspace_loc:/path} gets split into 2 path segments + // still, MBS does that and we need to handle that + String buildPathString = buildCWD.toString(); + buildPathString = mngr.resolveValue(buildPathString, "", null, cfgDescription); //$NON-NLS-1$ + buildCWD = new Path(buildPathString); + } catch (CdtVariableException e) { + CCorePlugin.log(e); + } + + } + buildCWD = buildCWD.addTrailingSeparator(); + return buildCWD; + } + + /** + * Resolve location to file system location in a configuration context. + * Resolving includes replacing build/environment variables with values, making relative path absolute etc. + * + * @param location - location to resolve. If relative, it is taken to be rooted in build working directory. + * @param cfgDescription - the configuration context. + * @return resolved file system location. + */ + private static String resolveEntry(String location, ICConfigurationDescription cfgDescription) { + // Substitute build/environment variables + ICdtVariableManager varManager = CCorePlugin.getDefault().getCdtVariableManager(); + try { + location = varManager.resolveValue(location, "", null, cfgDescription); //$NON-NLS-1$ + } catch (CdtVariableException e) { + // Swallow exceptions but also log them + CCorePlugin.log(e); + } + // use OS file separators (i.e. '\' on Windows) + if (java.io.File.separatorChar != '/') { + location = location.replace('/', java.io.File.separatorChar); + } + + // note that we avoid using org.eclipse.core.runtime.Path for manipulations being careful + // to preserve "../" segments and not let collapsing them which is not correct for symbolic links. + Path locPath = new Path(location); + if (locPath.isAbsolute() && locPath.getDevice()==null) { + // prepend device (C:) for Windows + IPath buildCWD = getBuildCWD(cfgDescription); + String device = buildCWD.getDevice(); + if (device!=null) + location = device + location; + } + if (!locPath.isAbsolute()) { + // consider relative path to be from build working directory + IPath buildCWD = getBuildCWD(cfgDescription); + location = buildCWD.toOSString() + locPath; + } + return location; + } + + /** + * Convert the path entries to absolute file system locations represented as String array. + * Resolve the entries which are not resolved. + * + * @param entriesPath - language settings path entries. + * @param cfgDescription - configuration description for resolving entries. + * @return array of the locations. + */ + private String[] convertToLocations(LinkedHashSet entriesPath, ICConfigurationDescription cfgDescription){ + List locations = new ArrayList(entriesPath.size()); + for (ICLanguageSettingEntry entry : entriesPath) { + ACPathEntry entryPath = (ACPathEntry)entry; + if (entryPath.isValueWorkspacePath()) { + IPath loc = entryPath.getLocation(); + if (loc!=null) { + if (checkBit(entryPath.getFlags(), ICSettingEntry.FRAMEWORKS_MAC)) { + // handle frameworks, see IScannerInfo.getIncludePaths() + locations.add(loc.append(FRAMEWORK_HEADERS_INCLUDE).toOSString()); + locations.add(loc.append(FRAMEWORK_PRIVATE_HEADERS_INCLUDE).toOSString()); + } else { + locations.add(loc.toOSString()); + } + } + } else { + String locStr = entryPath.getName(); + if (entryPath.isResolved()) { + locations.add(locStr); + } else { + locStr = resolveEntry(locStr, cfgDescription); + if (locStr!=null) { + if (checkBit(entryPath.getFlags(), ICSettingEntry.FRAMEWORKS_MAC)) { + // handle frameworks, see IScannerInfo.getIncludePaths() + locations.add(locStr+FRAMEWORK_HEADERS_INCLUDE); + locations.add(locStr+FRAMEWORK_PRIVATE_HEADERS_INCLUDE); + } else { + locations.add(locStr); + // add relative paths again for indexer to resolve from source file location + IPath unresolvedPath = entryPath.getLocation(); + if (!unresolvedPath.isAbsolute()) { + IPath expandedPath = expandVariables(unresolvedPath, cfgDescription); + if (!expandedPath.isAbsolute()) { + locations.add(expandedPath.toOSString()); + } + } + } + } + } + } + } + + return locations.toArray(new String[locations.size()]); + } + + private static boolean checkBit(int flags, int bit) { + return (flags & bit) == bit; + } + + @Override + public void subscribe(IResource resource, IScannerInfoChangeListener listener) { + // Handled by ScannerInfoProviderProxy for the moment + } + + @Override + public void unsubscribe(IResource resource, IScannerInfoChangeListener listener) { + // Handled by ScannerInfoProviderProxy for the moment + } + +} diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsSerializableStorage.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsSerializableStorage.java new file mode 100644 index 00000000000..c8fe2a73a99 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/language/settings/providers/LanguageSettingsSerializableStorage.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.language.settings.providers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; +import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; +import org.eclipse.cdt.core.settings.model.ICSettingEntry; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.core.settings.model.util.LanguageSettingEntriesSerializer; +import org.eclipse.cdt.internal.core.XmlUtil; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * The class representing persistent storage for language settings entries {@link ICLanguageSettingEntry}. + */ +public class LanguageSettingsSerializableStorage extends LanguageSettingsStorage { + private static final String ELEM_LANGUAGE = "language"; //$NON-NLS-1$ + private static final String ATTR_LANGUAGE_ID = "id"; //$NON-NLS-1$ + private static final String ELEM_RESOURCE = "resource"; //$NON-NLS-1$ + private static final String ATTR_PROJECT_PATH = "project-relative-path"; //$NON-NLS-1$ + + private static final String ELEM_ENTRY = LanguageSettingsExtensionManager.ELEM_ENTRY; + private static final String ATTR_ENTRY_KIND = LanguageSettingsExtensionManager.ATTR_ENTRY_KIND; + private static final String ATTR_ENTRY_NAME = LanguageSettingsExtensionManager.ATTR_ENTRY_NAME; + private static final String ATTR_ENTRY_VALUE = LanguageSettingsExtensionManager.ATTR_ENTRY_VALUE; + private static final String ELEM_ENTRY_FLAG = LanguageSettingsExtensionManager.ELEM_ENTRY_FLAG; + + /** + * Serialize the provider entries under parent XML element. + * + * @param elementProvider - element where to serialize the entries. + */ + public void serializeEntries(Element elementProvider) { + synchronized (fStorage) { + for (Entry>> entryLang : fStorage.entrySet()) { + serializeLanguage(elementProvider, entryLang.getKey(), entryLang.getValue()); + } + } + } + + /** + * Serialize the provider entries for a given language list. + */ + private void serializeLanguage(Element parentElement, String langId, Map> langMap) { + if (langId!=null) { + Element elementLanguage = XmlUtil.appendElement(parentElement, ELEM_LANGUAGE, new String[] {ATTR_LANGUAGE_ID, langId}); + parentElement = elementLanguage; + } + for (Entry> entryRc : langMap.entrySet()) { + serializeResource(parentElement, entryRc.getKey(), entryRc.getValue()); + } + } + + /** + * Serialize the provider entries for a given resource list. + */ + private void serializeResource(Element parentElement, String rcProjectPath, List rcList) { + if (rcProjectPath!=null) { + Element elementRc = XmlUtil.appendElement(parentElement, ELEM_RESOURCE, new String[] {ATTR_PROJECT_PATH, rcProjectPath}); + parentElement = elementRc; + } + serializeSettingEntries(parentElement, rcList); + } + + /** + * Serialize given settings entries. + */ + private void serializeSettingEntries(Element parentElement, List settingEntries) { + for (ICLanguageSettingEntry entry : settingEntries) { + Element elementSettingEntry = XmlUtil.appendElement(parentElement, ELEM_ENTRY, new String[] { + ATTR_ENTRY_KIND, LanguageSettingEntriesSerializer.kindToString(entry.getKind()), + ATTR_ENTRY_NAME, entry.getName(), + }); + switch (entry.getKind()) { + case ICSettingEntry.MACRO: + elementSettingEntry.setAttribute(ATTR_ENTRY_VALUE, entry.getValue()); + break; +// case ICLanguageSettingEntry.LIBRARY_FILE: +// // YAGNI: sourceAttachment fields may need to be covered +// break; + } + int flags = entry.getFlags(); + if (flags != 0) { + // Element elementFlag = + XmlUtil.appendElement(elementSettingEntry, ELEM_ENTRY_FLAG, new String[] { + ATTR_ENTRY_VALUE, LanguageSettingEntriesSerializer.composeFlagsString(entry.getFlags()) + }); + } + } + } + + /** + * Load provider entries from XML provider element. + * + * @param providerNode - parent XML element "provider" where entries are defined. + */ + public void loadEntries(Element providerNode) { + List settings = new ArrayList(); + NodeList nodes = providerNode.getChildNodes(); + for (int i=0;i 0) { + setSettingEntries(null, null, settings); + } + } + + /** + * Load a setting entry from XML element. + */ + private ICLanguageSettingEntry loadSettingEntry(Node parentElement) { + String settingKind = XmlUtil.determineAttributeValue(parentElement, ATTR_ENTRY_KIND); + String settingName = XmlUtil.determineAttributeValue(parentElement, ATTR_ENTRY_NAME); + + NodeList flagNodes = parentElement.getChildNodes(); + int flags = 0; + for (int i=0;i settings = new ArrayList(); + NodeList nodes = parentNode.getChildNodes(); + for (int i=0;i 0) { + setSettingEntries(null, langId, settings); + } + } + + /** + * Load entries defined in resource element. + */ + private void loadResourceElement(Node parentNode, String cfgId, String langId) { + String rcProjectPath = XmlUtil.determineAttributeValue(parentNode, ATTR_PROJECT_PATH); + + List settings = new ArrayList(); + NodeList nodes = parentNode.getChildNodes(); + for (int i=0;i 0) { + setSettingEntries(rcProjectPath, langId, settings); + } + } + + @Override + public LanguageSettingsSerializableStorage clone() throws CloneNotSupportedException { + return (LanguageSettingsSerializableStorage) super.clone(); + } + +} 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 416bce252a1..33cc04432cc 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 @@ -11,15 +11,21 @@ *******************************************************************************/ package org.eclipse.cdt.internal.core.settings.model; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsBroadcastingProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsManager; +import org.eclipse.cdt.core.language.settings.providers.LanguageSettingsStorage; import org.eclipse.cdt.core.settings.model.CExternalSetting; import org.eclipse.cdt.core.settings.model.ICBuildSetting; import org.eclipse.cdt.core.settings.model.ICConfigExtensionReference; @@ -37,6 +43,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.LanguageSettingsDelta; import org.eclipse.cdt.utils.envvar.StorableEnvironment; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.QualifiedName; @@ -87,6 +94,10 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ // private CConfigBasedDescriptor fDescriptor; // private Map fExternalSettingsProviderMap; + private List fLanguageSettingsProviders = new ArrayList(0); + private LinkedHashMap lspPersistedState = new LinkedHashMap(); + + private class DeltaSet { public Set extSet; public Set idSet; @@ -977,4 +988,33 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ public void updateExternalSettingsProviders(String[] ids){ ExtensionContainerFactory.updateReferencedProviderIds(fCfg, ids); } + + /** + * Returns delta and updates last persisted state to the new state. + * That implies that the delta needs to be used to fire an event of it will + * be lost. + */ + public LanguageSettingsDelta dropDelta() { + LanguageSettingsDelta languageSettingsDelta = null; + LinkedHashMap newState = new LinkedHashMap(); + for (ILanguageSettingsProvider provider : fLanguageSettingsProviders) { + if (LanguageSettingsManager.isWorkspaceProvider(provider)) { + provider = LanguageSettingsManager.getRawProvider(provider); + } + if (provider instanceof ILanguageSettingsBroadcastingProvider) { + LanguageSettingsStorage store = ((ILanguageSettingsBroadcastingProvider) provider).copyStorage(); + // avoid triggering event if empty provider was added + if (store != null && !store.isEmpty()) { + newState.put(provider.getId(), store); + } + } + } + if (!newState.equals(lspPersistedState)) { + languageSettingsDelta = new LanguageSettingsDelta(lspPersistedState, newState); + lspPersistedState = newState; + } + + return languageSettingsDelta; + } + } diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties index a37c844b3f2..38f30f17a08 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/SettingsModelMessages.properties @@ -15,8 +15,8 @@ CConfigurationDescription.0=data was not created CConfigurationDescription.1=expected proxy of type ICFileDescription, but was CConfigurationDescription.2=data was not created CConfigurationDescription.3=expected proxy of type ICFolderDescription, but was -CConfigurationStatus.1=configurations settings invalid CConfigurationDescriptionCache.0=description is read only +CConfigurationStatus.1=configurations settings invalid CProjectConverterDesciptor.0=illegal provider implementation CProjectConverterDesciptor.1=no provider defined CProjectDescriptionManager.1=required build system is not installed @@ -49,5 +49,8 @@ CExternalSettingsManager.3=writable ref info is requested for the read only conf CfgExportSettingContainerFactory.2=invalid id: project name not specified ExtensionContainerFactory.4=invalid setting provider class specified ExtensionContainerFactory.5=provider element not specified +LanguageSettingsBaseProvider.CanBeConfiguredOnlyOnce=LanguageSettingsBaseProvider can be configured only once +LanguageSettingsScannerInfoProvider.UnableToDetermineLanguage=Error getting ScannerInfo: Unable to determine language for resource {0} SettingsContext.0=no project associated with the context SettingsContext.1=can not accept the not-context project description + diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageSettingsChangeListener.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageSettingsChangeListener.java new file mode 100644 index 00000000000..bc1846adba7 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/LanguageSettingsChangeListener.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2011, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.pdom; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeEvent; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsChangeListener; +import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; +import org.eclipse.cdt.core.settings.model.ICProjectDescription; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; + +/** + * This class handles changes in language settings for the PDOM. + */ +public class LanguageSettingsChangeListener implements ILanguageSettingsChangeListener { + private PDOMManager fManager; + + /** + * Constructor. + * + * @param manager - PDOM manager. + */ + public LanguageSettingsChangeListener(PDOMManager manager) { + fManager = manager; + } + + @Override + public void handleEvent(ILanguageSettingsChangeEvent event) { + IWorkspaceRoot wspRoot = ResourcesPlugin.getWorkspace().getRoot(); + IProject project = wspRoot.getProject(event.getProjectName()); + + if (project != null) { + ICProjectDescription prjDescription = CCorePlugin.getDefault().getProjectDescription(project); + if (prjDescription != null) { + ICConfigurationDescription indexedCfgDescription = prjDescription.getDefaultSettingConfiguration(); + String indexedCfgId = indexedCfgDescription.getId(); + + for (String cfgId : event.getConfigurationDescriptionIds()) { + if (cfgId.equals(indexedCfgId)) { + fManager.handlePostBuildEvent(); + return; + } + } + } + } + } +} diff --git a/core/org.eclipse.cdt.core/plugin.properties b/core/org.eclipse.cdt.core/plugin.properties index af08cf10ff5..58aae223c1e 100644 --- a/core/org.eclipse.cdt.core/plugin.properties +++ b/core/org.eclipse.cdt.core/plugin.properties @@ -26,6 +26,7 @@ ErrorParser.name=Error Parser BinaryParser.name=Binary Parser PathEntryStore.name=Path Entry Store ScannerInfoProvider.name=Scanner Information Provider +LanguageSettingsProvider.name=Language Settings Provider CIndexer.name= C/C++ Indexer language.name= CDT Language diff --git a/core/org.eclipse.cdt.core/plugin.xml b/core/org.eclipse.cdt.core/plugin.xml index 1104c07a709..c4e1a386479 100644 --- a/core/org.eclipse.cdt.core/plugin.xml +++ b/core/org.eclipse.cdt.core/plugin.xml @@ -655,6 +655,7 @@ + + + + + + + + + This extension point is used to contribute a new Language Settings Provider. A Language Settings Provider is used to get additions to compiler options such as include paths (-I) or preprocessor defines (-D) and others into the project model. + + + + + + + + + + This extension point is used to contribute a new Language Settings Provider. A Language Settings Provider is used to get additions to compiler options such as include paths (-I) or preprocessor defines (-D) and others into the project model. + + + + + + + + + + + + + + + + + ID of the extension point, not used + + + + + + + Name of the extension point, not used + + + + + + + + + + + + + The definition of a language settings provider. + + + + + + + + + + + A fully qualified name of the Java class that implements <samp>org.eclipse.cdt.core.settings.model.ILanguageSettingsProvider</samp> interface. If empty, <samp>org.eclipse.cdt.core.language.settings.providers.LanguageSettingsBaseProvider</samp> is used by default which provides basic functionality defined by this extension point. +If there is a need to configure a provider in more deliberate way, attribute <samp>parameter</samp> could be used in a class extending <samp>LanguageSettingsBaseProvider</samp>. +Default constructor (constructor without arguments) of this class must be public and the package be exported in order to be able to instantiate via extension point. + + + + + + + + + + Unique ID of the provider. + + + + + + + Name of the provider. This name will be presented to a user in UI. + + + + + + + A custom parameter to initialize provider. For example, used to deliver command for GCCBuiltinSpecsDetector. + + + + + + + A flag indicating that the provider should be owned by a configuration rather than be global in workspace and shared between projects (when the choice has not been indicated yet by other means). This preference is consulted in order to initially set the "shared" attribute, for example when a user adds a provider from the list of extension providers. +The value "true" of this attribute is meaningful only for providers capable of being non-shared, i.e. providers extending ILanguageSettingsEditableProvider. + + + + + + + + + + The definition of language scope. Includes the list of language ID this provider is applicable to. If a language scope is not present, the provider will provide for any language. + + + + + + + ID of the language for which this provider provides the entries. As an example, those are languages contributed by CDT (see extension org.eclipse.cdt.core.language): +<p>- "<samp>org.eclipse.cdt.core.gcc</samp>" for C,</p> +<p>- "<samp>org.eclipse.cdt.core.g++</samp>" for C++.</p> + + + + + + + + + + The Language Settings Entries used to provide additions to compiler options such as include paths (-I) or preprocessor defines (-D) and others into the project model. + + + + + + + + + + Kind of language settings entry which maps to compiler options. For example, following mapping is used for gcc options: + +<br>"<samp>-I</samp>" : includePath +<br>"<samp>-D</samp>" : macro +<br>"<samp>-include</samp>" : includeFile +<br>"<samp>-L</samp>" : libraryPath +<br>"<samp>-l</samp>" : libraryFile +<br>"<samp>-imacros</samp>" : macroFile + + + + + + + + + + + + + + + + + + + + + + + "name" attribute maps to path for the entries representing a path to a folder or file and to name for <samp>macro</samp> kind representing name-value pair. For example: +<br>"<samp>/usr/include/</samp>" +<br>"<samp>MACRO</samp>" (for <samp>#define MACRO value</samp>) +<br>Note that relative paths are treated as rooted in build working directory (when applicable). + + + + + + + "value" attribute is used for <samp>macro</samp> kind representing name-value pair only. It is not used for the entries representing a path. For example: +<br>"<samp>value</samp>" (for <samp>#define MACRO value</samp>) + + + + + + + + + + Combination of flags for the entry. + + + + + + + A value of the flag. Corresponds to <samp>ICSettingEntry</samp> flags, see JavaDoc there for more details. Here is an excerpt from the Javadoc for the flags intended to be used with this extension point (the others might be not supported): + <br>- <samp>BUILTIN</samp> : Indicates settings built in a tool (compiler) itself. That kind of settings are not passed as options to a compiler but indexer or other clients might need them. + <br>- <samp>LOCAL</samp> : Applicable for <samp>includePath</samp> only which could be local (#include "...") or system (#include <...>). If an <samp>includePath</samp> is not marked as <samp>LOCAL</samp> it is treated as system. + <br>- <samp>RESOLVED</samp> : Indicates that the entries do not need to be resolved such as expansion of environment variables, normalizing the path against build working directory etc. + <br>- <samp>VALUE_WORKSPACE_PATH</samp> : is used to indicate that the entry is a resource managed by eclipse in the workspace. The path is rooted in the workspace root. + <br>- <samp>UNDEFINED</samp> : indicates that the entry should not be defined, corresponds to <samp>-U</samp> option of gcc compiler. If this flag is defined it will negate entries with the same name (and kind) for all providers down the list. + + + + + + + + + + + + + + + + + + + + + + + + + + CDT 9.0 + + + + + + + + + [Enter extension point usage example here.] + + + + + + + + + Plug-ins that want to extend this extension point must implement <samp>org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider</samp> interface. +<br/> +For those cases where contributed settings entries (representing the compiler options) are not changed dynamically it is sufficient to configure existing class LanguageSettingsBaseProvider which is provided by default. +<br/> + + + + + + + + + [Enter information about supplied implementation of this extension point.] + + + + + + + + + Copyright (c) 2009, 2011 Andrew Gvozdev and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html + + + + diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/AbstractExecutableExtensionBase.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/AbstractExecutableExtensionBase.java new file mode 100644 index 00000000000..0da0d9629ca --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/AbstractExecutableExtensionBase.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.core; + +/** + * Helper abstract class serving as a base for creating a frame of executable class + * defined as an extension in plugin.xml. + * + * @since 5.4 + * + */ +public abstract class AbstractExecutableExtensionBase { + private String fId; + private String fName; + + /** + * Default constructor will initialize with the name of the class + * using reflection mechanism. + */ + public AbstractExecutableExtensionBase() { + fName = this.getClass().getSimpleName(); + fId = this.getClass().getCanonicalName(); + } + + /** + * Constructor to initialize with ID and name of the extension. + * + * @param id - ID of the extension. + * @param name - name of the extension. + */ + public AbstractExecutableExtensionBase(String id, String name) { + fName = name; + fId = id; + } + + /** + * Set extension ID. + * + * @param id of extension + */ + public void setId(String id) { + fId = id; + } + + /** + * Set extension name. + * + * @param name of extension + */ + public void setName(String name) { + fName = name; + } + + /** + * @return id of extension + */ + public String getId() { + return fId; + } + + /** + * @return name of extension + */ + public String getName() { + return fName; + } + + /** + * Method toString() for debugging purposes. + */ + @SuppressWarnings("nls") + @Override + public String toString() { + return "id="+fId+", name="+fName; + } +}