diff --git a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java index 27e40a598f2..cf6aea5f211 100644 --- a/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java +++ b/build/org.eclipse.cdt.managedbuilder.core.tests/tests/org/eclipse/cdt/managedbuilder/language/settings/providers/tests/BuiltinSpecsDetectorTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010, 2012 Andrew Gvozdev and others. + * Copyright (c) 2010, 2013 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 @@ -8,7 +8,7 @@ * Contributors: * Andrew Gvozdev - Initial API and implementation *******************************************************************************/ - package org.eclipse.cdt.managedbuilder.language.settings.providers.tests; +package org.eclipse.cdt.managedbuilder.language.settings.providers.tests; import java.io.IOException; import java.io.OutputStream; @@ -18,7 +18,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.ICommandLauncher; +import org.eclipse.cdt.core.envvar.EnvironmentVariable; +import org.eclipse.cdt.core.envvar.IContributedEnvironment; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvider; +import org.eclipse.cdt.core.language.settings.providers.ILanguageSettingsProvidersKeeper; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.settings.model.CIncludeFileEntry; import org.eclipse.cdt.core.settings.model.CIncludePathEntry; @@ -34,7 +41,11 @@ import org.eclipse.cdt.core.settings.model.ICSettingEntry; 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.envvar.EnvironmentVariableManager; +import org.eclipse.cdt.internal.core.envvar.UserDefinedEnvironmentSupplier; +import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager; import org.eclipse.cdt.managedbuilder.language.settings.providers.AbstractBuiltinSpecsDetector; +import org.eclipse.cdt.utils.envvar.StorableEnvironment; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -54,15 +65,18 @@ public class BuiltinSpecsDetectorTest extends BaseTestCase { private static final String CUSTOM_COMMAND_1 = "echo 1"; private static final String CUSTOM_COMMAND_2 = "echo 2"; private static final String ELEM_TEST = "test"; + private static final String ENV_SAMPLE = "SAMPLE"; + private static final String ENV_SAMPLE_VALUE_1 = "Sample Value 1"; + private static final String ENV_SAMPLE_VALUE_2 = "Sample Value 2"; // those attributes must match that in AbstractBuiltinSpecsDetector private static final String ATTR_PARAMETER = "parameter"; //$NON-NLS-1$ private static final String ATTR_CONSOLE = "console"; //$NON-NLS-1$ /** - * Mock built-in specs detector to test basic functionality of {@link AbstractBuiltinSpecsDetector}. + * Dummy to keep boilerplate code. */ - private class MockBuiltinSpecsDetector extends AbstractBuiltinSpecsDetector { + private class DummyBuiltinSpecsDetector extends AbstractBuiltinSpecsDetector { @Override protected List parseOptions(String line) { return null; @@ -75,7 +89,12 @@ public class BuiltinSpecsDetectorTest extends BaseTestCase { protected String getCompilerCommand(String languageId) { return null; } + } + /** + * Mock built-in specs detector to test basic functionality of {@link AbstractBuiltinSpecsDetector}. + */ + private class MockBuiltinSpecsDetector extends DummyBuiltinSpecsDetector { @Override protected void startupForLanguage(String languageId) throws CoreException { super.startupForLanguage(languageId); @@ -89,20 +108,7 @@ public class BuiltinSpecsDetectorTest extends BaseTestCase { /** * Mock built-in specs detector to test execute() functionality. */ - private class MockBuiltinSpecsDetectorExecutedFlag extends AbstractBuiltinSpecsDetector { - @Override - protected List parseOptions(String line) { - return null; - } - @Override - protected AbstractOptionParser[] getOptionParsers() { - return null; - } - @Override - protected String getCompilerCommand(String languageId) { - return null; - } - + private class MockBuiltinSpecsDetectorExecutedFlag extends DummyBuiltinSpecsDetector { @Override protected void execute() { super.execute(); @@ -116,6 +122,42 @@ public class BuiltinSpecsDetectorTest extends BaseTestCase { } } + /** + * Mock built-in specs detector to test environment change functionality. + */ + private class MockBuiltinSpecsDetectorEnvironmentChangeListener extends DummyBuiltinSpecsDetector { + private String sampleEnvVarValue = null; + + @Override + protected boolean validateEnvironment() { + return false; + } + @Override + protected void execute() { + super.execute(); + sampleEnvVarValue = environmentMap.get(ENV_SAMPLE); + } + @Override + public MockBuiltinSpecsDetectorEnvironmentChangeListener cloneShallow() throws CloneNotSupportedException { + MockBuiltinSpecsDetectorEnvironmentChangeListener clone = (MockBuiltinSpecsDetectorEnvironmentChangeListener) super.cloneShallow(); + clone.sampleEnvVarValue = sampleEnvVarValue; + return clone; + } + @Override + public MockBuiltinSpecsDetectorEnvironmentChangeListener clone() throws CloneNotSupportedException { + MockBuiltinSpecsDetectorEnvironmentChangeListener clone = (MockBuiltinSpecsDetectorEnvironmentChangeListener) super.clone(); + clone.sampleEnvVarValue = sampleEnvVarValue; + return clone; + } + + protected boolean isExecuted() { + return isExecuted; + } + public String getSampleEnvVar() { + return sampleEnvVarValue; + } + } + /** * Mock built-in specs detector to test parsing functionality. */ @@ -508,6 +550,260 @@ public class BuiltinSpecsDetectorTest extends BaseTestCase { assertEquals(expected, entries.get(0)); } + /** + * Test environment changes for provider registered to configuration. + */ + public void testAbstractBuiltinSpecsDetector_EnvChangesConfiguration_1() throws Exception { + // Create model project and accompanied descriptions + String projectName = getName(); + IProject project = ResourceHelper.createCDTProjectWithConfig(projectName); + ICConfigurationDescription[] cfgDescriptions = getConfigurationDescriptions(project); + ICConfigurationDescription cfgDescription = cfgDescriptions[0]; + + // Create provider + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = new MockBuiltinSpecsDetectorEnvironmentChangeListener(); + // register environment listener on configuration - note that provider is not included in the configuration + provider.registerListener(cfgDescription); + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + assertEquals(true, provider.isExecuted()); + assertEquals(null, provider.getSampleEnvVar()); + // unset "isExecuted" flag + provider.clear(); + assertEquals(false, provider.isExecuted()); + assertEquals(null, provider.getSampleEnvVar()); + + // Set an environment variable to the configuration + { + ICProjectDescription prjDescriptionWritable = CoreModel.getDefault().getProjectDescription(project, true); + ICConfigurationDescription cfgDescriptionWritable = prjDescriptionWritable.getActiveConfiguration(); + // create and set sample environment variable in the configuration + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + + // Set an environment variable + IEnvironmentVariable var = new EnvironmentVariable(ENV_SAMPLE, ENV_SAMPLE_VALUE_1); + contribEnv.addVariable(var, cfgDescriptionWritable); + assertEquals(var, envManager.getVariable(ENV_SAMPLE, cfgDescriptionWritable, true)); + + CoreModel.getDefault().setProjectDescription(project, prjDescriptionWritable); + } + + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + // check if provider got executed with new value + assertEquals(true, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_1, provider.getSampleEnvVar()); + + // Repeat one more time with different value of environment variable + + // unset "isExecuted" flag + provider.clear(); + assertEquals(false, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_1, provider.getSampleEnvVar()); + + // Set an environment variable to the configuration + { + ICProjectDescription prjDescriptionWritable = CoreModel.getDefault().getProjectDescription(project, true); + ICConfigurationDescription cfgDescriptionWritable = prjDescriptionWritable.getActiveConfiguration(); + // create and set sample environment variable in the configuration + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + + // Set an environment variable + IEnvironmentVariable var = new EnvironmentVariable(ENV_SAMPLE, ENV_SAMPLE_VALUE_2); + contribEnv.addVariable(var, cfgDescriptionWritable); + assertEquals(var, envManager.getVariable(ENV_SAMPLE, cfgDescriptionWritable, true)); + + CoreModel.getDefault().setProjectDescription(project, prjDescriptionWritable); + } + + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + // check if provider got executed with new value + assertEquals(true, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_2, provider.getSampleEnvVar()); + + // unregister listeners + provider.unregisterListener(); + } + + /** + * Test running on environment changes as provider assigned to a configuration. + */ + public void testAbstractBuiltinSpecsDetector_EnvChangesConfiguration_2() throws Exception { + // Create a project with one configuration + IProject project = ResourceHelper.createCDTProjectWithConfig(getName()); + + // Assign a provider to configuration + { + ICProjectDescription prjDescriptionWritable = CoreModel.getDefault().getProjectDescription(project, true); + ICConfigurationDescription cfgDescriptionWritable = prjDescriptionWritable.getActiveConfiguration(); + // Create provider + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = new MockBuiltinSpecsDetectorEnvironmentChangeListener(); + List providers = new ArrayList(); + providers.add(provider); + ((ILanguageSettingsProvidersKeeper) cfgDescriptionWritable).setLanguageSettingProviders(providers); + // Write to project description + CProjectDescriptionManager.getInstance().setProjectDescription(project, prjDescriptionWritable); + + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + // Check that provider got executed + assertEquals(true, provider.isExecuted()); + assertEquals(null, provider.getSampleEnvVar()); + } + + // Set environment variable to the configuration + { + ICProjectDescription prjDescriptionWritable = CoreModel.getDefault().getProjectDescription(project, true); + ICConfigurationDescription cfgDescriptionWritable = prjDescriptionWritable.getActiveConfiguration(); + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + + // Set an environment variable + IEnvironmentVariable var = new EnvironmentVariable(ENV_SAMPLE, ENV_SAMPLE_VALUE_1); + contribEnv.addVariable(var, cfgDescriptionWritable); + assertEquals(var, envManager.getVariable(ENV_SAMPLE, cfgDescriptionWritable, true)); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescriptionWritable).getLanguageSettingProviders(); + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = (MockBuiltinSpecsDetectorEnvironmentChangeListener) providers.get(0); + // unset "isExecuted" flag + provider.clear(); + assertEquals(false, provider.isExecuted()); + assertEquals(null, provider.getSampleEnvVar()); + + // Save project description including saving environment to the configuration + CoreModel.getDefault().setProjectDescription(project, prjDescriptionWritable); + } + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + + // Check if the provider got executed + { + // check if environment variable got there + ICProjectDescription prjDescription = CoreModel.getDefault().getProjectDescription(project, false); + ICConfigurationDescription cfgDescription = prjDescription.getActiveConfiguration(); + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IEnvironmentVariable var = envManager.getVariable(ENV_SAMPLE, cfgDescription, true); + assertNotNull(var); + assertEquals(ENV_SAMPLE_VALUE_1, var.getValue()); + + // check if provider got executed with new value + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = (MockBuiltinSpecsDetectorEnvironmentChangeListener) providers.get(0); + assertEquals(true, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_1, provider.getSampleEnvVar()); + } + + // Repeat one more time with different value of environment variable + // Set another environment variable to the configuration + { + ICProjectDescription prjDescriptionWritable = CoreModel.getDefault().getProjectDescription(project, true); + ICConfigurationDescription cfgDescriptionWritable = prjDescriptionWritable.getActiveConfiguration(); + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + + // Set an environment variable + IEnvironmentVariable var = new EnvironmentVariable(ENV_SAMPLE, ENV_SAMPLE_VALUE_2); + contribEnv.addVariable(var, cfgDescriptionWritable); + assertEquals(var, envManager.getVariable(ENV_SAMPLE, cfgDescriptionWritable, true)); + + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescriptionWritable).getLanguageSettingProviders(); + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = (MockBuiltinSpecsDetectorEnvironmentChangeListener) providers.get(0); + // unset "isExecuted" flag + provider.clear(); + assertEquals(false, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_1, provider.getSampleEnvVar()); + + // Save project description including saving environment to the configuration + CoreModel.getDefault().setProjectDescription(project, prjDescriptionWritable); + } + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + + // Check if the provider got executed + { + // check if environment variable got there + ICProjectDescription prjDescription = CoreModel.getDefault().getProjectDescription(project, false); + ICConfigurationDescription cfgDescription = prjDescription.getActiveConfiguration(); + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IEnvironmentVariable var = envManager.getVariable(ENV_SAMPLE, cfgDescription, true); + assertNotNull(var); + assertEquals(ENV_SAMPLE_VALUE_2, var.getValue()); + + // check if provider got executed with new value + List providers = ((ILanguageSettingsProvidersKeeper) cfgDescription).getLanguageSettingProviders(); + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = (MockBuiltinSpecsDetectorEnvironmentChangeListener) providers.get(0); + assertEquals(true, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_2, provider.getSampleEnvVar()); + } + } + + /** + * Test running on environment changes as global provider on workspace level. + */ + public void testAbstractBuiltinSpecsDetector_EnvChangesGlobal() throws Exception { + // Create provider + MockBuiltinSpecsDetectorEnvironmentChangeListener provider = new MockBuiltinSpecsDetectorEnvironmentChangeListener(); + // register environment listener on workspace + provider.registerListener(null); + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + assertEquals(true, provider.isExecuted()); + assertEquals(null, provider.getSampleEnvVar()); + // unset "isExecuted" flag + provider.clear(); + assertEquals(false, provider.isExecuted()); + assertEquals(null, provider.getSampleEnvVar()); + + // create and set sample environment variable in the workspace + UserDefinedEnvironmentSupplier fUserSupplier = EnvironmentVariableManager.fUserSupplier; + StorableEnvironment vars = fUserSupplier.getWorkspaceEnvironmentCopy(); + vars.createVariable(ENV_SAMPLE, ENV_SAMPLE_VALUE_1); + fUserSupplier.setWorkspaceEnvironment(vars); + + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + + // check if provider got executed with new value + assertEquals(true, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_1, provider.getSampleEnvVar()); + + provider.clear(); + // create and set sample environment variable in the workspace + vars.deleteAll(); + vars.createVariable(ENV_SAMPLE, ENV_SAMPLE_VALUE_2); + fUserSupplier.setWorkspaceEnvironment(vars); + + try { + Job.getJobManager().join(AbstractBuiltinSpecsDetector.JOB_FAMILY_BUILTIN_SPECS_DETECTOR, null); + } catch (Exception e) { + } + // check if provider got executed with new value + assertEquals(true, provider.isExecuted()); + assertEquals(ENV_SAMPLE_VALUE_2, provider.getSampleEnvVar()); + + // unregister listeners + provider.unregisterListener(); + } + /** * Check that entries get grouped by kinds by stock built-in specs detector. */ diff --git a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java index b67eb9c7a56..737e0135a19 100644 --- a/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java +++ b/build/org.eclipse.cdt.managedbuilder.core/src/org/eclipse/cdt/managedbuilder/language/settings/providers/AbstractBuiltinSpecsDetector.java @@ -46,9 +46,12 @@ import org.eclipse.cdt.core.settings.model.ICLanguageSettingEntry; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.internal.core.BuildRunnerHelper; import org.eclipse.cdt.internal.core.XmlUtil; +import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.managedbuilder.core.ManagedBuilderCorePlugin; import org.eclipse.cdt.managedbuilder.internal.core.ManagedMakeMessages; import org.eclipse.cdt.utils.CommandLineUtil; +import org.eclipse.cdt.utils.envvar.IEnvironmentChangeEvent; +import org.eclipse.cdt.utils.envvar.IEnvironmentChangeListener; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; @@ -83,7 +86,9 @@ import org.w3c.dom.Element; * * @since 8.1 */ -public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSettingsOutputScanner implements ICListenerAgent { +public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSettingsOutputScanner + implements ICListenerAgent, IEnvironmentChangeListener { + public static final String JOB_FAMILY_BUILTIN_SPECS_DETECTOR = "org.eclipse.cdt.managedbuilder.AbstractBuiltinSpecsDetector"; //$NON-NLS-1$ protected static final String COMPILER_MACRO = "${COMMAND}"; //$NON-NLS-1$ @@ -345,11 +350,21 @@ public abstract class AbstractBuiltinSpecsDetector extends AbstractLanguageSetti @Override public void registerListener(ICConfigurationDescription cfgDescription) { currentCfgDescription = cfgDescription; + EnvironmentVariableManager.fUserSupplier.registerEnvironmentChangeListener(this); + execute(); } @Override public void unregisterListener() { + EnvironmentVariableManager.fUserSupplier.unregisterEnvironmentChangeListener(this); + } + + /** @since 8.2 */ + @Override + public void handleEvent(IEnvironmentChangeEvent event) { + // here comes workspace environment change + execute(); } @Override diff --git a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java index c5dcb3c01a0..c2c0ec14a46 100644 --- a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java +++ b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwEnvironmentVariableSupplier.java @@ -1,195 +1,104 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 QNX Software Systems and others. + * Copyright (c) 2006, 2013 QNX Software Systems 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: - * QNX Software Systems - Initial API and implementation + * Doug Schaefer, QNX Software Systems - Initial API and implementation + * Andrew Gvozdev - Ability to use different MinGW versions in different cfg *******************************************************************************/ package org.eclipse.cdt.managedbuilder.gnu.mingw; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; -import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.internal.core.MinGW; +import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.managedbuilder.core.IConfiguration; import org.eclipse.cdt.managedbuilder.envvar.IBuildEnvironmentVariable; import org.eclipse.cdt.managedbuilder.envvar.IConfigurationEnvironmentVariableSupplier; import org.eclipse.cdt.managedbuilder.envvar.IEnvironmentVariableProvider; -import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.cdt.managedbuilder.internal.envvar.BuildEnvVar; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; /** - * @author Doug Schaefer - * * @noextend This class is not intended to be subclassed by clients. */ public class MingwEnvironmentVariableSupplier implements IConfigurationEnvironmentVariableSupplier { private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ - - private static String envPathValueCached = null; - private static String envMingwHomeValueCached = null; - private static IPath binDir = null; - private static IPath msysBinDir = null; - - private static class MingwBuildEnvironmentVariable implements IBuildEnvironmentVariable { - private static final String PATH_SEPARATOR = ";"; //$NON-NLS-1$ - private final String name; - private final String value; - private final int operation; - - public MingwBuildEnvironmentVariable(String name, String value, int operation) { - this.name = name; - this.value = value; - this.operation = operation; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getValue() { - return value; - } - - @Override - public int getOperation() { - return operation; - } - - @Override - public String getDelimiter() { - return PATH_SEPARATOR; - } - } + private static final String BACKSLASH = java.io.File.separator; + private static final String PATH_DELIMITER = EnvironmentVariableManager.getDefault().getDefaultDelimiter(); /** * @return location of $MINGW_HOME/bin folder on the file-system. + * @deprecated. Deprecated as of CDT 8.2. Note that MinGW root path in general may depend on configuration. * * If you use this do not cache results to ensure user preferences are accounted for. * Please rely on internal caching. */ + @Deprecated public static IPath getBinDir() { - locateMingw(); + IPath binDir = null; + String minGWHome = MinGW.getMinGWHome(); + if (minGWHome != null) { + binDir = new Path(minGWHome).append("bin"); //$NON-NLS-1$ + } return binDir; } /** * @return location of $MINGW_HOME/msys/bin folder on the file-system. + * @deprecated. Deprecated as of CDT 8.2. Note that MinGW root path in general may depend on configuration. * * If you use this do not cache results to ensure user preferences are accounted for. * Please rely on internal caching. */ + @Deprecated public static IPath getMsysBinDir() { - locateMingw(); + IPath msysBinDir = null; + String msysHome = MinGW.getMSysHome(); + if (msysHome != null) { + msysBinDir = new Path(msysHome).append("bin"); //$NON-NLS-1$ + } return msysBinDir; } - /** - * Locate MinGW directories. The results are judicially cached so it is reasonably cheap to call. - * The reason to call it each time is to check if user changed environment in preferences. - */ - private static void locateMingw() { - IEnvironmentVariable varPath = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_PATH, null, true); - String envPathValue = varPath != null ? varPath.getValue() : null; - IEnvironmentVariable varMingwHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable("MINGW_HOME", null, true); //$NON-NLS-1$ - String envMingwHomeValue = varMingwHome != null ? varMingwHome.getValue() : null; - - if (CDataUtil.objectsEqual(envPathValue, envPathValueCached) && CDataUtil.objectsEqual(envMingwHomeValue, envMingwHomeValueCached)) { - return; - } - - envPathValueCached = envPathValue; - envMingwHomeValueCached = envMingwHomeValue; - - binDir = locateBinDir(); - msysBinDir = locateMsysBinDir(binDir); - } - - private static IPath locateBinDir() { - // Check $MINGW_HOME - IPath mingwBinDir = new Path(envMingwHomeValueCached + "\\bin"); //$NON-NLS-1$ - if (mingwBinDir.toFile().isDirectory()) { - return mingwBinDir; - } - - // Try the mingw directory in the platform install directory - // CDT distributions like Wascana may distribute MinGW like that - IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); - mingwBinDir = installPath.append("mingw\\bin"); //$NON-NLS-1$ - if (mingwBinDir.toFile().isDirectory()) { - return mingwBinDir; - } - - // Check for MinGW-w64 on Windows 64 bit, see http://mingw-w64.sourceforge.net/ - if (Platform.ARCH_X86_64.equals(Platform.getOSArch())) { - IPath gcc64Loc = PathUtil.findProgramLocation("x86_64-w64-mingw32-gcc.exe", envPathValueCached); //$NON-NLS-1$ - if (gcc64Loc != null) { - return gcc64Loc.removeLastSegments(1); - } - } - - // Look in PATH values. Look for mingw32-gcc.exe - // TODO: Since this dir is already in the PATH, why are we adding it here? - // This is really only to support isToolchainAvail. Must be a better way. - // AG: Because otherwise the toolchain won't be shown in the list of "supported" toolchains in UI - // when MinGW installed in custom location even if it is in the PATH - IPath gccLoc = PathUtil.findProgramLocation("mingw32-gcc.exe", envPathValueCached); //$NON-NLS-1$ - if (gccLoc != null) { - return gccLoc.removeLastSegments(1); - } - - // Try the default MinGW install dir - mingwBinDir = new Path("C:\\MinGW\\bin"); //$NON-NLS-1$ - if (mingwBinDir.toFile().isDirectory()) { - return mingwBinDir; - } - - return null; - } - - private static IPath locateMsysBinDir(IPath binPath) { - if (binPath != null) { - // Just look in the install location parent dir - IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); - IPath msysBinPath = installPath.append("msys\\bin"); //$NON-NLS-1$ - if (msysBinPath.toFile().isDirectory()) { - return msysBinPath; - } - - if (envMingwHomeValueCached != null) { - msysBinPath = new Path(envMingwHomeValueCached + "\\msys\\1.0\\bin"); //$NON-NLS-1$ - if (msysBinPath.toFile().isDirectory()) { - return msysBinPath; - } - } - - // Try the new MinGW msys bin dir - msysBinPath = new Path("C:\\MinGW\\msys\\1.0\\bin"); //$NON-NLS-1$ - if (msysBinPath.toFile().isDirectory()) { - return msysBinPath; - } - } - return null; - } - @Override public IBuildEnvironmentVariable getVariable(String variableName, IConfiguration configuration, IEnvironmentVariableProvider provider) { - if (variableName.equals(ENV_PATH)) { - locateMingw(); - if (binDir != null) { - String pathStr = binDir.toOSString(); - if (msysBinDir != null) { - pathStr += MingwBuildEnvironmentVariable.PATH_SEPARATOR + msysBinDir.toOSString(); + if (variableName.equals(MinGW.ENV_MINGW_HOME)) { + IEnvironmentVariable varMinGWHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(MinGW.ENV_MINGW_HOME, null, false); + if (varMinGWHome == null) { + // Contribute if the variable does not already come from workspace environment + String minGWHome = MinGW.getMinGWHome(); + if (minGWHome == null) { + // If the variable is not defined still show it in the environment variables list as a hint to user + minGWHome = ""; //$NON-NLS-1$ } - return new MingwBuildEnvironmentVariable(ENV_PATH, pathStr, IBuildEnvironmentVariable.ENVVAR_PREPEND); + return new BuildEnvVar(MinGW.ENV_MINGW_HOME, new Path(minGWHome).toOSString(), IBuildEnvironmentVariable.ENVVAR_REPLACE); } + return null; + + } else if (variableName.equals(MinGW.ENV_MSYS_HOME)) { + IEnvironmentVariable varMsysHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(MinGW.ENV_MSYS_HOME, null, false); + if (varMsysHome == null) { + // Contribute if the variable does not already come from workspace environment + String msysHome = MinGW.getMSysHome(); + if (msysHome == null) { + // If the variable is not defined still show it in the environment variables list as a hint to user + msysHome = ""; //$NON-NLS-1$ + } + return new BuildEnvVar(MinGW.ENV_MSYS_HOME, new Path(msysHome).toOSString(), IBuildEnvironmentVariable.ENVVAR_REPLACE); + } + return null; + + } else if (variableName.equals(ENV_PATH)) { + @SuppressWarnings("nls") + String path = "${" + MinGW.ENV_MINGW_HOME + "}" + BACKSLASH + "bin" + PATH_DELIMITER + + "${" + MinGW.ENV_MSYS_HOME + "}" + BACKSLASH + "bin"; + return new BuildEnvVar(ENV_PATH, path, IBuildEnvironmentVariable.ENVVAR_PREPEND); } return null; @@ -197,10 +106,11 @@ public class MingwEnvironmentVariableSupplier implements IConfigurationEnvironme @Override public IBuildEnvironmentVariable[] getVariables(IConfiguration configuration, IEnvironmentVariableProvider provider) { - IBuildEnvironmentVariable path = getVariable(ENV_PATH, configuration, provider); - return path != null - ? new IBuildEnvironmentVariable[] { path } - : new IBuildEnvironmentVariable[0]; + return new IBuildEnvironmentVariable[] { + getVariable(MinGW.ENV_MINGW_HOME, configuration, provider), + getVariable(MinGW.ENV_MSYS_HOME, configuration, provider), + getVariable(ENV_PATH, configuration, provider), + }; } } diff --git a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java index da6312958aa..52619dc0f6d 100644 --- a/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java +++ b/build/org.eclipse.cdt.managedbuilder.gnu.ui/src/org/eclipse/cdt/managedbuilder/gnu/mingw/MingwIsToolChainSupported.java @@ -1,30 +1,35 @@ /********************************************************************** - * Copyright (c) 2007, 2010 QNX Software Systems and others. + * Copyright (c) 2007, 2013 QNX Software Systems 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: - * QNX Software Systems - Initial API and implementation + * Doug Schaefer (QNX Software Systems) - Initial API and implementation + * Andrew Gvozdev - Ability to use different MinGW versions in different cfg **********************************************************************/ package org.eclipse.cdt.managedbuilder.gnu.mingw; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.internal.core.MinGW; import org.eclipse.cdt.managedbuilder.core.IManagedIsToolChainSupported; import org.eclipse.cdt.managedbuilder.core.IToolChain; +import org.eclipse.cdt.managedbuilder.internal.envvar.EnvironmentVariableManagerToolChain; import org.osgi.framework.Version; /** - * @author Doug Schaefer - * * @noextend This class is not intended to be subclassed by clients. */ public class MingwIsToolChainSupported implements IManagedIsToolChainSupported { + private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ + @Override public boolean isSupported(IToolChain toolChain, Version version, String instance) { - // Only supported if we can find the mingw bin dir to run the compiler - return MingwEnvironmentVariableSupplier.getBinDir() != null; + IEnvironmentVariable var = new EnvironmentVariableManagerToolChain(toolChain).getVariable(ENV_PATH, true); + String envPath = var != null ? var.getValue() : null; + return MinGW.isAvailable(envPath); } } diff --git a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/envvar/IEnvironmentVariableManagerTests.java b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/envvar/IEnvironmentVariableManagerTests.java index fd7535bf5e1..de9e26f3fcf 100644 --- a/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/envvar/IEnvironmentVariableManagerTests.java +++ b/core/org.eclipse.cdt.core.tests/misc/org/eclipse/cdt/core/envvar/IEnvironmentVariableManagerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2010 Broadcom Corp. and others. + * Copyright (c) 2009, 2013 Broadcom Corp. 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 @@ -11,6 +11,7 @@ package org.eclipse.cdt.core.envvar; import java.io.ByteArrayInputStream; +import java.util.Arrays; import junit.framework.Test; import junit.framework.TestCase; @@ -22,6 +23,9 @@ import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.cdt.core.testplugin.ResourceHelper; +import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; +import org.eclipse.cdt.utils.envvar.IEnvironmentChangeEvent; +import org.eclipse.cdt.utils.envvar.IEnvironmentChangeListener; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; @@ -31,6 +35,29 @@ import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; public class IEnvironmentVariableManagerTests extends TestCase { + /** + * Mock listener to listen to environment variable change events. + */ + private class MockEnvironmentListener implements IEnvironmentChangeListener { + private int count = 0; + private IEnvironmentChangeEvent lastEvent = null; + + public int getCount() { + return count; + } + public void resetCount() { + count = 0; + lastEvent = null; + } + public IEnvironmentChangeEvent getLastEvent() { + return lastEvent; + } + @Override + public void handleEvent(IEnvironmentChangeEvent event) { + count++; + lastEvent = event; + } + } @Override protected void setUp() throws Exception { @@ -639,4 +666,88 @@ public class IEnvironmentVariableManagerTests extends TestCase { assertEquals(varInvalidListValue,varInvalidList.getValue()); } + /** + * Test case to test environment variable change notifications + * + * @throws Exception + */ + public void testEnvironmentChangeListener() throws Exception { + // Register environment event listener + MockEnvironmentListener envListener = new MockEnvironmentListener(); + EnvironmentVariableManager.fUserSupplier.registerEnvironmentChangeListener(envListener); + assertEquals(0, envListener.getCount()); + + // Define sample environment variable name + String variableName = "VAR"; + + // Test adding a variable + IEnvironmentVariable[] varsAdded = { new EnvironmentVariable(variableName, "value") }; + { + // Reset the listener + envListener.resetCount(); + assertEquals(0, envListener.getCount()); + + // Add environment variable + EnvironmentVariableManager.fUserSupplier.setVariables(varsAdded, null); + EnvironmentVariableManager.fUserSupplier.storeWorkspaceEnvironment(true); + + // Doublecheck that variable added + IEnvironmentVariable[] actualVars = EnvironmentVariableManager.fUserSupplier.getVariables(null); + assertTrue(((Arrays.equals(varsAdded, actualVars)))); + + // Check event received by the listener + assertEquals(1, envListener.getCount()); + IEnvironmentChangeEvent event = envListener.getLastEvent(); + assertTrue(Arrays.equals(new IEnvironmentVariable[0], event.getOldVariables())); + assertTrue(Arrays.equals(varsAdded, event.getNewVariables())); + } + + // Test changing a variable + IEnvironmentVariable[] varsChanged = { new EnvironmentVariable(variableName, "value-changed") }; + { + // Reset the listener + envListener.resetCount(); + assertEquals(0, envListener.getCount()); + + // Add environment variable + EnvironmentVariableManager.fUserSupplier.setVariables(varsChanged, null); + EnvironmentVariableManager.fUserSupplier.storeWorkspaceEnvironment(true); + + // Doublecheck that variable added + IEnvironmentVariable[] actualVars = EnvironmentVariableManager.fUserSupplier.getVariables(null); + assertTrue(Arrays.equals(varsChanged, actualVars)); + + // Check event received by the listener + assertEquals(1, envListener.getCount()); + IEnvironmentChangeEvent event = envListener.getLastEvent(); + assertTrue(Arrays.equals(varsAdded, event.getOldVariables())); + assertTrue(Arrays.equals(varsChanged, event.getNewVariables())); + } + + // Test removing a variable + IEnvironmentVariable[] varsRemoved = {}; + { + // Reset the listener + envListener.resetCount(); + assertEquals(0, envListener.getCount()); + + // Add environment variable + EnvironmentVariableManager.fUserSupplier.setVariables(varsRemoved, null); + EnvironmentVariableManager.fUserSupplier.storeWorkspaceEnvironment(true); + + // Doublecheck that variable added + IEnvironmentVariable[] actualVars = EnvironmentVariableManager.fUserSupplier.getVariables(null); + assertTrue(Arrays.equals(varsRemoved, actualVars)); + + // Check event received by the listener + assertEquals(1, envListener.getCount()); + IEnvironmentChangeEvent event = envListener.getLastEvent(); + assertTrue(Arrays.equals(varsChanged, event.getOldVariables())); + assertTrue(Arrays.equals(varsRemoved, event.getNewVariables())); + } + + // Release environment event listener + EnvironmentVariableManager.fUserSupplier.unregisterEnvironmentChangeListener(envListener); + } + } diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java index 9d6a65a6de9..3dc6e481ff0 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2TemplateTests.java @@ -7539,10 +7539,47 @@ public class AST2TemplateTests extends AST2TestBase { // void foo(T t) { // bar(t); // } - public void testUnqualifiedFunctionCallInTemplate_402498() throws Exception { + public void testUnqualifiedFunctionCallInTemplate_402498a() throws Exception { parseAndCheckBindings(); } + // template + // auto foo(T t) -> decltype(bar(t)); + // + // namespace N { + // class A {}; + // int bar(A); + // } + // + // int main() { + // auto x = foo(N::A()); + // } + public void testUnqualifiedFunctionCallInTemplate_402498b() throws Exception { + new BindingAssertionHelper(getAboveComment(), true).assertVariableType("x", CommonTypes.int_); + } + + // template + // auto foo(T t) -> decltype(bar(t)); + // + // namespace N { + // class A {}; + // } + // + // int bar(A); + // + // int main() { + // auto x = foo(N::A()); + // } + public void testUnqualifiedFunctionCallInTemplate_402498c() throws Exception { + BindingAssertionHelper helper = new BindingAssertionHelper(getAboveComment(), true); + ICPPVariable x = helper.assertNonProblem("x", ICPPVariable.class); + // We really should assert that x's type is a ProblemType, but the semantic + // analyzer is too lenient and makes it a TypeOfDependentExpression if it + // can't instantiate the return type of foo() properly. + // That's another bug for another day. + assertFalse(x.getType().isSameType(CommonTypes.int_)); + } + // template // struct no_type {}; // diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java index ca8d20b7f90..ded306768b4 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/CommentTests.java @@ -29,14 +29,14 @@ public class CommentTests extends AST2TestBase { return suite(CommentTests.class); } - public void testCountCommentsInHeaderFile() throws ParserException{ + public void testCountCommentsInHeaderFile() throws ParserException { IASTTranslationUnit tu = parse(getHSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); assertEquals(9, comments.length); } - public void testCommentsInHeaderFile() throws ParserException{ + public void testCommentsInHeaderFile() throws ParserException { IASTTranslationUnit tu = parse(getHSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); @@ -51,14 +51,14 @@ public class CommentTests extends AST2TestBase { assertEquals("//Endcomment h", new String(comments[8].getComment())); } - public void testCountCommentsInCPPFile() throws ParserException{ + public void testCountCommentsInCPPFile() throws ParserException { IASTTranslationUnit tu = parse(getCppSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); assertEquals(10, comments.length); } - public void testCommentsInCPPFile() throws ParserException{ + public void testCommentsInCPPFile() throws ParserException { IASTTranslationUnit tu = parse(getCppSource(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); @@ -74,14 +74,14 @@ public class CommentTests extends AST2TestBase { assertEquals("//An integer", new String(comments[9].getComment())); } - public void testCountCommentsInCFile() throws ParserException{ + public void testCountCommentsInCFile() throws ParserException { IASTTranslationUnit tu = parse(getCSource(), ParserLanguage.C, false, true); IASTComment[] comments = tu.getComments(); assertEquals(4, comments.length); } - public void testCommentsInCFile() throws ParserException{ + public void testCommentsInCFile() throws ParserException { IASTTranslationUnit tu = parse(getCSource(), ParserLanguage.C, false, true); IASTComment[] comments = tu.getComments(); @@ -211,7 +211,7 @@ public class CommentTests extends AST2TestBase { } // //comment - public void testCommentLocation_bug186337() throws Exception{ + public void testCommentLocation_bug186337() throws Exception { CharSequence code= getContents(1)[0]; IASTTranslationUnit tu = parse(code.toString(), ParserLanguage.CPP, false, true); IASTComment[] comments = tu.getComments(); @@ -234,7 +234,6 @@ public class CommentTests extends AST2TestBase { // #ifdef WHATEVA // TODO: ignored // #endif // TODO: ignored // // TODO: shows up in task list - public void testCommentInDirectives_bug192546() throws Exception { CharSequence code= getContents(1)[0]; IASTTranslationUnit tu = parse(code.toString(), ParserLanguage.CPP, false, false); diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java index cda751ea9f3..2846fdcb38c 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/rewrite/comenthandler/CommentHandlingTest.java @@ -30,6 +30,7 @@ import org.eclipse.cdt.core.parser.tests.rewrite.TestSourceFile; import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter; import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.NodeCommentMap; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; /** * This test tests the behavior of the class ASTCommenter. It checks if the ASTCommenter assigns @@ -158,15 +159,17 @@ public class CommentHandlingTest extends RewriteBaseTest { return output.toString().trim(); } - private String getSignature(IASTNode actNode) { - if (actNode instanceof IASTCompositeTypeSpecifier) { - IASTCompositeTypeSpecifier comp = (IASTCompositeTypeSpecifier) actNode; + private String getSignature(IASTNode node) { + if (node instanceof IASTCompositeTypeSpecifier) { + IASTCompositeTypeSpecifier comp = (IASTCompositeTypeSpecifier) node; return comp.getName().toString(); - } else if (actNode instanceof IASTEnumerationSpecifier) { - IASTEnumerationSpecifier comp = (IASTEnumerationSpecifier) actNode; + } else if (node instanceof IASTEnumerationSpecifier) { + IASTEnumerationSpecifier comp = (IASTEnumerationSpecifier) node; return comp.getName().toString(); + } else if (node instanceof IASTTranslationUnit) { + return Path.fromOSString(node.getFileLocation().getFileName()).lastSegment(); } - return actNode.getRawSignature(); + return node.getRawSignature(); } private static String getSeparatingRegexp() { diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index b4de8014640..bad3554ca8a 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -61,7 +61,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.internal.core.dom.rewrite;x-friends:="org.eclipse.cdt.core.tests,org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.astwriter;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.changegenerator;x-internal:=true, - org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-internal:=true, + org.eclipse.cdt.internal.core.dom.rewrite.commenthandler;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.dom.rewrite.util;x-internal:=true, org.eclipse.cdt.internal.core.envvar;x-friends:="org.eclipse.cdt.ui,org.eclipse.cdt.managedbuilder.core", org.eclipse.cdt.internal.core.index;x-friends:="org.eclipse.cdt.ui", diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java index ef76745892c..440be3149c3 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTFileLocation.java @@ -37,25 +37,25 @@ public interface IASTFileLocation extends IASTNodeLocation { public int getNodeLength(); /** - * Get the starting line number. Locations obtained via the index do not have line numbers - * and return 0. + * Returns the starting line number. Locations obtained via the index do not have line numbers + * and return {@code 0}. * - * @return int representing line number or 0 if not applicable + * @return the 1-based line number, or {@code 0} if not applicable */ public int getStartingLineNumber(); /** - * Get the ending line number. Locations obtained via the index do not have line numbers - * and return 0. + * Returns the ending line number. Locations obtained via the index do not have line numbers + * and return {@code 0}. * - * @return int representing line number or 0 if not applicable + * @return the 1-based line number, or {@code 0} if not applicable */ public int getEndingLineNumber(); /** * Returns the inclusion statement that included this file, or null for * a top-level file. - * Also null when the file location does not belong to an AST node, e.g. + * Also {@code null} when the file location does not belong to an AST node, e.g. * if it is obtained from a name in the index. * @since 5.4 */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java index e6bcf447a89..c0020387e7a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTForStatement.java @@ -11,8 +11,8 @@ package org.eclipse.cdt.core.dom.ast; /** - * The for statement. The initialization clause can be an expression or a - * declaration but not both. + * The 'for' statement. The initialization clause can be an expression + * or a declaration but not both. * * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. @@ -57,8 +57,7 @@ public interface IASTForStatement extends IASTStatement { * @param statement */ public void setInitializerStatement( IASTStatement statement ); - - + /** * Get the condition expression for the loop. * diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java index 9d7934fc4aa..de2a6c26ad4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTIdExpression.java @@ -32,7 +32,7 @@ public interface IASTIdExpression extends IASTExpression, IASTNameOwner { public IASTName getName(); /** - * Set the name to be used inthe expression. + * Sets the name to be used in the expression. * * @param name */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java index 0f7ce9af3f1..998f3ada3ee 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTPreprocessorIncludeStatement.java @@ -23,16 +23,14 @@ import org.eclipse.cdt.core.parser.ISignificantMacros; */ public interface IASTPreprocessorIncludeStatement extends IASTPreprocessorStatement, IFileNomination { /** - * INCLUDE_NAME describes the relationship between an include directive and - * it's name. + * {@code INCLUDE_NAME} describes the relationship between an include directive and its name. */ public static final ASTNodeProperty INCLUDE_NAME = new ASTNodeProperty( "IASTPreprocessorMacroDefinition.INCLUDE_NAME - Include Name"); //$NON-NLS-1$ - /** - * Returns the absolute location of the file found through #include. - * Only valid if {@link #isResolved()} returns true. + * Returns the absolute location of the file found through #include, or an empty string if + * include was not resolved. */ public String getPath(); @@ -109,14 +107,14 @@ public interface IASTPreprocessorIncludeStatement extends IASTPreprocessorStatem public boolean isErrorInIncludedFile(); /** - * Returns true, if an attempt will be or has been made to create AST for the target + * Returns {@code true}, if an attempt will be or has been made to create AST for the target * of this inclusion. * @since 5.4 */ public boolean createsAST(); /** - * Returns the file from the index that this include statement has pulled in, or null + * Returns the file from the index that this include statement has pulled in, or {@code null} * if the include creates AST or is unresolved or skipped. * @since 5.4 */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java index 05d283d8ef5..2b4665d6c5b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTTranslationUnit.java @@ -32,26 +32,37 @@ import org.eclipse.core.runtime.IAdaptable; */ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomination, IAdaptable { /** - * OWNED_DECLARATION represents the relationship between an IASTTranslationUnit and - * it's nested IASTDeclaration's. + * {@code OWNED_DECLARATION} represents the relationship between an {@code IASTTranslationUnit} + * and its nested {@code IASTDeclaration}'s. */ public static final ASTNodeProperty OWNED_DECLARATION = new ASTNodeProperty( "IASTTranslationUnit.OWNED_DECLARATION - IASTDeclaration for IASTTranslationUnit"); //$NON-NLS-1$ /** - * SCANNER_PROBLEM represents the relationship between an IASTTranslationUnit and - * it's nested IASTProblem. + * {@code SCANNER_PROBLEM} represents the relationship between an {@code IASTTranslationUnit} + * and its nested {@code IASTProblem}. */ public static final ASTNodeProperty SCANNER_PROBLEM = new ASTNodeProperty( "IASTTranslationUnit.SCANNER_PROBLEM - IASTProblem (scanner caused) for IASTTranslationUnit"); //$NON-NLS-1$ /** - * PREPROCESSOR_STATEMENT represents the relationship between an IASTTranslationUnit and - * it's nested IASTPreprocessorStatement. + * {@code PREPROCESSOR_STATEMENT} represents the relationship between + * an {@code IASTTranslationUnit} and its nested {@code IASTPreprocessorStatement}. */ public static final ASTNodeProperty PREPROCESSOR_STATEMENT = new ASTNodeProperty( "IASTTranslationUnit.PREPROCESSOR_STATEMENT - IASTPreprocessorStatement for IASTTranslationUnit"); //$NON-NLS-1$ + public static final ASTNodeProperty MACRO_EXPANSION = new ASTNodeProperty( + "IASTTranslationUnit.MACRO_EXPANSION - IASTPreprocessorMacroExpansion node for macro expansions."); //$NON-NLS-1$ + + /** + * @deprecated names for macro expansions are nested inside of + * {@link IASTPreprocessorMacroExpansion}. + */ + @Deprecated + public static final ASTNodeProperty EXPANSION_NAME = new ASTNodeProperty( + "IASTTranslationUnit.EXPANSION_NAME - IASTName generated for macro expansions."); //$NON-NLS-1$ + /** * A translation unit contains an ordered sequence of declarations. * @@ -62,7 +73,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Adds declaration to translation unit. * - * @param declaration IASTDeclaration + * @param declaration {@code IASTDeclaration} */ @Override public void addDeclaration(IASTDeclaration declaration); @@ -130,7 +141,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi * The object is suitable for working in one of the files that is part of * the translation unit. * @param filePath file of interest, as returned by {@link IASTFileLocation#getFileName()}, - * or null to specify the root source of the translation-unit. + * or {@code null} to specify the root source of the translation-unit. * @return an IASTNodeSelector. * @since 5.0 */ @@ -158,8 +169,8 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi public IASTPreprocessorMacroDefinition[] getBuiltinMacroDefinitions(); /** - * Returns the include directives encountered in parsing this translation unit. This will also contain directives - * used for handling the gcc-options -imacros and -include. + * Returns the include directives encountered in parsing this translation unit. This will also + * contain directives used for handling the gcc-options -imacros and -include. *

* In case the information for a header-file is pulled in from the index, * include directives contained therein are not returned. @@ -180,7 +191,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Returns all preprocessor and scanner problems. - * @return IASTProblem[] + * @return {@code IASTProblem[]} */ public IASTProblem[] getPreprocessorProblems(); @@ -199,21 +210,11 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Flattens the node locations provided into a single file location. * - * @param nodeLocations IASTNodeLocations to flatten + * @param nodeLocations {@code IASTNodeLocation}s to flatten * @return null if not possible, otherwise, a file location representing where the macros are. */ public IASTFileLocation flattenLocationsToFile(IASTNodeLocation[] nodeLocations); - /** - * @deprecated names for macro expansions are nested inside of {@link IASTPreprocessorMacroExpansion}. - */ - @Deprecated - public static final ASTNodeProperty EXPANSION_NAME = new ASTNodeProperty( - "IASTTranslationUnit.EXPANSION_NAME - IASTName generated for macro expansions."); //$NON-NLS-1$ - - public static final ASTNodeProperty MACRO_EXPANSION = new ASTNodeProperty( - "IASTTranslationUnit.MACRO_EXPANSION - IASTPreprocessorMacroExpansion node for macro expansions."); //$NON-NLS-1$ - public static interface IDependencyTree { public String getTranslationUnitPath(); @@ -253,7 +254,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi /** * Return the set of files that have been skipped because they have been part of the index - * prior to creating this AST, or null if not available. + * prior to creating this AST, or {@code null} if not available. * Applies only, if AST was created with an index and the option to skip headers found in * the index. * @since 5.1 @@ -261,7 +262,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi IIndexFileSet getIndexFileSet(); /** - * Return the set of files in the index that are superseded by this AST, or null + * Return the set of files in the index that are superseded by this AST, or {@code null} * if not available. Applies only, if AST was created with an index. * @since 5.3 */ @@ -271,7 +272,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi * In case the AST was created in a way that supports comment parsing, all comments of * the translation unit are returned. Otherwise an empty array will be supplied. * - * @return IASTComment[] + * @return {@code IASTComment[]} * @since 4.0 */ public IASTComment[] getComments(); @@ -339,7 +340,7 @@ public interface IASTTranslationUnit extends IASTDeclarationListOwner, IFileNomi public IASTTranslationUnit copy(CopyStyle style); /** - * Returns the ITranslationUnit this AST originated from, or null if the AST + * Returns the ITranslationUnit this AST originated from, or {@code null} if the AST * does not correspond to an ITranslationUnit. * * @since 5.3 diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java index ae47d0d8b67..1b81285abbc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPASTForStatement.java @@ -15,14 +15,16 @@ import org.eclipse.cdt.core.dom.ast.IASTDeclaration; import org.eclipse.cdt.core.dom.ast.IASTForStatement; /** - * + * The C++ 'for' statement. + * * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ public interface ICPPASTForStatement extends IASTForStatement { - - public static final ASTNodeProperty CONDITION_DECLARATION = new ASTNodeProperty( "org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement"); //$NON-NLS-1$ - public void setConditionDeclaration( IASTDeclaration d ); + public static final ASTNodeProperty CONDITION_DECLARATION = + new ASTNodeProperty("org.eclipse.cdt.core.dom.ast.cpp.ICPPASTForStatement"); //$NON-NLS-1$ + + public void setConditionDeclaration(IASTDeclaration d); public IASTDeclaration getConditionDeclaration(); /** diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPBase.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPBase.java index 05e4a01f4a4..acf0cb3fab5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPBase.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPBase.java @@ -6,8 +6,8 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * IBM - Initial API and implementation - * Markus Schorn (Wind River Systems) + * IBM - Initial API and implementation + * Markus Schorn (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.core.dom.ast.cpp; @@ -24,7 +24,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType; * @noimplement This interface is not intended to be implemented by clients. */ public interface ICPPBase extends Cloneable { - public static final ICPPBase[] EMPTY_BASE_ARRAY = new ICPPBase[0]; + public static final ICPPBase[] EMPTY_BASE_ARRAY = {}; public static final int v_private = ICPPASTBaseSpecifier.v_private; public static final int v_protected = ICPPASTBaseSpecifier.v_protected; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java index 22225a116aa..b3bff980948 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/cpp/ICPPEnumeration.java @@ -28,7 +28,7 @@ public interface ICPPEnumeration extends IEnumeration, ICPPBinding { boolean isScoped(); /** - * Returns the underlying type of the enumeration if it is fixed, or null otherwise. + * Returns the underlying type of the enumeration if it is fixed, or {@code null} otherwise. * The underlying type can only be fixed in C++. */ IType getFixedType(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java index 330878bb36c..6307942c555 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/parser/util/CharArrayUtils.java @@ -74,6 +74,21 @@ public class CharArrayUtils { return true; } + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of a string. + * @since 5.5 + */ + public static final boolean equals(char[] str1, int start1, int length1, String str2) { + if (length1 != str2.length() || str1.length < length1 + start1) + return false; + for (int i = 0; i < length1; ++i) { + if (str1[start1++] != str2.charAt(i)) + return false; + } + return true; + } + /** * Returns {@code true} if a prefix of the character array is the same as contents * of a string. @@ -117,6 +132,10 @@ public class CharArrayUtils { return str1.length - str2.length; } + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of another character array. + */ public static final boolean equals(char[] str1, int start1, int length1, char[] str2) { if (length1 != str2.length || str1.length < length1 + start1) return false; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java index 17e83bb272a..b6b9281b02c 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTTranslationUnit.java @@ -485,4 +485,52 @@ public abstract class ASTTranslationUnit extends ASTNode implements IASTTranslat } return fSizeofCalculator; } + + /** + * Returns the offset of the given node, or -1 if the node is not part of the translation + * unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getNodeOffset(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return -1; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getNodeOffset() : -1; + } + + /** + * Returns the end offset of the given node, or -1 if the node is not part of the translation + * unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getNodeEndOffset(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return -1; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getNodeOffset() + nodeLocation.getNodeLength() : -1; + } + + /** + * Returns the 1-based starting line number of the given node, or 0 if the node is not part of + * the translation unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getStartingLineNumber(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return 0; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getStartingLineNumber() : 0; + } + + /** + * Returns the 1-based ending line number of the given node, or 0 if the node is not part of + * the translation unit file or doesn't have a file-location. + * @see IASTNode#getFileLocation() + */ + public static int getEndingLineNumber(IASTNode node) { + if (!node.isPartOfTranslationUnitFile()) + return 0; + IASTFileLocation nodeLocation = node.getFileLocation(); + return nodeLocation != null ? nodeLocation.getEndingLineNumber() : 0; + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java index 7a5b2a3c690..370ef687b8d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTLinkageSpecification.java @@ -6,7 +6,7 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * John Camelon (IBM) - Initial API and implementation + * John Camelon (IBM) - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; @@ -22,13 +22,12 @@ import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguityParent; /** * Extern "C" construct. */ -public class CPPASTLinkageSpecification extends ASTNode implements - ICPPASTLinkageSpecification, IASTAmbiguityParent { - +public class CPPASTLinkageSpecification extends ASTNode + implements ICPPASTLinkageSpecification, IASTAmbiguityParent { private String fLiteral; private IASTDeclaration[] fAllDeclarations; private IASTDeclaration[] fActiveDeclarations; - private int fLastDeclaration=-1; + private int fLastDeclaration = -1; public CPPASTLinkageSpecification() { } @@ -45,13 +44,10 @@ public class CPPASTLinkageSpecification extends ASTNode implements @Override public CPPASTLinkageSpecification copy(CopyStyle style) { CPPASTLinkageSpecification copy = new CPPASTLinkageSpecification(fLiteral); - for (IASTDeclaration declaration : getDeclarations()) + for (IASTDeclaration declaration : getDeclarations()) { copy.addDeclaration(declaration == null ? null : declaration.copy(style)); - copy.setOffsetAndLength(this); - if (style == CopyStyle.withLocations) { - copy.setCopyLocation(this); } - return copy; + return copy(copy, style); } @Override @@ -70,7 +66,7 @@ public class CPPASTLinkageSpecification extends ASTNode implements if (decl != null) { decl.setParent(this); decl.setPropertyInParent(OWNED_DECLARATION); - fAllDeclarations = ArrayUtil.appendAt( IASTDeclaration.class, fAllDeclarations, ++fLastDeclaration, decl); + fAllDeclarations = ArrayUtil.appendAt(IASTDeclaration.class, fAllDeclarations, ++fLastDeclaration, decl); fActiveDeclarations= null; } } @@ -99,9 +95,9 @@ public class CPPASTLinkageSpecification extends ASTNode implements public boolean accept(ASTVisitor action) { if (action.shouldVisitDeclarations) { switch (action.visit(this)) { - case ASTVisitor.PROCESS_ABORT : return false; - case ASTVisitor.PROCESS_SKIP : return true; - default : break; + case ASTVisitor.PROCESS_ABORT: return false; + case ASTVisitor.PROCESS_SKIP: return true; + default: break; } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPDeferredFunction.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPDeferredFunction.java index a9902441692..6f56b9210a9 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPDeferredFunction.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPDeferredFunction.java @@ -28,7 +28,6 @@ import org.eclipse.cdt.internal.core.dom.parser.ProblemType; public class CPPDeferredFunction extends CPPUnknownBinding implements ICPPFunction, ICPPComputableFunction { private static final ICPPFunctionType FUNCTION_TYPE= new CPPFunctionType(ProblemType.UNKNOWN_FOR_EXPRESSION, IType.EMPTY_TYPE_ARRAY); - private static final ICPPFunction[] NO_CANDIDATES= {}; /** * Creates a CPPDeferredFunction given a set of overloaded functions @@ -52,7 +51,7 @@ public class CPPDeferredFunction extends CPPUnknownBinding implements ICPPFuncti * @return the constructed CPPDeferredFunction */ public static ICPPFunction createForName(char[] name) { - return new CPPDeferredFunction(null, name, NO_CANDIDATES); + return new CPPDeferredFunction(null, name, null); } private final IBinding fOwner; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java index 42a6213a7b2..7d6a236b90d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2012 Google, Inc and others. + * Copyright (c) 2009, 2013 Google, Inc 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 @@ -177,9 +177,10 @@ public class AccessContext { /** * Returns access level to the members of a class. + * * @param classType A class - * @param inheritedAccessLevel Access level inherited from derived class. One of: v_public, v_protected, - * v_private. + * @param inheritedAccessLevel Access level inherited from derived class. + * One of: v_public, v_protected, v_private. * @return One of: v_public, v_protected, v_private. */ private int getMemberAccessLevel(ICPPClassType classType, int inheritedAccessLevel) { @@ -197,14 +198,14 @@ public class AccessContext { return accessLevel; } - private boolean isAccessibleBaseClass(ICPPClassType classType, ICPPClassType defived, int depth) { + private boolean isAccessibleBaseClass(ICPPClassType classType, ICPPClassType derived, int depth) { if (depth > CPPSemantics.MAX_INHERITANCE_DEPTH) return false; - if (defived.isSameType(classType)) + if (derived.isSameType(classType)) return true; - ICPPBase[] bases = defived.getBases(); + ICPPBase[] bases = ClassTypeHelper.getBases(derived, name); if (bases != null) { for (ICPPBase base : bases) { IBinding baseClass = base.getBaseClass(); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java index 6dd5c3bab52..b744feffa86 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java @@ -562,8 +562,12 @@ public class CPPSemantics { return false; } - private static void doKoenigLookup(LookupData data) throws DOMException { + public static void doKoenigLookup(LookupData data) throws DOMException { data.ignoreUsingDirectives = true; + // Set 'qualified' to true for the duration of this function call + // so the calls to lookup() don't ascend into enclosing scopes. + boolean originalQualified = data.qualified; + data.qualified = true; Set friendFns = new HashSet(2); Set associated = getAssociatedScopes(data, friendFns); for (ICPPNamespaceScope scope : associated) { @@ -572,6 +576,7 @@ public class CPPSemantics { } } mergeResults(data, friendFns.toArray(), false); + data.qualified = originalQualified; } static IBinding checkDeclSpecifier(IBinding binding, IASTName name, IASTNode decl) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionSet.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionSet.java index 9597fa30445..501ba428afe 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionSet.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalFunctionSet.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Wind River Systems, Inc. and others. + * Copyright (c) 2012, 2013 Wind River Systems, Inc. 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 @@ -28,6 +28,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateArgument; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap; +import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.core.dom.parser.ISerializableEvaluation; import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer; import org.eclipse.cdt.internal.core.dom.parser.Value; @@ -46,21 +47,41 @@ public class EvalFunctionSet extends CPPDependentEvaluation { // the type of 'obj' (needed for correct overload resolution of 'member_function' later). // Otherwise null. private final IType fImpliedObjectType; + + // Used to represent an EvalFunctionSet with zero functions. + // (We need the name in resolveFunction() - usually we get it from the CPPFunctionSet + // by asking the first function in the set for its name.) + // Exactly one of fFunctionSet and fName should be non-null. + private final char[] fName; public EvalFunctionSet(CPPFunctionSet set, boolean addressOf, IType impliedObjectType, IASTNode pointOfDefinition) { this(set, addressOf, impliedObjectType, findEnclosingTemplate(pointOfDefinition)); } + public EvalFunctionSet(CPPFunctionSet set, boolean addressOf, IType impliedObjectType, IBinding templateDefinition) { super(templateDefinition); fFunctionSet= set; fAddressOf= addressOf; fImpliedObjectType= impliedObjectType; + fName= null; + } + + public EvalFunctionSet(char[] name, boolean addressOf, IASTNode pointOfDefinition) { + this(name, addressOf, findEnclosingTemplate(pointOfDefinition)); + } + + public EvalFunctionSet(char[] name, boolean addressOf, IBinding templateDefinition) { + super(templateDefinition); + fFunctionSet= null; + fAddressOf= addressOf; + fImpliedObjectType= null; + fName= name; } public CPPFunctionSet getFunctionSet() { return fFunctionSet; } - + public boolean isAddressOf() { return fAddressOf; } @@ -81,6 +102,8 @@ public class EvalFunctionSet extends CPPDependentEvaluation { @Override public boolean isTypeDependent() { + if (fFunctionSet == null) + return true; final ICPPTemplateArgument[] args = fFunctionSet.getTemplateArguments(); if (args != null) { for (ICPPTemplateArgument arg : args) { @@ -115,54 +138,74 @@ public class EvalFunctionSet extends CPPDependentEvaluation { return PRVALUE; } + // Descriptive names for flags used during serialization. + private final static short FLAG_ADDRESS_OF = ITypeMarshalBuffer.FLAG1; + private final static short FLAG_HAS_FUNCTION_SET = ITypeMarshalBuffer.FLAG2; + private final static short FLAG_HAS_TEMPLATE_ARGS = ITypeMarshalBuffer.FLAG3; + @Override public void marshal(ITypeMarshalBuffer buffer, boolean includeValue) throws CoreException { - final ICPPFunction[] bindings = fFunctionSet.getBindings(); - final ICPPTemplateArgument[] args = fFunctionSet.getTemplateArguments(); short firstBytes = ITypeMarshalBuffer.EVAL_FUNCTION_SET; if (fAddressOf) - firstBytes |= ITypeMarshalBuffer.FLAG1; - if (args != null) - firstBytes |= ITypeMarshalBuffer.FLAG2; + firstBytes |= FLAG_ADDRESS_OF; + if (fFunctionSet != null) { + firstBytes |= FLAG_HAS_FUNCTION_SET; + final ICPPFunction[] bindings = fFunctionSet.getBindings(); + final ICPPTemplateArgument[] args = fFunctionSet.getTemplateArguments(); + if (args != null) + firstBytes |= FLAG_HAS_TEMPLATE_ARGS; - buffer.putShort(firstBytes); - buffer.putInt(bindings.length); - for (ICPPFunction binding : bindings) { - buffer.marshalBinding(binding); - } - if (args != null) { - buffer.putInt(args.length); - for (ICPPTemplateArgument arg : args) { - buffer.marshalTemplateArgument(arg); + buffer.putShort(firstBytes); + buffer.putInt(bindings.length); + for (ICPPFunction binding : bindings) { + buffer.marshalBinding(binding); } + if (args != null) { + buffer.putInt(args.length); + for (ICPPTemplateArgument arg : args) { + buffer.marshalTemplateArgument(arg); + } + } + buffer.marshalType(fImpliedObjectType); + } else { + buffer.putShort(firstBytes); + buffer.putCharArray(fName); } - buffer.marshalType(fImpliedObjectType); marshalTemplateDefinition(buffer); } public static ISerializableEvaluation unmarshal(short firstBytes, ITypeMarshalBuffer buffer) throws CoreException { - final boolean addressOf= (firstBytes & ITypeMarshalBuffer.FLAG1) != 0; - int bindingCount= buffer.getInt(); - ICPPFunction[] bindings= new ICPPFunction[bindingCount]; - for (int i = 0; i < bindings.length; i++) { - bindings[i]= (ICPPFunction) buffer.unmarshalBinding(); - } - ICPPTemplateArgument[] args= null; - if ((firstBytes & ITypeMarshalBuffer.FLAG2) != 0) { - int len= buffer.getInt(); - args = new ICPPTemplateArgument[len]; - for (int i = 0; i < args.length; i++) { - args[i]= buffer.unmarshalTemplateArgument(); + final boolean addressOf= (firstBytes & FLAG_ADDRESS_OF) != 0; + if ((firstBytes & FLAG_HAS_FUNCTION_SET) != 0) { + int bindingCount= buffer.getInt(); + ICPPFunction[] bindings= new ICPPFunction[bindingCount]; + for (int i = 0; i < bindings.length; i++) { + bindings[i]= (ICPPFunction) buffer.unmarshalBinding(); } + ICPPTemplateArgument[] args= null; + if ((firstBytes & FLAG_HAS_TEMPLATE_ARGS) != 0) { + int len= buffer.getInt(); + args = new ICPPTemplateArgument[len]; + for (int i = 0; i < args.length; i++) { + args[i]= buffer.unmarshalTemplateArgument(); + } + } + IType impliedObjectType= buffer.unmarshalType(); + IBinding templateDefinition= buffer.unmarshalBinding(); + return new EvalFunctionSet(new CPPFunctionSet(bindings, args, null), addressOf, impliedObjectType, templateDefinition); + } else { + char[] name = buffer.getCharArray(); + IBinding templateDefinition= buffer.unmarshalBinding(); + return new EvalFunctionSet(name, addressOf, templateDefinition); } - IType impliedObjectType= buffer.unmarshalType(); - IBinding templateDefinition= buffer.unmarshalBinding(); - return new EvalFunctionSet(new CPPFunctionSet(bindings, args, null), addressOf, impliedObjectType, templateDefinition); } @Override public ICPPEvaluation instantiate(ICPPTemplateParameterMap tpMap, int packOffset, ICPPClassSpecialization within, int maxdepth, IASTNode point) { + if (fFunctionSet == null) + return this; + ICPPTemplateArgument[] originalArguments = fFunctionSet.getTemplateArguments(); ICPPTemplateArgument[] arguments = originalArguments; if (originalArguments != null) @@ -211,13 +254,42 @@ public class EvalFunctionSet extends CPPDependentEvaluation { * succeeded or not */ public ICPPEvaluation resolveFunction(ICPPEvaluation[] args, IASTNode point) { - ICPPFunction[] functions = fFunctionSet.getBindings(); - LookupData data = new LookupData(functions[0].getNameCharArray(), - fFunctionSet.getTemplateArguments(), point); + // Set up the LookupData. + LookupData data; + ICPPFunction[] functions = null; + if (fFunctionSet == null) { + data = new LookupData(fName, null, point); + } else { + functions = fFunctionSet.getBindings(); + data = new LookupData(functions[0].getNameCharArray(), + fFunctionSet.getTemplateArguments(), point); + data.foundItems = functions; + } data.setFunctionArguments(false, args); if (fImpliedObjectType != null) data.setImpliedObjectType(fImpliedObjectType); + try { + // Perform ADL if appropriate. + if (fImpliedObjectType == null && !data.hasTypeOrMemberFunctionOrVariableResult()) { + CPPSemantics.doKoenigLookup(data); + + Object[] foundItems = (Object[]) data.foundItems; + if (foundItems != null && (functions == null || foundItems.length > functions.length)) { + // ADL found additional functions. + functions = Arrays.copyOf(foundItems, foundItems.length, ICPPFunction[].class); + + // doKoenigLookup() may introduce duplicates into the result. These must be + // eliminated to avoid resolveFunction() reporting an ambiguity. (Normally, when + // looukp() and doKoenigLookup() are called on the same LookupData object, the + // two functions coordinate using data stored in that object to eliminate + // duplicates, but in this case lookup() was called before with a different + // LookupData object and now we are only calling doKoenigLookup()). + functions = ArrayUtil.removeDuplicates(functions); + } + } + + // Perform template instantiation and overload resolution. IBinding binding = CPPSemantics.resolveFunction(data, functions, true); if (binding instanceof ICPPFunction && !(binding instanceof ICPPUnknownBinding)) return new EvalBinding(binding, null, getTemplateDefinition()); @@ -230,9 +302,11 @@ public class EvalFunctionSet extends CPPDependentEvaluation { @Override public int determinePackSize(ICPPTemplateParameterMap tpMap) { int r = CPPTemplates.PACK_SIZE_NOT_FOUND; - ICPPTemplateArgument[] templateArguments = fFunctionSet.getTemplateArguments(); - for (ICPPTemplateArgument arg : templateArguments) { - r = CPPTemplates.combinePackSize(r, CPPTemplates.determinePackSize(arg, tpMap)); + if (fFunctionSet != null) { + ICPPTemplateArgument[] templateArguments = fFunctionSet.getTemplateArguments(); + for (ICPPTemplateArgument arg : templateArguments) { + r = CPPTemplates.combinePackSize(r, CPPTemplates.determinePackSize(arg, tpMap)); + } } return r; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalID.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalID.java index 1a9b23ef992..3e4996d1847 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalID.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/EvalID.java @@ -206,10 +206,13 @@ public class EvalID extends CPPDependentEvaluation { } if (binding instanceof CPPDeferredFunction) { - CPPDeferredFunction deferredFunction = (CPPDeferredFunction) binding; - if (deferredFunction.getCandidates() != null) { - CPPFunctionSet functionSet = new CPPFunctionSet(deferredFunction.getCandidates(), templateArgs, null); + ICPPFunction[] candidates = ((CPPDeferredFunction) binding).getCandidates(); + if (candidates != null) { + CPPFunctionSet functionSet = new CPPFunctionSet(candidates, templateArgs, null); return new EvalFunctionSet(functionSet, isAddressOf(expr), null, expr); + } else { + // Just store the name. ADL at the time of instantiation might come up with bindings. + return new EvalFunctionSet(name.getSimpleID(), isAddressOf(expr), expr); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java index da151fec236..586e8f6e881 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/LookupData.java @@ -472,12 +472,6 @@ public class LookupData extends ScopeLookupData { public void setFunctionArguments(boolean containsImpliedObject, ICPPEvaluation... exprs) { argsContainImpliedObject= containsImpliedObject; functionArgs= exprs; - for (ICPPEvaluation e : exprs) { - if (e.isTypeDependent()) { - setIgnorePointOfDeclaration(true); - break; - } - } } public void setFunctionArguments(boolean containsImpliedObject, IASTInitializerClause... exprs) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java index 30dc0f59346..f78c870b83f 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2009 Institute for Software, HSR Hochschule fuer Technik + * Copyright (c) 2008, 2013 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -7,7 +7,8 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Institute for Software - initial API and implementation + * Institute for Software - initial API and implementation + * Sergey Prigogin (Google) ******************************************************************************/ package org.eclipse.cdt.internal.core.dom.rewrite.commenthandler; @@ -64,7 +65,7 @@ public class ASTCommenter { } private int checkOffsets(IASTNode node) { - int offset = ((ASTNode)node).getOffset(); + int offset = ((ASTNode) node).getOffset(); int status = PROCESS_CONTINUE; if (isCommentOnSameLine(node) @@ -82,8 +83,8 @@ public class ASTCommenter { } private boolean isCommentOnSameLine(IASTNode node) { - return commentNodeLocation.getStartingLineNumber() == node.getFileLocation() - .getEndingLineNumber(); + return commentNodeLocation.getStartingLineNumber() == + node.getFileLocation().getEndingLineNumber(); } @Override @@ -168,41 +169,29 @@ public class ASTCommenter { } /** - * Creates a NodeCommentMap for the given TranslationUnit. This is the only way - * to get a NodeCommentMap which contains all the comments mapped against nodes. + * Creates a NodeCommentMap for the given AST. This is the only way to get a NodeCommentMap + * which contains all the comments mapped against nodes. * - * @param tu TranslationUnit + * @param ast the AST * @return NodeCommentMap */ - public static NodeCommentMap getCommentedNodeMap(IASTTranslationUnit tu){ + public static NodeCommentMap getCommentedNodeMap(IASTTranslationUnit ast) { NodeCommentMap commentMap = new NodeCommentMap(); - if (tu == null) { + if (ast == null) { return commentMap; } - IASTComment[] commentsArray = tu.getComments(); - if (commentsArray == null) { - return commentMap; - } - // Note that constructing a real ArrayList is required here, since in filterNonTuComments, the - // remove-method will be invoked on the list's iterator. Calling it on the type Arrays$ArrayList (the - // resulting type of Arrays.asList() ) would throw a UnsupportedOperationException. - ArrayList comments = new ArrayList(Arrays.asList(commentsArray)); - filterNonTuComments(comments); - return addCommentsToCommentMap(tu, comments); - } - - /** - * Note that passing an ArrayList (instead of just List or Collection) is required here, since this - * guarantees that the call to the remove-method on the list's iterator will not result in an - * UnsupportedOperationException which might be the case for other Collection/List types. - */ - private static void filterNonTuComments(ArrayList comments) { - Iterator iterator = comments.iterator(); - while (iterator.hasNext()) { - if (!iterator.next().isPartOfTranslationUnitFile()) { - iterator.remove(); + IASTComment[] commentsArray = ast.getComments(); + List comments = new ArrayList(commentsArray.length); + for (IASTComment comment : commentsArray) { + if (comment.isPartOfTranslationUnitFile()) { + comments.add(comment); } } + assignPreprocessorComments(commentMap, comments, ast); + CommentHandler commentHandler = new CommentHandler(comments); + ASTCommenterVisitor commenter = new ASTCommenterVisitor(commentHandler, commentMap); + ast.accept(commenter); + return commentMap; } private static boolean isCommentDirectlyBeforePreprocessorStatement(IASTComment comment, @@ -211,11 +200,11 @@ public class ASTCommenter { return true; } IASTFileLocation commentLocation = comment.getFileLocation(); - int preprcessorOffset = statement.getFileLocation().getNodeOffset(); - if (preprcessorOffset > commentLocation.getNodeOffset()) { - PreprocessorRangeChecker vister = new PreprocessorRangeChecker(preprcessorOffset, commentLocation); - tu.accept(vister); - return vister.isPreStatementComment; + int preprocessorOffset = statement.getFileLocation().getNodeOffset(); + if (preprocessorOffset > commentLocation.getNodeOffset()) { + PreprocessorRangeChecker visitor = new PreprocessorRangeChecker(preprocessorOffset, commentLocation); + tu.accept(visitor); + return visitor.isPreStatementComment; } return false; } @@ -224,24 +213,12 @@ public class ASTCommenter { return node.isPartOfTranslationUnitFile(); } - private static NodeCommentMap addCommentsToCommentMap(IASTTranslationUnit tu, - ArrayList comments){ - NodeCommentMap commentMap = new NodeCommentMap(); - CommentHandler commHandler = new CommentHandler(comments); - - assignPreprocessorComments(commentMap, comments, tu); - ASTCommenterVisitor commenter = new ASTCommenterVisitor(commHandler, commentMap); - tu.accept(commenter); - return commentMap; - } - /** - * Note that passing an ArrayList (instead of just List or Collection) is required here, since this - * guarantees that the call to the remove-method on the list's iterator will not result in an - * UnsupportedOperationException which might be the case for other Collection/List types. + * Puts leading and trailing comments to {@code commentMap} and removes them from + * the {@code comments} list. */ private static void assignPreprocessorComments(NodeCommentMap commentMap, - ArrayList comments, IASTTranslationUnit tu) { + List comments, IASTTranslationUnit tu) { IASTPreprocessorStatement[] preprocessorStatementsArray = tu.getAllPreprocessorStatements(); if (preprocessorStatementsArray == null) { return; @@ -252,6 +229,7 @@ public class ASTCommenter { return; } + List freestandingComments = new ArrayList(comments.size()); Iterator statementsIter = preprocessorStatements.iterator(); Iterator commentIter = comments.iterator(); IASTPreprocessorStatement curStatement = getNextNodeInTu(statementsIter); @@ -261,18 +239,26 @@ public class ASTCommenter { int commentLineNr = curComment.getFileLocation().getStartingLineNumber(); if (commentLineNr == statementLineNr) { commentMap.addTrailingCommentToNode(curStatement, curComment); - commentIter.remove(); curComment = getNextNodeInTu(commentIter); } else if (commentLineNr > statementLineNr) { curStatement = getNextNodeInTu(statementsIter); } else if (isCommentDirectlyBeforePreprocessorStatement(curComment, curStatement, tu)) { commentMap.addLeadingCommentToNode(curStatement, curComment); - commentIter.remove(); curComment = getNextNodeInTu(commentIter); } else { + freestandingComments.add(curComment); curComment = getNextNodeInTu(commentIter); } } + while (curComment != null) { + freestandingComments.add(curComment); + curComment = getNextNodeInTu(commentIter); + } + + if (freestandingComments.size() != comments.size()) { + comments.clear(); + comments.addAll(freestandingComments); + } } private static T getNextNodeInTu(Iterator iter) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java index a8a97e9dbb8..030b99f16d5 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/ASTCommenterVisitor.java @@ -39,7 +39,7 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTCompositeTypeSpecifier * @author Guido Zgraggen IFS */ public class ASTCommenterVisitor extends ASTVisitor { - protected CommentHandler commHandler; + protected CommentHandler commentHandler; protected NodeCommentMap commentMap; private NodeCommenter nodeCommenter; @@ -61,14 +61,14 @@ public class ASTCommenterVisitor extends ASTVisitor { shouldVisitTranslationUnit = true; } - public ASTCommenterVisitor(CommentHandler commHandler, NodeCommentMap commentMap) { - this.commHandler = commHandler; + public ASTCommenterVisitor(CommentHandler commentHandler, NodeCommentMap commentMap) { + this.commentHandler = commentHandler; this.commentMap = commentMap; init(); } private void init() { - nodeCommenter = new NodeCommenter(this, commHandler, commentMap); + nodeCommenter = new NodeCommenter(this, commentHandler, commentMap); } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java index ec0d9f9afc8..589a67b2510 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/commenthandler/NodeCommenter.java @@ -62,13 +62,13 @@ import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTWhileStatement; */ public class NodeCommenter { protected ASTVisitor visitor; - protected CommentHandler commHandler; + protected CommentHandler commentHandler; protected NodeCommentMap commentMap; protected List children; public NodeCommenter(ASTVisitor visitor, CommentHandler commHandler, NodeCommentMap commentMap) { this.visitor = visitor; - this.commHandler = commHandler; + this.commentHandler = commHandler; this.commentMap = commentMap; this.children = new ArrayList(); } @@ -122,17 +122,17 @@ public class NodeCommenter { private void addLeadingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addLeadingCommentToNode(node, comment); - commHandler.allreadyAdded(comment); + commentHandler.allreadyAdded(comment); } private void addTrailingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addTrailingCommentToNode(node, comment); - commHandler.allreadyAdded(comment); + commentHandler.allreadyAdded(comment); } private void addFreestandingCommentToMap(ASTNode node, IASTComment comment) { commentMap.addFreestandingCommentToNode(node, comment); - commHandler.allreadyAdded(comment); + commentHandler.allreadyAdded(comment); } private boolean isTrailing(ASTNode node, ASTNode com, int nodeLineNumber, int commentLineNumber) { @@ -200,8 +200,8 @@ public class NodeCommenter { } protected int appendComments(ASTNode node) { - while (commHandler.hasMore()) { - IASTComment comment = commHandler.getFirst(); + while (commentHandler.hasMore()) { + IASTComment comment = commentHandler.getFirst(); if (isNotSameFile(node, comment)) { return ASTVisitor.PROCESS_SKIP; @@ -215,8 +215,8 @@ public class NodeCommenter { } protected int appendFreestandingComments(ASTNode node) { - while (commHandler.hasMore()) { - IASTComment comment = commHandler.getFirst(); + while (commentHandler.hasMore()) { + IASTComment comment = commentHandler.getFirst(); if (isNotSameFile(node, comment)) { return ASTVisitor.PROCESS_SKIP; @@ -234,9 +234,9 @@ public class NodeCommenter { } public void appendRemainingComments(IASTDeclaration declaration) { - while (commHandler.hasMore()) { - IASTComment comment = commHandler.getFirst(); - if (appendComment((ASTNode)declaration, comment)) { + while (commentHandler.hasMore()) { + IASTComment comment = commentHandler.getFirst(); + if (appendComment((ASTNode) declaration, comment)) { continue; } addFreestandingCommentToMap((ASTNode) declaration, comment); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/composite/cpp/CPPCompositesFactory.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/composite/cpp/CPPCompositesFactory.java index df5d9d8c559..75575e0fb3b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/composite/cpp/CPPCompositesFactory.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/composite/cpp/CPPCompositesFactory.java @@ -330,15 +330,17 @@ public class CPPCompositesFactory extends AbstractCompositeFactory { if (eval instanceof EvalFunctionSet) { EvalFunctionSet e= (EvalFunctionSet) eval; final CPPFunctionSet fset = e.getFunctionSet(); - ICPPFunction[] a = fset.getBindings(); - ICPPTemplateArgument[] b = fset.getTemplateArguments(); - IType c = e.getImpliedObjectType(); - - ICPPFunction[] a2 = getCompositeFunctionArray(a); - ICPPTemplateArgument[] b2 = TemplateInstanceUtil.convert(this, b); - IType c2 = getCompositeType(c); - if (a != a2 || b != b2 || c != c2 || templateDefinition != templateDefinition2) - e= new EvalFunctionSet(new CPPFunctionSet(a2, b2, null), e.isAddressOf(), c2, templateDefinition2); + if (fset != null) { + ICPPFunction[] a = fset.getBindings(); + ICPPTemplateArgument[] b = fset.getTemplateArguments(); + IType c = e.getImpliedObjectType(); + + ICPPFunction[] a2 = getCompositeFunctionArray(a); + ICPPTemplateArgument[] b2 = TemplateInstanceUtil.convert(this, b); + IType c2 = getCompositeType(c); + if (a != a2 || b != b2 || c != c2 || templateDefinition != templateDefinition2) + e= new EvalFunctionSet(new CPPFunctionSet(a2, b2, null), e.isAddressOf(), c2, templateDefinition2); + } return e; } if (eval instanceof EvalID) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java index 43bc89e7012..993acd2402a 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/ASTPreprocessorNode.java @@ -113,6 +113,7 @@ abstract class ASTPreprocessorNode extends ASTNode { class ASTComment extends ASTPreprocessorNode implements IASTComment { private final boolean fIsBlockComment; private String fFilePath; + public ASTComment(IASTTranslationUnit parent, String filePath, int offset, int endOffset, boolean isBlockComment) { super(parent, IASTTranslationUnit.PREPROCESSOR_STATEMENT, offset, endOffset); fIsBlockComment= isBlockComment; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java index 66382144eba..6d6518d4200 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/CPreprocessor.java @@ -68,8 +68,8 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; /** - * C-Preprocessor providing tokens for the parsers. The class should not be used directly, rather than that - * you should be using the {@link IScanner} interface. + * C-Preprocessor providing tokens for the parsers. The class should not be used directly, + * rather than that you should be using the {@link IScanner} interface. * @since 5.0 */ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { @@ -87,8 +87,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { private static final char[] ONE = "1".toCharArray(); //$NON-NLS-1$ - - // standard built-ins + // Standard built-ins private static final ObjectStyleMacro __CDT_PARSER__= new ObjectStyleMacro("__CDT_PARSER__".toCharArray(), ONE); //$NON-NLS-1$ private static final ObjectStyleMacro __cplusplus = new ObjectStyleMacro("__cplusplus".toCharArray(), ONE); //$NON-NLS-1$ private static final ObjectStyleMacro __STDC__ = new ObjectStyleMacro("__STDC__".toCharArray(), ONE); //$NON-NLS-1$ @@ -374,7 +373,7 @@ public class CPreprocessor implements ILexerLog, IScanner, IAdaptable { return fLocationMap; } - private void configureKeywords(ParserLanguage language, IScannerExtensionConfiguration configuration) { + private void configureKeywords(ParserLanguage language, IScannerExtensionConfiguration configuration) { Keywords.addKeywordsPreprocessor(fPPKeywords); if (language == ParserLanguage.C) { Keywords.addKeywordsC(fKeywords); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java index 2674e80e36a..cdd034e83f8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeGuardDetection.java @@ -6,9 +6,8 @@ * http://www.eclipse.org/legal/epl-v10.html * * Contributors: - * Markus Schorn - initial API and implementation + * Markus Schorn - initial API and implementation *******************************************************************************/ - package org.eclipse.cdt.internal.core.parser.scanner; import static org.eclipse.cdt.core.parser.OffsetLimitReachedException.ORIGIN_PREPROCESSOR_DIRECTIVE; @@ -39,7 +38,7 @@ public class IncludeGuardDetection { Token t = l.nextToken(); if (t.getType() == IToken.tIDENTIFIER) { char[] guard= null; - switch(ppKeywords.get(t.getCharImage())) { + switch (ppKeywords.get(t.getCharImage())) { case IPreprocessorDirective.ppIfndef: // #ifndef GUARD t= l.nextToken(); @@ -98,7 +97,7 @@ public class IncludeGuardDetection { Token t= l.nextDirective(); if (t.getType() == IToken.tEND_OF_INPUT) return true; - switch(ppKeywords.get(l.nextToken().getCharImage())) { + switch (ppKeywords.get(l.nextToken().getCharImage())) { case IPreprocessorDirective.ppIf: case IPreprocessorDirective.ppIfdef: case IPreprocessorDirective.ppIfndef: @@ -124,7 +123,6 @@ public class IncludeGuardDetection { return t; } - public static boolean detectIncludeEndif(Lexer l) { l.saveState(); try { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java index 458dbc3f632..8c397858cf4 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/parser/scanner/IncludeSearchPathElement.java @@ -7,11 +7,16 @@ * * Contributors: * Markus Schorn - initial API and implementation + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.parser.scanner; import java.io.File; +import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + /** * Represents an entry of the include search path */ @@ -42,14 +47,14 @@ public final class IncludeSearchPathElement { public String getLocation(String includeDirective) { if (fIsFrameworkDirectory) { int firstSep = firstSeparator(includeDirective); - if (firstSep < 1) { + if (firstSep <= 0) { return null; } String framework = includeDirective.substring(0, firstSep); String file= includeDirective.substring(firstSep + 1); if (file.length() == 0) return null; - + StringBuilder buf= new StringBuilder(fPath); replace(buf, FRAMEWORK_VAR, framework); replace(buf, FILE_VAR, file); @@ -58,6 +63,42 @@ public final class IncludeSearchPathElement { return ScannerUtility.createReconciledPath(fPath, includeDirective); } + /** + * Returns the include directive for the given location satisfying the condition + * {@code getLocation(getIncludeDirective(location) == location}. If no such include directive + * without ".." exists, returns {@code null}. + */ + public String getIncludeDirective(String location) { + IPath dirPath = new Path(fPath); + IPath locationPath = new Path(location); + if (fIsFrameworkDirectory) { + if (dirPath.segmentCount() != locationPath.segmentCount()) + return null; + int i = PathUtil.matchingFirstSegments(dirPath, locationPath); + String dirSegment = dirPath.segment(i); + String locationSegment = locationPath.segment(i); + String framework = deduceVariable(FRAMEWORK_VAR, dirSegment, locationSegment); + if (framework == null) + return null; + i++; + dirPath = dirPath.removeFirstSegments(i); + locationPath = locationPath.removeFirstSegments(i); + i = PathUtil.matchingFirstSegments(dirPath, locationPath); + if (i < dirPath.segmentCount() - 1) + return null; + dirSegment = dirPath.segment(i); + locationSegment = locationPath.segment(i); + String file = deduceVariable(FILE_VAR, dirSegment, locationSegment); + if (file == null) + return null; + return framework + '/' + file; + } + + if (!PathUtil.isPrefix(dirPath, locationPath)) + return null; + return locationPath.removeFirstSegments(dirPath.segmentCount()).setDevice(null).toPortableString(); + } + private int firstSeparator(String path) { int firstSep= path.indexOf('/'); if (NON_SLASH_SEPARATOR) { @@ -73,6 +114,20 @@ public final class IncludeSearchPathElement { } } + private String deduceVariable(String varName, String raw, String substituted) { + int pos = raw.indexOf(varName); + if (pos < 0) + return null; + int suffixLength = raw.length() - pos - varName.length(); + if (substituted.length() <= pos + suffixLength) + return null; + for (int i = 0; i < suffixLength; i++) { + if (raw.charAt(raw.length() - i) != substituted.charAt(substituted.length() - i)) + return null; + } + return substituted.substring(pos, substituted.length() - suffixLength); + } + /** * For debugging only. */ diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java index 25a7c72a7b2..d82538ccce7 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java @@ -235,10 +235,11 @@ public class PDOM extends PlatformObject implements IPDOM { * 141.0 - Storing enclosing template bindings for evaluations, bug 399829. * 142.0 - Changed marshalling of evaluations to allow more than 15 evaluation kinds, bug 401479. * 143.0 - Store implied object type in EvalFunctionSet, bug 402409. + * 144.0 - Add support for storing function sets with zero functions in EvalFunctionSet, bug 402498. */ - private static final int MIN_SUPPORTED_VERSION= version(143, 0); - private static final int MAX_SUPPORTED_VERSION= version(143, Short.MAX_VALUE); - private static final int DEFAULT_VERSION = version(143, 0); + private static final int MIN_SUPPORTED_VERSION= version(144, 0); + private static final int MAX_SUPPORTED_VERSION= version(144, Short.MAX_VALUE); + private static final int DEFAULT_VERSION = version(144, 0); private static int version(int major, int minor) { return (major << 16) + minor; diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/EnvironmentChangeEvent.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/EnvironmentChangeEvent.java index bb4dc6e1356..9592954a972 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/EnvironmentChangeEvent.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/EnvironmentChangeEvent.java @@ -1,37 +1,47 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 Intel Corporation and others. + * Copyright (c) 2005, 2013 Intel Corporation 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: - * Intel Corporation - Initial API and implementation + * Intel Corporation - Initial API and implementation + * Andrew Gvozdev - Implementation of notification mechanism including changes to this API *******************************************************************************/ package org.eclipse.cdt.internal.core.envvar; +import java.util.Collection; + import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.utils.envvar.IEnvironmentChangeEvent; -public class EnvironmentChangeEvent { - private static final IEnvironmentVariable[] EMPTY_VAR_ARRAY = new IEnvironmentVariable[0]; - - private IEnvironmentVariable[] fAddedVars, fRemovedVars, fChangedVars; - - EnvironmentChangeEvent(IEnvironmentVariable[] addedVars, IEnvironmentVariable[] removedVars, IEnvironmentVariable[] changedVars){ - fAddedVars = addedVars != null ? (IEnvironmentVariable[])addedVars.clone() : null; - fRemovedVars = removedVars != null ? (IEnvironmentVariable[])removedVars.clone() : null; - fChangedVars = changedVars != null ? (IEnvironmentVariable[])changedVars.clone() : null; - } - public IEnvironmentVariable[] getAddedVariables(){ - return fAddedVars != null ? (IEnvironmentVariable[])fAddedVars.clone() : EMPTY_VAR_ARRAY; +/** + * Concrete implementation of event describing changes to environment variables defined by user + * on CDT Environment page in Preferences. + */ +public class EnvironmentChangeEvent implements IEnvironmentChangeEvent { + private IEnvironmentVariable[] oldVariables; + private IEnvironmentVariable[] newVariables; + + /** + * Constructor. + * + * @param oldVars - set of environment variables before the change. + * @param newVars - set of environment variables after the change. + */ + public EnvironmentChangeEvent(Collection oldVars, Collection newVars) { + oldVariables = oldVars.toArray(new IEnvironmentVariable[oldVars.size()]); + newVariables = newVars.toArray(new IEnvironmentVariable[newVars.size()]); } - public IEnvironmentVariable[] getRemovedVariables(){ - return fRemovedVars != null ? (IEnvironmentVariable[])fRemovedVars.clone() : EMPTY_VAR_ARRAY; + @Override + public IEnvironmentVariable[] getOldVariables() { + return oldVariables; } - public IEnvironmentVariable[] getChangedVariables(){ - return fChangedVars != null ? (IEnvironmentVariable[])fChangedVars.clone() : EMPTY_VAR_ARRAY; + @Override + public IEnvironmentVariable[] getNewVariables() { + return newVariables; } - } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/UserDefinedEnvironmentSupplier.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/UserDefinedEnvironmentSupplier.java index ae89d9ef715..b1ff1d23024 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/UserDefinedEnvironmentSupplier.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/core/envvar/UserDefinedEnvironmentSupplier.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2011 Intel Corporation and others. + * Copyright (c) 2005, 2013 Intel Corporation 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 @@ -9,23 +9,23 @@ * Intel Corporation - Initial API and implementation * James Blackburn (Broadcom Corp.) * IBM Corporation + * Andrew Gvozdev - Notification mechanism for changes to environment variables *******************************************************************************/ package org.eclipse.cdt.internal.core.envvar; +import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.Map; -import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; import org.eclipse.cdt.core.settings.model.ICProjectDescription; -import org.eclipse.cdt.core.settings.model.util.CDataUtil; import org.eclipse.cdt.internal.core.settings.model.CConfigurationSpecSettings; import org.eclipse.cdt.internal.core.settings.model.IInternalCCfgInfo; import org.eclipse.cdt.utils.envvar.EnvVarOperationProcessor; +import org.eclipse.cdt.utils.envvar.IEnvironmentChangeListener; +import org.eclipse.cdt.utils.envvar.PrefsStorableEnvironment; import org.eclipse.cdt.utils.envvar.StorableEnvironment; import org.eclipse.cdt.utils.envvar.StorableEnvironmentLoader; import org.eclipse.core.resources.IProject; @@ -52,114 +52,26 @@ import org.osgi.service.prefs.Preferences; * * @since 3.0 */ -public class UserDefinedEnvironmentSupplier extends - StorableEnvironmentLoader - implements ICoreEnvironmentVariableSupplier{ - +public class UserDefinedEnvironmentSupplier extends StorableEnvironmentLoader implements ICoreEnvironmentVariableSupplier { public static final String NODENAME = "environment"; //$NON-NLS-1$ public static final String PREFNAME_WORKSPACE = "workspace"; //$NON-NLS-1$ public static final String PREFNAME_PROJECT = "project"; //$NON-NLS-1$ public static final String NODENAME_CFG = "project"; //$NON-NLS-1$ -/* private static final String fNonOverloadableVariables[] = new String[]{ - //users not allowed currently to override the "CWD" and "PWD" variables - EnvVarOperationProcessor.normalizeName("CWD"), //$NON-NLS-1$ - EnvVarOperationProcessor.normalizeName("PWD") //$NON-NLS-1$ - }; -*/ - private StorableEnvironment fWorkspaceVariables; + private PrefsStorableEnvironment fWorkspaceVariables; private StorableEnvironment fOverrideVariables = new StorableEnvironment(false); - static class VarKey { - private IEnvironmentVariable fVar; - private boolean fNameOnly; - private int fCode; - - VarKey(IEnvironmentVariable var, boolean nameOnly) { - fVar = var; - fNameOnly = nameOnly; - } - - public IEnvironmentVariable getVariable() { - return fVar; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) - return true; - - if (!(obj instanceof VarKey)) - return false; - - VarKey other = (VarKey)obj; - - IEnvironmentVariable otherVar = other.fVar; - - if (fVar == otherVar) - return true; - - if (!CDataUtil.objectsEqual(fVar.getName(), otherVar.getName())) - return false; - - if (fNameOnly) - return true; - - if (fVar.getOperation() != otherVar.getOperation()) - return false; - - if (!CDataUtil.objectsEqual(fVar.getValue(), otherVar.getValue())) - return false; - - if (!CDataUtil.objectsEqual(fVar.getDelimiter(),otherVar.getDelimiter())) - return false; - - return true; - } - - @Override - public int hashCode() { - int code = fCode; - if (code == 0) { - code = 47; - - String tmp = fVar.getName(); - if (tmp != null) - code += tmp.hashCode(); - - if (fNameOnly) - return code; - - code += fVar.getOperation(); - - tmp = fVar.getValue(); - if (tmp != null) - code += tmp.hashCode(); - - tmp = fVar.getDelimiter(); - if (tmp != null) - code += tmp.hashCode(); - - fCode = code; - } - return code; - } - - } public StorableEnvironment getEnvironment(Object context) { - return getEnvironment(context,true); + return getEnvironment(context, false); } protected StorableEnvironment getEnvironment(Object context, boolean forceLoad) { -// if (context == null) -// return null; - StorableEnvironment env = null; if (context instanceof IInternalCCfgInfo) { try { CConfigurationSpecSettings settings = ((IInternalCCfgInfo)context).getSpecSettings(); env = settings.getEnvironment(); - if (env == null && forceLoad) { + if (env == null || forceLoad) { env = loadEnvironment(context, settings.isReadOnly()); settings.setEnvironment(env); } @@ -168,8 +80,8 @@ public class UserDefinedEnvironmentSupplier extends } } else if (context instanceof IWorkspace || context == null) { - if (fWorkspaceVariables == null && forceLoad) - fWorkspaceVariables = loadEnvironment(context, false); + if (fWorkspaceVariables == null || forceLoad) + fWorkspaceVariables = (PrefsStorableEnvironment) loadEnvironment(context, false); env = fWorkspaceVariables; } @@ -238,7 +150,7 @@ public class UserDefinedEnvironmentSupplier extends } private Preferences getWorkspaceNode() { - Preferences prefNode = new InstanceScope().getNode(CCorePlugin.PLUGIN_ID); + Preferences prefNode = InstanceScope.INSTANCE.getNode(CCorePlugin.PLUGIN_ID); if (prefNode == null) return null; @@ -291,81 +203,11 @@ public class UserDefinedEnvironmentSupplier extends fWorkspaceVariables.setAppendEnvironment(env.appendEnvironment()); fWorkspaceVariables.setAppendContributedEnvironment(env.appendContributedEnvironment()); - EnvironmentChangeEvent event = createEnvironmentChangeEvent(newVariables, oldVariables); - storeWorkspaceEnvironment(true); -// updateProjectInfo(null); - - return event != null; + return ! Arrays.equals(oldVariables, newVariables); } - static EnvironmentChangeEvent createEnvironmentChangeEvent(IEnvironmentVariable[] newVars, IEnvironmentVariable[] oldVars) { - IEnvironmentVariable[] addedVars = null, removedVars = null, changedVars = null; - - if (oldVars == null || oldVars.length == 0) { - if (newVars != null && newVars.length != 0) - addedVars = newVars.clone(); - } else if (newVars == null || newVars.length == 0) { - removedVars = oldVars.clone(); - } else { - HashSet newSet = new HashSet(newVars.length); - HashSet oldSet = new HashSet(oldVars.length); - - for (IEnvironmentVariable newVar : newVars) { - newSet.add(new VarKey(newVar, true)); - } - - for (IEnvironmentVariable oldVar : oldVars) { - oldSet.add(new VarKey(oldVar, true)); - } - - @SuppressWarnings("unchecked") - HashSet newSetCopy = (HashSet)newSet.clone(); - - newSet.removeAll(oldSet); - oldSet.removeAll(newSetCopy); - - if (newSet.size() != 0) { - addedVars = varsFromKeySet(newSet); - } - - if (oldSet.size() != 0) { - removedVars = varsFromKeySet(oldSet); - } - - newSetCopy.removeAll(newSet); - - HashSet modifiedSet = new HashSet(newSetCopy.size()); - for (VarKey key : newSetCopy) { - modifiedSet.add(new VarKey(key.getVariable(), false)); - } - - for (IEnvironmentVariable oldVar : oldVars) { - modifiedSet.remove(new VarKey(oldVar, false)); - } - - if (modifiedSet.size() != 0) - changedVars = varsFromKeySet(modifiedSet); - } - - if (addedVars != null || removedVars != null || changedVars != null) - return new EnvironmentChangeEvent(addedVars, removedVars, changedVars); - return null; - } - - static IEnvironmentVariable[] varsFromKeySet(Set set) { - IEnvironmentVariable vars[] = new IEnvironmentVariable[set.size()]; - int i = 0; - for(Iterator iter = set.iterator(); iter.hasNext(); i++) { - VarKey key = iter.next(); - vars[i] = key.getVariable(); - } - - return vars; - } - - public void storeProjectEnvironment(ICProjectDescription des, boolean force) { ICConfigurationDescription cfgs[] = des.getConfigurations(); for (ICConfigurationDescription cfg : cfgs) { @@ -448,7 +290,6 @@ public class UserDefinedEnvironmentSupplier extends return null; IEnvironmentVariable var = env.createVariable(name,value,op,delimiter); if (env.isChanged()) { -// updateProjectInfo(context); env.setChanged(false); } return var; @@ -459,9 +300,6 @@ public class UserDefinedEnvironmentSupplier extends if (env == null) return null; IEnvironmentVariable var = env.deleteVariable(name); - if (var != null) { -// updateProjectInfo(context); - } return var; } @@ -470,9 +308,7 @@ public class UserDefinedEnvironmentSupplier extends if (env == null) return; - if (env.deleteAll()) { -// updateProjectInfo(context); - } + env.deleteAll(); } public void setVariables(IEnvironmentVariable vars[], Object context) { @@ -482,28 +318,14 @@ public class UserDefinedEnvironmentSupplier extends env.setVariales(vars); if (env.isChanged()) { -// updateProjectInfo(context); env.setChanged(false); } } -// protected void updateProjectInfo(Object context) { -// } - -// protected void cfgVarsModified(ICConfigurationDescription cfg) { -// cfg.setRebuildState(true); -// EnvironmentVariableProvider.getDefault().checkBuildPathVariables(cfg); -// } - protected String getValidName(String name) { if (name == null || (name = name.trim()).length() == 0) return null; -// if (fNonOverloadableVariables != null) { -// for(int i = 0; i < fNonOverloadableVariables.length; i++) { -// if (fNonOverloadableVariables[i].equals(EnvVarOperationProcessor.normalizeName(name))) -// return null; -// } -// } + return name; } @@ -548,4 +370,30 @@ public class UserDefinedEnvironmentSupplier extends } } + /** + * Adds a listener that will be notified of changes in environment variables. + * + * @param listener - the listener to add + * @since 5.5 + */ + public void registerEnvironmentChangeListener(IEnvironmentChangeListener listener) { + if (fWorkspaceVariables == null) { + getEnvironment(null, false); + } + if (fWorkspaceVariables != null) { + fWorkspaceVariables.registerEnvironmentChangeListener(listener); + } + } + + /** + * Removes an environment variables change listener. + * + * @param listener - the listener to remove. + * @since 5.5 + */ + public void unregisterEnvironmentChangeListener(IEnvironmentChangeListener listener) { + if (fWorkspaceVariables != null) { + fWorkspaceVariables.unregisterEnvironmentChangeListener(listener); + } + } } diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java index e073278a6ee..e70cc0c9191 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/internal/formatter/Scribe.java @@ -464,8 +464,8 @@ public class Scribe { printIndentationIfNecessary(buffer); buffer.append(lineSeparator); } - lastNumberOfNewLines+= linesNumber; - line+= linesNumber; + lastNumberOfNewLines += linesNumber; + line += linesNumber; column= 1; needSpace= false; pendingSpace= false; @@ -475,8 +475,8 @@ public class Scribe { printIndentationIfNecessary(buffer); buffer.append(lineSeparator); } - lastNumberOfNewLines+= linesNumber; - line+= linesNumber; + lastNumberOfNewLines += linesNumber; + line += linesNumber; column= 1; needSpace= false; pendingSpace= false; @@ -491,8 +491,8 @@ public class Scribe { printIndentationIfNecessary(buffer); buffer.append(lineSeparator); } - lastNumberOfNewLines+= realNewLineNumber; - line+= realNewLineNumber; + lastNumberOfNewLines += realNewLineNumber; + line += realNewLineNumber; column= 1; needSpace= false; pendingSpace= false; @@ -771,14 +771,8 @@ public class Scribe { printComment(); currentPosition= scanner.getCurrentPosition(); } - if (pendingSpace) { - addInsertEdit(currentPosition, SPACE); - pendingSpace= false; - needSpace= false; - } if (startOffset + length < currentPosition) { - // don't move backwards - return; + return; // Don't move backwards } boolean savedPreserveNL= preserveNewLines; boolean savedSkipOverInactive= skipOverInactive; @@ -994,7 +988,7 @@ public class Scribe { addReplaceEdit(start, previousStart - 1, String.valueOf(buffer)); } else { - column+= (nextCharacterStart - previousStart); + column += (nextCharacterStart - previousStart); } isNewLine= false; } @@ -1041,7 +1035,7 @@ public class Scribe { column= 1; line++; } else { - column+= (nextCharacterStart - previousStart); + column += (nextCharacterStart - previousStart); } isNewLine= false; } @@ -1513,7 +1507,7 @@ public class Scribe { while (column <= indentationLevel - indentationLevel % tabLength) { buffer.append('\t'); int complement= tabLength - ((column - 1) % tabLength); // amount of space - column+= complement; + column += complement; needSpace= false; } while (column <= indentationLevel) { @@ -1540,7 +1534,7 @@ public class Scribe { if (column <= columnForLeadingIndents) { if ((column - 1 + tabLength) <= indentationLevel) { buffer.append('\t'); - column+= tabLength; + column += tabLength; } else if ((column - 1 + indentationSize) <= indentationLevel) { // print one indentation for (int i= 0, max= indentationSize; i < max; i++) { @@ -1563,7 +1557,7 @@ public class Scribe { while (column <= indentationLevel) { if ((column - 1 + tabLength) <= indentationLevel) { buffer.append('\t'); - column+= tabLength; + column += tabLength; } else if ((column - 1 + indentationSize) <= indentationLevel) { // print one indentation for (int i= 0, max= indentationSize; i < max; i++) { diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java new file mode 100644 index 00000000000..7d02aae3607 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/internal/core/MinGW.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2012, 2013 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; + +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; +import org.eclipse.cdt.core.settings.model.util.CDataUtil; +import org.eclipse.cdt.utils.PathUtil; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; + +/** + * A collection of MinGW-related utility methods. + */ +public class MinGW { + public static final String ENV_MINGW_HOME = "MINGW_HOME"; //$NON-NLS-1$ + public static final String ENV_MSYS_HOME = "MSYS_HOME"; //$NON-NLS-1$ + private static final String ENV_PATH = "PATH"; //$NON-NLS-1$ + + private static final boolean isWindowsPlatform = Platform.getOS().equals(Platform.OS_WIN32); + + private static String envPathValueCached = null; + private static String envMinGWHomeValueCached = null; + private static String minGWLocation = null; + private static boolean isMinGWLocationCached = false; + + private static String envMinGWHomeValueCached_msys = null; + private static String mSysLocation = null; + private static boolean isMSysLocationCached = false; + + private final static Map mingwLocationCache = Collections.synchronizedMap(new WeakHashMap(1)); + + /** + * @return The absolute path to MinGW root folder or {@code null} if not found + */ + private static String findMinGWRoot(String envPathValue, String envMinGWHomeValue) { + String rootValue = null; + + // Check $MINGW_HOME + if (envMinGWHomeValue != null && !envMinGWHomeValue.isEmpty()) { + IPath mingwBinDir = new Path(envMinGWHomeValue + "\\bin"); //$NON-NLS-1$ + if (mingwBinDir.toFile().isDirectory()) { + rootValue = mingwBinDir.removeLastSegments(1).toOSString(); + } + } + + // Try the mingw directory in the platform install directory + // CDT distributions like Wascana may distribute MinGW like that + if (rootValue == null) { + IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); + IPath mingwBinDir = installPath.append("mingw\\bin"); //$NON-NLS-1$ + if (mingwBinDir.toFile().isDirectory()) { + rootValue = mingwBinDir.removeLastSegments(1).toOSString(); + } + } + + // Look in PATH values. Look for mingw32-gcc.exe or x86_64-w64-mingw32-gcc.exe + if (rootValue == null) { + rootValue = findMingwInPath(envPathValue); + } + + // Try the default MinGW install dir + if (rootValue == null) { + IPath mingwBinDir = new Path("C:\\MinGW"); //$NON-NLS-1$ + if (mingwBinDir.toFile().isDirectory()) { + rootValue = mingwBinDir.toOSString(); + } + } + + return rootValue; + } + + private static String findMingwInPath(String envPath) { + if (envPath == null) { + // $PATH from user preferences + IEnvironmentVariable varPath = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_PATH, null, true); + if (varPath != null) { + envPath = varPath.getValue(); + } + } + + String mingwLocation = mingwLocationCache.get(envPath); + // check if WeakHashMap contains the key as null may be the cached value + if (mingwLocation == null && !mingwLocationCache.containsKey(envPath)) { + // Check for MinGW-w64 on Windows 64 bit, see http://mingw-w64.sourceforge.net/ + if (Platform.ARCH_X86_64.equals(Platform.getOSArch())) { + IPath gcc64Loc = PathUtil.findProgramLocation("x86_64-w64-mingw32-gcc.exe", envPath); //$NON-NLS-1$ + if (gcc64Loc != null) { + mingwLocation = gcc64Loc.removeLastSegments(2).toOSString(); + } + } + + // Look for mingw32-gcc.exe + if (mingwLocation == null) { + IPath gccLoc = PathUtil.findProgramLocation("mingw32-gcc.exe", envPath); //$NON-NLS-1$ + if (gccLoc != null) { + mingwLocation = gccLoc.removeLastSegments(2).toOSString(); + } + } + mingwLocationCache.put(envPath, mingwLocation); + } + + return mingwLocation; + } + + private static String findMSysRoot(String envMinGWHomeValue) { + String msysHome = null; + + // Look in the install location parent dir + IPath installPath = new Path(Platform.getInstallLocation().getURL().getFile()); + IPath installMsysBin = installPath.append("msys\\bin"); //$NON-NLS-1$ + if (installMsysBin.toFile().isDirectory()) { + msysHome = installMsysBin.removeLastSegments(1).toOSString(); + } + + // Look under $MINGW_HOME + if (msysHome == null) { + if (envMinGWHomeValue != null && !envMinGWHomeValue.isEmpty()) { + IPath minGwMsysBin = new Path(envMinGWHomeValue + "\\msys\\1.0\\bin"); //$NON-NLS-1$ + if (minGwMsysBin.toFile().isDirectory()) { + msysHome = minGwMsysBin.removeLastSegments(1).toOSString(); + } + } + } + + // Try under default MinGW dir + if (msysHome == null) { + IPath minGwMsysBin = new Path("C:\\MinGW\\msys\\1.0\\bin"); //$NON-NLS-1$ + if (minGwMsysBin.toFile().isDirectory()) { + msysHome = minGwMsysBin.removeLastSegments(1).toOSString(); + } + } + + // Try in default MSYS root folder + if (msysHome == null) { + IPath defaultMsysBin = new Path("C:\\msys\\1.0\\bin"); //$NON-NLS-1$ + if (defaultMsysBin.toFile().isDirectory()) { + msysHome = defaultMsysBin.removeLastSegments(1).toOSString(); + } + } + return msysHome; + } + + /** + * Find location where MinGW is installed. A number of locations is being checked, + * such as environment variable $MINGW_HOME, $PATH, Windows registry et al. + *

+ * If you use this do not cache results to ensure user preferences are accounted for. + * Please rely on internal caching. + * + * @return MinGW root ("/") path in Windows format. + */ + public static String getMinGWHome() { + if (!isWindowsPlatform) { + return null; + } + + IEnvironmentVariable varPath = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_PATH, null, true); + String envPathValue = varPath != null ? varPath.getValue() : null; + IEnvironmentVariable varMinGWHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_MINGW_HOME, null, true); + String envMinGWHomeValue = varMinGWHome != null ? varMinGWHome.getValue() : null; + + // isMinGWLocationCached is used to figure fact of caching when all cached objects are null + if (isMinGWLocationCached && CDataUtil.objectsEqual(envPathValue, envPathValueCached) && CDataUtil.objectsEqual(envMinGWHomeValue, envMinGWHomeValueCached)) { + return minGWLocation; + } + + minGWLocation = findMinGWRoot(envPathValue, envMinGWHomeValue); + envPathValueCached = envPathValue; + envMinGWHomeValueCached = envMinGWHomeValue; + isMinGWLocationCached = true; + + return minGWLocation; + } + + /** + * Find location where MSys is installed. Environment variable $MSYS_HOME and + * some predetermined locations are being checked. + *

+ * If you use this do not cache results to ensure user preferences are accounted for. + * Please rely on internal caching. + * + * @return MSys root ("/") path in Windows format. + */ + public static String getMSysHome() { + if (!isWindowsPlatform) { + return null; + } + + // Use $MSYS_HOME if defined + IEnvironmentVariable varMsysHome = CCorePlugin.getDefault().getBuildEnvironmentManager().getVariable(ENV_MSYS_HOME, null, true); + String msysHomeValue = varMsysHome != null ? varMsysHome.getValue() : null; + if (msysHomeValue != null) { + return msysHomeValue; + } + + String envMinGWHomeValue = getMinGWHome(); + + // isMSysLocationCached is used to figure whether it was cached when all cached objects are null + if (isMSysLocationCached && CDataUtil.objectsEqual(envMinGWHomeValue, envMinGWHomeValueCached_msys)) { + return mSysLocation; + } + + mSysLocation = findMSysRoot(envMinGWHomeValue); + envMinGWHomeValueCached_msys = envMinGWHomeValue; + isMSysLocationCached = true; + + return mSysLocation; + } + + /** + * Check if MinGW is available in the path. + * + * @param envPath - list of directories to search for MinGW separated + * by path separator (format of environment variable $PATH) + * or {@code null} to use current $PATH. + * @return {@code true} if MinGW is available, {@code false} otherwise. + */ + public static boolean isAvailable(String envPath) { + return isWindowsPlatform && findMingwInPath(envPath) != null; + } + + /** + * Check if MinGW is available in $PATH. + * + * @return {@code true} if MinGW is available, {@code false} otherwise. + */ + public static boolean isAvailable() { + return isWindowsPlatform && findMingwInPath(null) != null; + } +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/IEnvironmentChangeEvent.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/IEnvironmentChangeEvent.java new file mode 100644 index 00000000000..9683e1d48a0 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/IEnvironmentChangeEvent.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2013, 2013 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.utils.envvar; + +import org.eclipse.cdt.core.envvar.IEnvironmentVariable; + +/** + * A class to describe changes to environment variables defined by user + * on CDT Environment page in Preferences. + * + * @since 5.5 + */ +public interface IEnvironmentChangeEvent { + /** + * @return an array of environment variables before the changes. If there are no variables, + * returns an empty array. + */ + public IEnvironmentVariable[] getOldVariables(); + + /** + * @return an array of environment variables after the changes. If there are no variables, + * returns an empty array. + */ + public IEnvironmentVariable[] getNewVariables(); + +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/IEnvironmentChangeListener.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/IEnvironmentChangeListener.java new file mode 100644 index 00000000000..6adea60095c --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/IEnvironmentChangeListener.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2013, 2013 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.utils.envvar; + + +/** + * Interface for listeners to changes in environment variables defined by user + * on CDT Environment page in Preferences. + * + * @since 5.5 + */ +public interface IEnvironmentChangeListener { + /** + * Indicates that environment variables have been changed. + * + * @param event - details of the event. + */ + public void handleEvent(IEnvironmentChangeEvent event); +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/PrefsStorableEnvironment.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/PrefsStorableEnvironment.java index 6742fa3df01..7bbe61d4345 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/PrefsStorableEnvironment.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/PrefsStorableEnvironment.java @@ -22,7 +22,9 @@ import java.util.Set; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; import org.eclipse.cdt.core.settings.model.ICStorageElement; +import org.eclipse.cdt.internal.core.envvar.EnvironmentChangeEvent; import org.eclipse.cdt.utils.envvar.StorableEnvironmentLoader.ISerializeInfo; +import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; @@ -72,6 +74,8 @@ public class PrefsStorableEnvironment extends StorableEnvironment { private boolean fAppendChanged = false; private boolean fAppendContributedChanged = false; + private static ListenerList fEnvironmentChangeListeners = new ListenerList(ListenerList.IDENTITY); + /** A listener for changes in the backing store */ private static class PrefListener implements IPreferenceChangeListener, INodeChangeListener { @@ -401,6 +405,9 @@ public class PrefsStorableEnvironment extends StorableEnvironment { void serialize() { if (!isDirty()) return; + + HashMap oldEnv = new HashMap(fCachedSerialEnv); + Preferences element = fSerialEnv.getNode().node(fSerialEnv.getPrefName()); element.putBoolean(ATTRIBUTE_APPEND, fAppend); fAppendChanged = false; @@ -430,6 +437,11 @@ public class PrefsStorableEnvironment extends StorableEnvironment { } catch (BackingStoreException e) { CCorePlugin.log(e); } + + if (fAppendChanged || fAppendContributedChanged || !oldEnv.equals(fCachedSerialEnv)) { + IEnvironmentChangeEvent event = new EnvironmentChangeEvent(oldEnv.values(), fCachedSerialEnv.values()); + notifyLanguageSettingsChangeListeners(event); + } } @Override @@ -535,6 +547,37 @@ public class PrefsStorableEnvironment extends StorableEnvironment { (fDeletedVariables != null && !fDeletedVariables.isEmpty()); } + /** + * Adds a listener that will be notified of changes in environment variables. + * + * @param listener - the listener to add + * @since 5.5 + */ + public void registerEnvironmentChangeListener(IEnvironmentChangeListener listener) { + fEnvironmentChangeListeners.add(listener); + } + + /** + * Removes an environment variables change listener. + * + * @param listener - the listener to remove. + * @since 5.5 + */ + public void unregisterEnvironmentChangeListener(IEnvironmentChangeListener listener) { + fEnvironmentChangeListeners.remove(listener); + } + + /** + * Notifies all environment change listeners of a change in environment. + * + * @param event - the {@link IEnvironmentChangeEvent} event to be broadcast. + */ + private static void notifyLanguageSettingsChangeListeners(IEnvironmentChangeEvent event) { + for (Object listener : fEnvironmentChangeListeners.getListeners()) { + ((IEnvironmentChangeListener) listener).handleEvent(event); + } + } + @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/core/org.eclipse.cdt.ui.tests/resources/formatter/sample/After.cpp b/core/org.eclipse.cdt.ui.tests/resources/formatter/sample/After.cpp index 89ede2db0a6..674808249f7 100644 --- a/core/org.eclipse.cdt.ui.tests/resources/formatter/sample/After.cpp +++ b/core/org.eclipse.cdt.ui.tests/resources/formatter/sample/After.cpp @@ -24,13 +24,15 @@ const SimpleStruct simpleStruct = { 1, "mySimple", 0.1232 }; \ } -const SimpleStruct array[] = { { SIZEOF( simpleStruct, num ), +const SimpleStruct array[] = { { +SIZEOF( simpleStruct, num ), #if FOO "foo" # else "bar" #endif - , 0.5 }, { SIZEOF( simpleStruct, floatNum ), "name", 1.1 } }; + , 0.5 }, { +SIZEOF( simpleStruct, floatNum ), "name", 1.1 } }; // single line outside scope diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java index e1836964b4f..70109e01c1b 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestBase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012 Google, Inc and others. + * Copyright (c) 2012, 2013 Google, Inc 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 @@ -22,7 +22,6 @@ import org.eclipse.core.filesystem.URIUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.text.TextSelection; @@ -56,8 +55,6 @@ import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext; * Common base for refactoring tests. */ public abstract class RefactoringTestBase extends BaseTestCase { - protected static final NullProgressMonitor NULL_PROGRESS_MONITOR = new NullProgressMonitor(); - /** Allows empty files to be created during test setup. */ protected boolean createEmptyFiles = true; /** See {@link PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER} */ @@ -137,7 +134,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { public void tearDown() throws Exception { if (cproject != null) { cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, - NULL_PROGRESS_MONITOR); + npm()); } resetPreferences(); super.tearDown(); @@ -175,7 +172,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { protected void executeRefactoring(Refactoring refactoring, RefactoringContext context, boolean withUserInput, boolean expectedSuccess) throws CoreException, Exception { try { - RefactoringStatus initialStatus = refactoring.checkInitialConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus initialStatus = refactoring.checkInitialConditions(npm()); if (!expectedSuccess) { assertStatusFatalError(initialStatus); return; @@ -190,7 +187,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { if (withUserInput) simulateUserInput(); - RefactoringStatus finalStatus = refactoring.checkFinalConditions(NULL_PROGRESS_MONITOR); + RefactoringStatus finalStatus = refactoring.checkFinalConditions(npm()); if (expectedFinalWarnings != 0) { assertStatusWarning(finalStatus, expectedFinalWarnings); } else if (expectedFinalInfos != 0) { @@ -198,8 +195,8 @@ public abstract class RefactoringTestBase extends BaseTestCase { } else { assertStatusOk(finalStatus); } - Change change = refactoring.createChange(NULL_PROGRESS_MONITOR); - change.perform(NULL_PROGRESS_MONITOR); + Change change = refactoring.createChange(npm()); + change.perform(npm()); } finally { if (context != null) context.dispose(); @@ -212,7 +209,7 @@ public abstract class RefactoringTestBase extends BaseTestCase { RefactoringHistory history = RefactoringHistoryService.getInstance().readRefactoringHistory( new ByteArrayInputStream(scriptSource.getBytes()), 0); for (RefactoringDescriptorProxy proxy : history.getDescriptors()) { - RefactoringDescriptor descriptor = proxy.requestDescriptor(NULL_PROGRESS_MONITOR); + RefactoringDescriptor descriptor = proxy.requestDescriptor(npm()); RefactoringStatus status = new RefactoringStatus(); RefactoringContext context = descriptor.createRefactoringContext(status); assertTrue(status.isOK()); diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java index 3969debfd10..496e5c0bca4 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/RefactoringTestSuite.java @@ -22,6 +22,7 @@ import org.eclipse.cdt.ui.tests.refactoring.extractlocalvariable.ExtractLocalVar import org.eclipse.cdt.ui.tests.refactoring.gettersandsetters.GenerateGettersAndSettersTest; import org.eclipse.cdt.ui.tests.refactoring.hidemethod.HideMethodRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.implementmethod.ImplementMethodRefactoringTest; +import org.eclipse.cdt.ui.tests.refactoring.includes.IncludesTestSuite; import org.eclipse.cdt.ui.tests.refactoring.rename.RenameRegressionTests; import org.eclipse.cdt.ui.tests.refactoring.togglefunction.ToggleRefactoringTest; import org.eclipse.cdt.ui.tests.refactoring.utils.UtilTestSuite; @@ -42,6 +43,7 @@ public class RefactoringTestSuite extends TestSuite { suite.addTest(ImplementMethodRefactoringTest.suite()); suite.addTest(ExtractLocalVariableRefactoringTest.suite()); suite.addTest(ToggleRefactoringTest.suite()); + suite.addTest(IncludesTestSuite.suite()); return suite; } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java index 4446a6de752..76a7edcea93 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java @@ -1,11 +1,11 @@ /******************************************************************************* * Rapperswil, University of applied sciences 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: + * 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: * Institute for Software - initial API and implementation * Sergey Prigogin (Google) *******************************************************************************/ @@ -181,7 +181,6 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { assertRefactoringSuccess(); } - //A.h //#ifndef A_H_ //#define A_H_ @@ -308,7 +307,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { public void testLocalVariableDeclaration_3() throws Exception { assertRefactoringSuccess(); } - + //A.h //#ifndef A_H_ //#define A_H_ diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java new file mode 100644 index 00000000000..c9329d20606 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/BindingClassifierTest.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2012, 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import junit.framework.TestSuite; + +import com.ibm.icu.text.MessageFormat; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexManager; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.testplugin.util.OneSourceMultipleHeadersTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; + +import org.eclipse.cdt.internal.ui.refactoring.includes.BindingClassifier; +import org.eclipse.cdt.internal.ui.refactoring.includes.InclusionContext; + +public class BindingClassifierTest extends OneSourceMultipleHeadersTestCase { + private IIndex fIndex; + private InclusionContext fContext; + private BindingClassifier fBindingClassifier; + + public BindingClassifierTest() { + super(new TestSourceReader(CTestPlugin.getDefault().getBundle(), "ui", BindingClassifierTest.class), true); + } + + public static TestSuite suite() { + return suite(BindingClassifierTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + IASTTranslationUnit ast = getAst(); + fIndex = CCorePlugin.getIndexManager().getIndex(getCProject(), + IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); + fIndex.acquireReadLock(); + ITranslationUnit tu = ast.getOriginatingTranslationUnit(); + fContext = new InclusionContext(tu, fIndex); + fBindingClassifier = new BindingClassifier(fContext); + fBindingClassifier.classifyNodeContents(ast); + } + + @Override + protected void tearDown() throws Exception { + fIndex.releaseReadLock(); + super.tearDown(); + } + + private void assertDefined(String... names) { + assertExpectedBindings(names, fBindingClassifier.getBindingsToDefine(), "defined"); + } + + private void assertDeclared(String... names) { + assertExpectedBindings(names, fBindingClassifier.getBindingsToDeclare(), "declared"); + } + + private void assertExpectedBindings(String[] expectedNames, Set bindings, String verb) { + Set expected = new TreeSet(Arrays.asList(expectedNames)); + Set extra = new TreeSet(); + for (IBinding binding : bindings) { + extra.add(binding.getName()); + } + Set missing = new TreeSet(expected); + missing.removeAll(extra); + extra.removeAll(expected); + if (extra.isEmpty() && missing.isEmpty()) + return; + List errors = new ArrayList(2); + if (!missing.isEmpty()) { + errors.add(MessageFormat.format("{0,choice,1#Binding|1 strings, String delimiter) { + StringBuilder buf = new StringBuilder(); + for (String str : strings) { + if (buf.length() != 0) + buf.append(delimiter); + buf.append(str); + } + return buf.toString(); + } + + // class A; + // typedef A* td1; + // typedef td1* td2; + // td2 f(); + + // A* a = *f(); + public void testTypedef_1() throws Exception { + assertDefined("f"); + assertDeclared("A"); + } + + // class A; + // typedef A* td1; + // typedef td1* td2; + // td2 f(); + + // td1 a = *f(); + public void testTypedef_2() throws Exception { + assertDefined("f", "td1"); + } + + // class A { int x; }; + // typedef A* td; + // td f(); + + // int a = f()->x; + public void testClassMember() throws Exception { + assertDefined("f", "A"); + } + + // class A { void m(); }; + + // void test(A* a) { + // a->m(); + // } + public void testMethodCall() throws Exception { + assertDefined("A"); + } + + // struct A {}; + // struct B {}; + + // struct C { + // A a; + // static B b; + // }; + public void testFieldReference() throws Exception { + assertDefined("A"); + assertDeclared("B"); + } + + // int a; + + // void test() { + // void* x = &a; + // } + public void testVariableReference() throws Exception { + assertDefined("a"); // Forward declaration of variables is not allowed by default. + } + + // struct A { + // void operator()(int p); + // }; + // const A a; + + // void test() { + // a(1); + // } + public void testCallOperator() throws Exception { + assertDefined("A", "a"); // Forward declaration of variables is not allowed by default. + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java new file mode 100644 index 00000000000..e4594327976 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestBase.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.preference.IPreferenceStore; +import org.osgi.framework.Bundle; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.IPDOMManager; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.model.CoreModel; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.core.testplugin.util.BaseTestCase; +import org.eclipse.cdt.core.testplugin.util.TestSourceReader; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; +import org.eclipse.cdt.ui.tests.refactoring.TestSourceFile; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; + +/** + * Common base for include-related tests. + */ +public abstract class IncludesTestBase extends BaseTestCase { + protected final String LINE_DELIMITER = "\n"; + + protected static class FirstHeaderChooser implements IHeaderChooser { + @Override + public IPath chooseHeader(String bindingName, Collection headers) { + return headers.isEmpty() ? null : headers.iterator().next(); + } + } + + /** Expected counts of errors, warnings and info messages */ + protected int expectedInitialErrors; + protected int expectedInitialWarnings; + protected int expectedFinalWarnings; + protected int expectedFinalInfos; + + protected IIndex index; + protected ICProject cproject; + protected IASTTranslationUnit ast; + protected TestSourceFile selectedFile; + private StringBuilder[] testData; + private boolean cpp = true; + private final Set testFiles = new LinkedHashSet(); + + protected IncludesTestBase() { + super(); + } + + protected IncludesTestBase(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + resetPreferences(); + cproject = cpp ? + CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) : + CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER); + Bundle bundle = CTestPlugin.getDefault().getBundle(); + CharSequence[] testData = TestSourceReader.getContentsForTest(bundle, "ui", getClass(), getName(), 0); + + IFile sourceFile = null; + for (CharSequence contents : testData) { + TestSourceFile testFile = null; + boolean expectedResult = false; + BufferedReader reader = new BufferedReader(new StringReader(contents.toString())); + String line; + while ((line = reader.readLine()) != null) { + String trimmedLine = line.trim(); + if (testFile == null) { + assertTrue("Invalid file name \"" + trimmedLine + "\"", trimmedLine.matches("^(\\w+/)*\\w+\\.\\w+$")); + testFile = new TestSourceFile(trimmedLine); + } else if (isResultDelimiter(trimmedLine)) { + expectedResult = true; + } else if (expectedResult) { + testFile.addLineToExpectedSource(line); + } else { + testFile.addLineToSource(line); + } + } + reader.close(); + + sourceFile = TestSourceReader.createFile(cproject.getProject(), new Path(testFile.getName()), + testFile.getSource()); + testFiles.add(testFile); + selectedFile = testFile; + } + CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER); + waitForIndexer(cproject); + + index= CCorePlugin.getIndexManager().getIndex(cproject); + + index.acquireReadLock(); + ast = TestSourceReader.createIndexBasedAST(index, cproject, sourceFile); + } + + @Override + public void tearDown() throws Exception { + if (cproject != null) { + cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, npm()); + } + resetPreferences(); + super.tearDown(); + } + + protected ICProject getCProject() { + return cproject; + } + + protected TestSourceFile getSelectedTestFile() { + return selectedFile; + } + + protected IFile getSelectedFile() { + if (selectedFile == null) + return null; + return cproject.getProject().getFile(new Path(selectedFile.getName())); + } + + protected ITranslationUnit getSelectedTranslationUnit() { + IFile file = getSelectedFile(); + if (file == null) + return null; + return (ITranslationUnit) CoreModel.getDefault().create(file); + } + + protected boolean isCpp() { + return cpp; + } + + protected void setCpp(boolean cpp) { + this.cpp = cpp; + } + + private boolean isResultDelimiter(String str) { + if (str.isEmpty()) + return false; + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) != '=') + return false; + } + return true; + } + + protected void resetPreferences() { + } + + protected IPreferenceStore getPreferenceStore() { + return CUIPlugin.getDefault().getPreferenceStore(); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java new file mode 100644 index 00000000000..cb49b4da418 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/IncludesTestSuite.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class IncludesTestSuite extends TestSuite { + + public static Test suite() throws Exception { + IncludesTestSuite suite = new IncludesTestSuite(); + suite.addTest(BindingClassifierTest.suite()); + suite.addTest(OrganizeIncludesTest.suite()); + return suite; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java new file mode 100644 index 00000000000..50fa7d63d68 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/includes/OrganizeIncludesTest.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.refactoring.includes; + +import java.util.List; + +import junit.framework.Test; + +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; + +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.PreferenceConstants; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; + +/** + * Tests for Extract Function refactoring. + */ +public class OrganizeIncludesTest extends IncludesTestBase { + + public OrganizeIncludesTest() { + super(); + } + + public OrganizeIncludesTest(String name) { + super(name); + } + + public static Test suite() { + return suite(OrganizeIncludesTest.class); + } + + @Override + protected void resetPreferences() { + super.resetPreferences(); + getPreferenceStore().setToDefault(PreferenceConstants.INCLUDES_UNUSED_STATEMENTS_DISPOSITION); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_COMPOSITE_TYPES); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_ENUMS); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_FUNCTIONS); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_TEMPLATES); + getPreferenceStore().setToDefault(PreferenceConstants.FORWARD_DECLARE_NAMESPACE_ELEMENTS); + getPreferenceStore().setToDefault(PreferenceConstants.INCLUDES_ALLOW_REORDERING); + } + + private void assertExpectedResults() throws Exception { + String actual = organizeIncludes(ast.getOriginatingTranslationUnit()); + assertEquals(selectedFile.getExpectedSource(), actual); + } + + /** + * Invokes include organizer and returns the new contents of the translation unit. + */ + private String organizeIncludes(ITranslationUnit tu) throws Exception { + IHeaderChooser headerChooser = new FirstHeaderChooser(); + IncludeOrganizer organizer = new IncludeOrganizer(tu, index, LINE_DELIMITER, headerChooser); + List edits = organizer.organizeIncludes(ast); + IDocument document = new Document(new String(tu.getContents())); + if (!edits.isEmpty()) { + // Apply text edits. + MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits.toArray(new TextEdit[edits.size()])); + edit.apply(document); + } + return document.get(); + } + + //h1.h + //typedef int my_type; + + //A.h + //class A { + // my_type m1(); + //}; + + //A.cpp + //// Comment line 1 + //// Comment line 2 + // + //// Comment for m1 + //my_type A::m1() { + // return 0; + //} + //==================== + //// Comment line 1 + //// Comment line 2 + // + //#include "A.h" + // + //#include "h1.h" + // + //// Comment for m1 + //my_type A::m1() { + // return 0; + //} + public void testNoExistingIncludes() throws Exception { + assertExpectedResults(); + } + + //B.h + //class B {}; + + //C.h + //class C {}; + + //A.h + //#if !defined(INCLUDE_GUARD) + //#define INCLUDE_GUARD + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A { + // B f; + // C m(); + //}; + //#endif // INCLUDE_GUARD + //==================== + //#if !defined(INCLUDE_GUARD) + //#define INCLUDE_GUARD + //// Comment line 1 + //// Comment line 2 + // + //#include "B.h" + // + //class C; + // + //// Comment for A + //class A { + // B f; + // C m(); + //}; + //#endif // INCLUDE_GUARD + public void testIncludeGuards() throws Exception { + assertExpectedResults(); + } + + //B.h + //template class B {}; + + //C.h + //class C {}; + + //A.h + //#pragma once + //namespace ns { + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A : public B {}; + //} // namespace ns + //==================== + //#pragma once + // + //#include "B.h" + //#include "C.h" + // + //namespace ns { + //// Comment line 1 + //// Comment line 2 + // + //// Comment for A + //class A : public B {}; + //} // namespace ns + public void testPragmaOnce() throws Exception { + assertExpectedResults(); + } + + //h1.h + //typedef int Type1; + + //h2.h + //class Type2 {}; + + //h3.h + //enum Type3 { ONE, TWO }; + + //h4.h + //class Unrelated {}; + + //A.h + //#include "h1.h" + //class Type2; + //enum class Type3; + //extern Type1 f1(); + //extern Type2 f2(); + //extern Type3 f3(); + + //A.cpp + //// Comment + // + //#include "h2.h" /* Required */ // another comment + //#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + //==================== + //// Comment + // + //#include "A.h" + // + ////#include "h1.h" // Unused + //#include "h2.h" /* Required */ // another comment + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + // + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + public void testExistingIncludes() throws Exception { + assertExpectedResults(); + } + + //h1.h + //typedef int Type1; + + //h2.h + //class Type2 {}; + + //h3.h + //enum class Type3 { ONE, TWO }; + + //h4.h + //class Unrelated {}; + + //A.h + //#include "h1.h" + //class Type2; + //enum class Type3; + //extern Type1 f1(); + //extern Type2 f2(); + //extern Type3 f3(); + + //A.cpp + //// Comment + // + //#include "h2.h" /* Required */ // another comment + //#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + //==================== + //// Comment + // + //#include "h2.h" /* Required */ // another comment + ////#include "h1.h" // Unused + //#include "h3.h" + //#include "h5.h" // Unresolved includes are preserved + //#include "A.h" + // + //#ifdef SOME_OTHER_TIME + //#include "h4.h" // Unused but unsafe to remove + //#endif + // + //void test() { + // f1(); + // f2(); + // f3(); + //} + public void testExistingIncludesNoReordering() throws Exception { + getPreferenceStore().setValue(PreferenceConstants.INCLUDES_ALLOW_REORDERING, false); + assertExpectedResults(); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java index 7d9ff572f11..4d08478705d 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/utils/DefinitionFinderTest.java @@ -88,7 +88,7 @@ public class DefinitionFinderTest extends RefactoringTestBase { for (IASTDeclaration declaration : ast.getDeclarations()) { if (declaration instanceof IASTSimpleDeclaration) { IASTName name = ((IASTSimpleDeclaration) declaration).getDeclarators()[0].getName(); - assertNotNull(DefinitionFinder.getDefinition(name, refactoringContext, NULL_PROGRESS_MONITOR)); + assertNotNull(DefinitionFinder.getDefinition(name, refactoringContext, npm())); } } } finally { diff --git a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF index eb9c2d464a2..f06ce79399a 100644 --- a/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.ui/META-INF/MANIFEST.MF @@ -42,6 +42,7 @@ Export-Package: org.eclipse.cdt.internal.corext;x-internal:=true, org.eclipse.cdt.internal.ui.refactoring.gettersandsetters;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.hidemethod;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.implementmethod;x-friends:="org.eclipse.cdt.ui.tests", + org.eclipse.cdt.internal.ui.refactoring.includes;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.rename;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.togglefunction;x-friends:="org.eclipse.cdt.ui.tests", org.eclipse.cdt.internal.ui.refactoring.utils;x-friends:="org.eclipse.cdt.ui.tests", diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index 18028baf180..5cdafd68387 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2003, 2012 IBM Corporation, QNX Software Systems, and others. +# Copyright (c) 2003, 2013 IBM Corporation, QNX Software Systems, 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 @@ -196,6 +196,8 @@ CPluginFileTypesPreferencePage.name=File Types CodeStylePreferencePage.name=Code Style codeTemplatePreferencePage.name=Code Templates codeFormatterPreferencePage.name=Formatter +includeStylePreferencePage.name=Include Style +organizeIncludesPreferencePage.name=Organize Includes nameStylePreferencePage.name=Name Style CodeAssistPreferencePage.name=Content Assist CodeAssistAdvancedPreferencePage.name=Advanced @@ -274,6 +276,10 @@ ActionDefinition.openType.description= Open an element in an Editor ActionDefinition.addInclude.name= Add Include ActionDefinition.addInclude.description= Create include statement on selection +#Organize Includes +ActionDefinition.addInclude.name= Organize Includes +ActionDefinition.addInclude.description= Evaluates all required includes and replaces the current includes + #Sort Lines ActionDefinition.sortLines.name= Sort Lines ActionDefinition.sortLines.description= Sort selected lines alphabetically @@ -566,7 +572,9 @@ preferenceKeywords.common=c cpp cplusplus cdt preferenceKeywords.codeformatter=profile codestyle project specific comment indentation brace white space blank line new control statement wrapping tab parenthesis bracket preferenceKeywords.codestyle=class member visibility order ordering preferenceKeywords.codetemplates=comment code constructor method file type content -preferenceKeywords.namestyle=name file getter setter field variable +preferenceKeywords.namestyle=name style file getter setter field variable +preferenceKeywords.includestyle=include includes style partner system header file system +preferenceKeywords.organizeincludes=include includes organize preferenceKeywords.todo=case sensitive task tag todo xxx fix fixme project comments preferenceKeywords.indexer=index skip references type macro search build configuration cache memory performance @@ -589,6 +597,7 @@ renameParticipant.name = Source Folder Rename FormatAction.label= &Format IndentAction.label= Correct &Indentation +OrganizeIncludesAction.label= Or&ganize Includes AddIncludeAction.label= A&dd Include SortLinesAction.label= Sor&t Lines CommentAction.label= Co&mment diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 84082641ddc..2543f789d9f 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -1232,6 +1232,22 @@ + + + + + + + + + + @@ -1862,6 +1884,13 @@ menubarPath="org.eclipse.jdt.ui.source.menu/importGroup" id="org.eclipse.cdt.ui.actions.SortLines"> + + + + + + @@ -4608,6 +4649,9 @@ + + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java new file mode 100644 index 00000000000..5c4545bca92 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/BusyCursorJobRunner.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IProgressService; + +import org.eclipse.cdt.ui.CUIPlugin; + +/** + * Synchronously executes a {@link Job} while allowing user to cancel it if it takes too long. + */ +public final class BusyCursorJobRunner { + /** + * Adapts a {@link Job} to be an {@link IRunnableWithProgress}. + */ + private static class JobRunnableWithProgressAdapter implements IRunnableWithProgress { + private final Job job; + + /** + * Creates the {@link IRunnableWithProgress} from the {@link Job}. + */ + public JobRunnableWithProgressAdapter(Job job) { + this.job = job; + } + + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + IStatus result; + try { + monitor.beginTask(job.getName(), IProgressMonitor.UNKNOWN); + result = executeAndWait(job, monitor); + } catch (RuntimeException e) { + throw new InvocationTargetException(e); + } + + switch (result.getSeverity()) { + case IStatus.CANCEL: + throw new InterruptedException(); + + case IStatus.ERROR: + if (result.getException() instanceof OperationCanceledException) { + throw new InterruptedException(); + } + throw new InvocationTargetException(new CoreException(result)); + } + } + } + + /** + * Runs the given job and waits for it to finish. If executing in the UI thread, sets the cursor + * to busy while the job is being executed. + * + * @param job the job to execute + * @return the status reflecting the result of the job execution + */ + public static IStatus execute(Job job) { + boolean inUiThread = Thread.currentThread() == Display.getDefault().getThread(); + if (inUiThread) { + return busyCursorWhile(job); + } + return executeAndWait(job, new NullProgressMonitor()); + } + + private static IStatus busyCursorWhile(Job job) { + try { + IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); + progressService.busyCursorWhile(new JobRunnableWithProgressAdapter(job)); + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; // Operation was cancelled. + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + if (targetException instanceof CoreException) { + return ((CoreException) targetException).getStatus(); + } + return new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, e.getMessage(), e); + } + return Status.OK_STATUS; + } + + private static IStatus executeAndWait(final Job job, IProgressMonitor monitor) { + final IStatus[] statusHolder = new IStatus[1]; + + IJobManager jobManager = Job.getJobManager(); + JobChangeAdapter listener = new JobChangeAdapter() { + @Override + public void done(IJobChangeEvent event) { + super.done(event); + if (event.getJob() == job) { + synchronized (statusHolder) { + statusHolder[0] = event.getResult(); + statusHolder.notifyAll(); + } + } + } + }; + jobManager.addJobChangeListener(listener); + job.schedule(); + + try { + synchronized (statusHolder) { + while (statusHolder[0] == null) { + try { + statusHolder.wait(100); + if (monitor.isCanceled()) { + job.cancel(); + } + } catch (InterruptedException e) { + job.cancel(); + return Status.CANCEL_STATUS; + } + } + + return statusHolder[0]; + } + } finally { + monitor.done(); + jobManager.removeJobChangeListener(listener); + } + } + + private BusyCursorJobRunner() {} +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java index fd83696f119..a142be73da1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/ICHelpContextIds.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2010 IBM Corporation and others. + * Copyright (c) 2006, 2013 IBM Corporation 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 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * QNX Software System * Anton Leherbauer (Wind River Systems) + * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.ui; @@ -19,7 +20,6 @@ import org.eclipse.cdt.ui.CUIPlugin; *

* This interface contains constants only; it is not intended to be implemented or extended. *

- * */ public interface ICHelpContextIds { public static final String PREFIX = CUIPlugin.PLUGIN_ID + "."; //$NON-NLS-1$ @@ -33,9 +33,10 @@ public interface ICHelpContextIds { public static final String OPEN_PROJECT_WIZARD_ACTION = PREFIX + "open_project_wizard_action"; //$NON-NLS-1$ public static final String CONVERT_TO_CCPP_WIZARD_PAGE = PREFIX + "cdt_t_conv_proj_context"; //$NON-NLS-1$ public static final String NEW_C_FILE_WIZARD_PAGE = PREFIX + "cdt_creating_cpp_file_context"; //$NON-NLS-1$ - // Actions + // Actions public static final String ADD_INCLUDE_ON_SELECTION_ACTION = PREFIX + "add_includes_on_selection_action_context"; //$NON-NLS-1$; + public static final String ORGANIZE_INCLUDES_ACTION = PREFIX + "organize_includes_action"; //$NON-NLS-1$; public static final String FILTER_PUBLIC_ACTION= PREFIX + "filter_public_action"; //$NON-NLS-1$ public static final String FILTER_FIELDS_ACTION= PREFIX + "filter_fields_action"; //$NON-NLS-1$ public static final String FILTER_STATIC_ACTION= PREFIX + "filter_static_action"; //$NON-NLS-1$ @@ -87,6 +88,8 @@ public interface ICHelpContextIds { public static final String SPELLING_CONFIGURATION_BLOCK= PREFIX + "spelling_configuration_block_context"; //$NON-NLS-1$ public static final String CODE_STYLE_PREFERENCE_PAGE = PREFIX + "code_style_preference_context"; //$NON-NLS-1$ public static final String CODE_TEMPLATES_PREFERENCE_PAGE = PREFIX + "code_templates_preference_context"; //$NON-NLS-1$ + public static final String INCLUDE_STYLE_PREFERENCE_PAGE = PREFIX + "include_style_preference_context"; //$NON-NLS-1$ + public static final String ORGANIZE_INCLUDES_PREFERENCE_PAGE = PREFIX + "organize_includes_preference_context"; //$NON-NLS-1$ public static final String NAME_STYLE_PREFERENCE_PAGE = PREFIX + "name_style_preference_context"; //$NON-NLS-1$ // Console view diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java index 1338d9d257a..4894e4aa2c8 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorActionContributor.java @@ -33,14 +33,14 @@ import org.eclipse.ui.texteditor.RetargetTextEditorAction; import org.eclipse.cdt.ui.actions.CdtActionConstants; import org.eclipse.cdt.internal.ui.IContextMenuConstants; +import org.eclipse.cdt.internal.ui.actions.FindWordAction; +import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; +import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; +import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectHistoryAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectNextAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectPreviousAction; import org.eclipse.cdt.internal.ui.actions.StructureSelectionAction; -import org.eclipse.cdt.internal.ui.actions.StructureSelectEnclosingAction; -import org.eclipse.cdt.internal.ui.actions.FindWordAction; -import org.eclipse.cdt.internal.ui.actions.GoToNextPreviousMemberAction; -import org.eclipse.cdt.internal.ui.actions.GotoNextBookmarkAction; public class CEditorActionContributor extends TextEditorActionContributor { @@ -229,6 +229,7 @@ public class CEditorActionContributor extends TextEditorActionContributor { bars.setGlobalActionHandler(CdtActionConstants.REMOVE_BLOCK_COMMENT, getAction(textEditor, "RemoveBlockComment")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.INDENT, getAction(textEditor, "Indent")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.ADD_INCLUDE, getAction(textEditor, "AddIncludeOnSelection")); //$NON-NLS-1$ + bars.setGlobalActionHandler(CdtActionConstants.ORGANIZE_INCLUDES, getAction(textEditor, "OrganizeIncludes")); //$NON-NLS-1$ bars.setGlobalActionHandler(CdtActionConstants.SORT_LINES, getAction(textEditor, "SortLines")); //$NON-NLS-1$ IAction action= getAction(textEditor, ITextEditorActionConstants.REFRESH); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java index 9c27d36db58..5d0e53a8d03 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.java @@ -36,6 +36,15 @@ public final class CEditorMessages extends NLS { public static String AddIncludeOnSelection_insertion_failed; public static String AddIncludeOnSelection_help_provider_error; public static String AddIncludesOperation_description; + public static String OrganizeIncludes_label; + public static String OrganizeIncludes_description; + public static String OrganizeIncludes_action; + public static String OrganizeIncludes_error_title; + public static String OrganizeIncludes_insertion_failed; + public static String OrganizeIncludes_help_provider_error; + public static String OrganizeIncludes_failed; + public static String OrganizeIncludes_choose_header; + public static String OrganizeIncludesOperation_description; public static String ShowInCView_description; public static String ShowInCView_label; public static String ShowInCView_tooltip; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties index c189c20f1a1..62aa9c4416e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties @@ -1,5 +1,5 @@ ######################################### -# Copyright (c) 2005, 2011 IBM Corporation and others. +# Copyright (c) 2005, 2012 IBM Corporation 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 @@ -12,6 +12,7 @@ # Markus Schorn (Wind River Systems) # Sergey Prigogin (Google) # Tomasz Wesolowski +# Mathias Kunter ######################################### AddIncludeOnSelection_label=Add Include @@ -21,6 +22,16 @@ AddIncludeOnSelection_insertion_failed=Adding include statements failed AddIncludeOnSelection_help_provider_error=Help provider error AddIncludesOperation_description=Adding include statement +OrganizeIncludes_label=Organize Includes +OrganizeIncludes_description=Organize includes for current file +OrganizeIncludes_action=Organizing includes +OrganizeIncludes_error_title=Error Organizing Includes +OrganizeIncludes_insertion_failed=Adding include statements failed +OrganizeIncludes_help_provider_error=Help provider error +OrganizeIncludes_failed=Organize Includes operation failed +OrganizeIncludes_choose_header=Choose a header file to include for symbol ''{0}'' +OrganizeIncludesOperation_description=Organizing include statements + ShowInCView_description=Show the current resource in the C/C++ Projects view ShowInCView_label=Show in C/C++ Projects ShowInCView_tooltip=Show current resource in C/C++ Projects view diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties index d020935426b..bd462477db4 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ConstructedCEditorMessages.properties @@ -18,6 +18,10 @@ AddIncludeOnSelection.label=A&dd Include AddIncludeOnSelection.description=Add include statement for selected name AddIncludeOnSelection.tooltip=Adds an include statement for selected name +OrganizeIncludes.label=Or&ganize Includes +OrganizeIncludes.tooltip=Evaluate All Required Includes and Replace the Current Ones +OrganizeIncludes.description=Evaluates all required includes and replaces the current ones + SortLines.label=Sort Lines SortLines.tooltip=Sort Selected Lines Alphabetically SortLines.description=Sorts selected lines alphabetically diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java index 33fd516ea1f..9808ef10801 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICEditorActionDefinitionIds.java @@ -72,6 +72,12 @@ public interface ICEditorActionDefinitionIds extends ITextEditorActionDefinition */ public static final String ADD_INCLUDE= "org.eclipse.cdt.ui.edit.text.c.add.include"; //$NON-NLS-1$ + /** + * Action definition ID of the source -> organize includes action + * (value "org.eclipse.cdt.ui.edit.text.c.organize.includes"). + */ + public static final String ORGANIZE_INCLUDES= "org.eclipse.cdt.ui.edit.text.c.organize.includes"; //$NON-NLS-1$ + /** * Action definition ID of the open declaration action * (value "org.eclipse.cdt.ui.edit.opendecl"). diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java new file mode 100644 index 00000000000..4adcddebc56 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InteractiveHeaderChooser.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; + +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; + +/** + * Dialog-based header chooser. + */ +public class InteractiveHeaderChooser implements IHeaderChooser { + private final Shell shell; + Map, IPath> userChoiceCache; + + public InteractiveHeaderChooser(Shell shell) { + this.shell = shell; + userChoiceCache = new HashMap, IPath>(); + } + + @Override + public IPath chooseHeader(final String bindingName, Collection headers) { + if (headers.isEmpty()) + return null; + if (headers.size() == 1) + return headers.iterator().next(); + + Set cacheKey = new HashSet(headers); + // Check the decision cache. If the cache doesn't help, ask the user. + // Query the cache. + if (userChoiceCache.containsKey(cacheKey)) { + return userChoiceCache.get(cacheKey); + } + + // Ask the user. + final IPath[] elemArray = headers.toArray(new IPath[headers.size()]); + final IPath[] selectedElement = new IPath[1]; + runInUIThread(new Runnable() { + @Override + public void run() { + if (!shell.isDisposed()) { + ElementListSelectionDialog dialog = + new ElementListSelectionDialog(shell, new LabelProvider()); + dialog.setElements(elemArray); + dialog.setTitle(CEditorMessages.OrganizeIncludes_label); + dialog.setMessage(NLS.bind(CEditorMessages.OrganizeIncludes_choose_header, bindingName)); + if (dialog.open() == Window.OK) { + selectedElement[0] = (IPath) dialog.getFirstResult(); + } + } + } + }); + + IPath selectedHeader = selectedElement[0]; + if (selectedHeader != null) + userChoiceCache.put(headers, selectedHeader); // Remember user's choice. + return selectedHeader; + } + + private void runInUIThread(Runnable runnable) { + if (Display.getCurrent() != null) { + runnable.run(); + } else { + Display.getDefault().syncExec(runnable); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java new file mode 100644 index 00000000000..72dcc7d6c5e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/OrganizeIncludesAction.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2012 Mathias Kunter 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: + * Mathias Kunter - initial API and implementation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.text.undo.DocumentUndoManagerRegistry; +import org.eclipse.text.undo.IDocumentUndoManager; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.TextEditorAction; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.index.IIndex; +import org.eclipse.cdt.core.index.IIndexManager; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.SharedASTJob; + +import org.eclipse.cdt.internal.ui.BusyCursorJobRunner; +import org.eclipse.cdt.internal.ui.ICHelpContextIds; +import org.eclipse.cdt.internal.ui.refactoring.includes.IHeaderChooser; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeOrganizer; + +/** + * Organizes the include directives and forward declarations of a source or header file. + */ +public class OrganizeIncludesAction extends TextEditorAction { + /** + * Constructor + * @param editor The editor on which this organize includes action should operate. + */ + public OrganizeIncludesAction(ITextEditor editor) { + super(CEditorMessages.getBundleForConstructedKeys(), "OrganizeIncludes.", editor); //$NON-NLS-1$ + CUIPlugin.getDefault().getWorkbench().getHelpSystem().setHelp(this, ICHelpContextIds.ORGANIZE_INCLUDES_ACTION); + } + + @Override + public void run() { + final ITextEditor editor = getTextEditor(); + final ITranslationUnit tu = getTranslationUnit(editor); + if (tu == null) { + return; + } + if (!validateEditorInputState()) { + return; + } + + final IHeaderChooser headerChooser = new InteractiveHeaderChooser(editor.getSite().getShell()); + final String lineDelimiter = getLineDelimiter(editor); + final List edits = new ArrayList(); + SharedASTJob job = new SharedASTJob(CEditorMessages.OrganizeIncludes_action, tu) { + @Override + public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { + IIndex index= CCorePlugin.getIndexManager().getIndex(tu.getCProject(), + IIndexManager.ADD_DEPENDENCIES | IIndexManager.ADD_EXTENSION_FRAGMENTS_ADD_IMPORT); + try { + index.acquireReadLock(); + IncludeOrganizer organizer = new IncludeOrganizer(tu, index, lineDelimiter, headerChooser); + edits.addAll(organizer.organizeIncludes(ast)); + return Status.OK_STATUS; + } catch (InterruptedException e) { + return Status.CANCEL_STATUS; + } finally { + index.releaseReadLock(); + } + } + }; + IStatus status = BusyCursorJobRunner.execute(job); + if (status.isOK()) { + if (!edits.isEmpty()) { + // Apply text edits. + MultiTextEdit edit = new MultiTextEdit(); + edit.addChildren(edits.toArray(new TextEdit[edits.size()])); + IEditorInput editorInput = editor.getEditorInput(); + IDocument document = editor.getDocumentProvider().getDocument(editorInput); + IDocumentUndoManager manager= DocumentUndoManagerRegistry.getDocumentUndoManager(document); + manager.beginCompoundChange(); + try { + edit.apply(document); + } catch (MalformedTreeException e) { + CUIPlugin.log(e); + } catch (BadLocationException e) { + CUIPlugin.log(e); + } + manager.endCompoundChange(); + } + } else if (status.matches(IStatus.ERROR)) { + ErrorDialog.openError(editor.getEditorSite().getShell(), + CEditorMessages.OrganizeIncludes_error_title, + CEditorMessages.OrganizeIncludes_insertion_failed, status); + } + } + + private static String getLineDelimiter(ITextEditor editor) { + try { + IEditorInput editorInput = editor.getEditorInput(); + IDocument document = editor.getDocumentProvider().getDocument(editorInput); + String delim= document.getLineDelimiter(0); + if (delim != null) { + return delim; + } + } catch (BadLocationException e) { + } + return System.getProperty("line.separator", "\n"); //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public void update() { + ITextEditor editor = getTextEditor(); + setEnabled(editor != null && getTranslationUnit(editor) != null); + } + + /** + * Returns the translation unit of the given editor. + * @param editor The editor. + * @return The translation unit. + */ + private static ITranslationUnit getTranslationUnit(ITextEditor editor) { + if (editor == null) { + return null; + } + return CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editor.getEditorInput()); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java new file mode 100644 index 00000000000..70aa8b24166 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeCategoriesBlock.java @@ -0,0 +1,293 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.preferences; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.layout.PixelConverter; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; + +import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; +import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle.IncludeKind; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.ITreeListAdapter; +import org.eclipse.cdt.internal.ui.wizards.dialogfields.TreeListDialogField; + +/** + * The preference block for configuring styles of different categories of include statements. + */ +public class IncludeCategoriesBlock extends OptionsConfigurationBlock { + private final List styles; + private final Map categories = new HashMap(); + private TreeListDialogField categoryTree; + private PixelConverter pixelConverter; + private StackLayout editorAreaStack; + private Category selectedCategory; + + public IncludeCategoriesBlock(IStatusChangeListener context, IProject project, + IWorkbenchPreferenceContainer container, List styles) { + super(context, project, new Key[0], container); + this.styles = styles; + createCategories(); + } + + private void createCategories() { + createCategory(IncludeKind.RELATED); + createCategory(IncludeKind.PARTNER); + createCategory(IncludeKind.IN_SAME_FOLDER); + createCategory(IncludeKind.IN_SUBFOLDER); + createCategory(IncludeKind.SYSTEM); + createCategory(IncludeKind.SYSTEM_WITH_EXTENSION); + createCategory(IncludeKind.SYSTEM_WITHOUT_EXTENSION); + createCategory(IncludeKind.OTHER); + createCategory(IncludeKind.IN_SAME_PROJECT); + createCategory(IncludeKind.IN_OTHER_PROJECT); + createCategory(IncludeKind.EXTERNAL); + } + + private Category createCategory(IncludeKind includeKind) { + Category parentCategory = categories.get(includeKind.parent); + Category category = new Category(includeKind, parentCategory); + categories.put(category.getIncludeKind(), category); + return category; + } + + public void postSetSelection(Object element) { + categoryTree.postSetSelection(new StructuredSelection(element)); + } + + @Override + protected Control createContents(Composite parent) { + pixelConverter = new PixelConverter(parent); + + setShell(parent.getShell()); + + Composite composite = new Composite(parent, SWT.NONE); + composite.setFont(parent.getFont()); + + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + composite.setLayout(layout); + + IncludeStyleAdapter adapter = new IncludeStyleAdapter(); + categoryTree = new TreeListDialogField(adapter, null, new IncludeStyleLabelProvider()); + categoryTree.setDialogFieldListener(adapter); + categoryTree.setLabelText(PreferencesMessages.IncludeCategoriesBlock_header_categories); + categoryTree.setViewerComparator(adapter); + + for (Category category : categories.values()) { + if (category.parent == null) + categoryTree.addElement(category); + } + + Label label = categoryTree.getLabelControl(composite); + label.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, false)); + + Control tree = categoryTree.getTreeControl(composite); + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = pixelConverter.convertWidthInCharsToPixels(50); + gd.heightHint = pixelConverter.convertHeightInCharsToPixels(2); + tree.setLayoutData(gd); + + createCategoryEditors(composite); + + categoryTree.setTreeExpansionLevel(2); + categoryTree.selectFirstElement(); + + updateControls(); + return composite; + } + + private void createCategoryEditors(Composite parent) { + Composite editorArea = new Composite(parent, SWT.NONE); + editorArea.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false)); + editorArea.setFont(parent.getFont()); + editorAreaStack = new StackLayout(); + editorArea.setLayout(editorAreaStack); + Map stylesByKind = new HashMap(); + for (IncludeGroupStyle style : styles) { + if (style.getIncludeKind() != IncludeKind.MATCHING_PATTERN) + stylesByKind.put(style.getIncludeKind(), style); + } + + for (Category category : categories.values()) { + IncludeGroupStyleBlock block = new IncludeGroupStyleBlock(fContext, fProject, fContainer, + category.getDescription()); + IncludeGroupStyle style = stylesByKind.get(category.getIncludeKind()); + block.setStyle(style); + Control composite = block.createContents(editorArea); + category.setEditor(block, composite); + } + } + + @Override + protected void updateControls() { + super.updateControls(); + // Refresh + categoryTree.refresh(); + updateConfigurationBlock(categoryTree.getSelectedElements()); + for (Category category : categories.values()) { + category.getEditor().updateControls(); + } + } + + private void updateConfigurationBlock(List selection) { + if (selection.size() == 0) + return; + selectedCategory = (Category) selection.get(0); + editorAreaStack.topControl = selectedCategory.getEditorArea(); + editorAreaStack.topControl.getParent().layout(); + } + + @Override + protected void validateSettings(Key changedKey, String oldValue, String newValue) { + fContext.statusChanged(new StatusInfo()); + } + + /** + * Represents a category of settings. + */ + private final static class Category { + public final Category parent; + public final int index; // Index in the siblings list + private final List children; + private final IncludeKind includeKind; + private Control editorArea; + private IncludeGroupStyleBlock editor; + + Category(IncludeKind includeKind, Category parent) { + this.includeKind = includeKind; + this.parent = parent; + children = new ArrayList(); + index = parent != null ? parent.addChild(this) : 0; + } + + private int addChild(Category category) { + children.add(category); + return children.size() - 1; + } + + Category[] getChildren() { + return children.toArray(new Category[children.size()]); + } + + boolean hasChildren() { + return !children.isEmpty(); + } + + @Override + public String toString() { + return includeKind.name; + } + + IncludeKind getIncludeKind() { + return includeKind; + } + + IncludeGroupStyleBlock getEditor() { + return editor; + } + + Control getEditorArea() { + return editorArea; + } + + void setEditor(IncludeGroupStyleBlock editor, Control editorArea) { + this.editor = editor; + this.editorArea = editorArea; + } + + String getName() { + return includeKind.name; + } + + String getDescription() { + return includeKind.description; + } + } + + private class IncludeStyleAdapter extends ViewerComparator + implements ITreeListAdapter, IDialogFieldListener { + @Override + public void selectionChanged(TreeListDialogField field) { + updateConfigurationBlock(field.getSelectedElements()); + } + + @Override + public void customButtonPressed(TreeListDialogField field, int index) { + } + + @Override + public void doubleClicked(TreeListDialogField field) { + } + + @Override + public Category[] getChildren(TreeListDialogField field, Object element) { + return ((Category) element).getChildren(); + } + + @Override + public Category getParent(TreeListDialogField field, Object element) { + return ((Category) element).parent; + } + + @Override + public boolean hasChildren(TreeListDialogField field, Object element) { + return ((Category) element).hasChildren(); + } + + @Override + public void dialogFieldChanged(DialogField field) { + } + + @Override + public void keyPressed(TreeListDialogField field, KeyEvent event) { + } + + @Override + public int category(Object element) { + return ((Category) element).index; + } + } + + private static class IncludeStyleLabelProvider extends LabelProvider { + @Override + public Image getImage(Object element) { + return null; + } + + @Override + public String getText(Object element) { + return ((Category) element).getName(); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java new file mode 100644 index 00000000000..487e722436a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/IncludeGroupStyleBlock.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2013 Google, Inc 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: + * Sergey Prigogin (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.preferences; + +import java.util.ArrayList; + +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.layout.PixelConverter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; + +import org.eclipse.cdt.utils.ui.controls.ControlFactory; + +import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; +import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; +import org.eclipse.cdt.internal.ui.refactoring.includes.IncludeGroupStyle; + +/** + * The preference block for configuring styles of different categories of include statements. + */ +public class IncludeGroupStyleBlock extends OptionsConfigurationBlock { + private final String description; + private IncludeGroupStyle style; + private final ArrayList