From a50078bfdf1eb718863299f7738b7383379d6f74 Mon Sep 17 00:00:00 2001 From: James Blackburn Date: Wed, 18 Nov 2009 11:36:35 +0000 Subject: [PATCH] Bug 265282 - Further issues with persistence issues - Return StorableEnvironment to its former self - Add derived PrefsStorableEnviornment to persist and track environment changes in the eclipse preference store - Add tests and JavaDoc for issues discovered --- .../IEnvironmentVariableManagerTests.java | 237 +++++++- .../model/CConfigurationSpecSettings.java | 3 +- .../envvar/PrefsStorableEnvironment.java | 507 ++++++++++++++++++ .../cdt/utils/envvar/StorableEnvVar.java | 47 +- .../cdt/utils/envvar/StorableEnvironment.java | 239 +++------ .../envvar/StorableEnvironmentLoader.java | 43 +- 6 files changed, 886 insertions(+), 190 deletions(-) create mode 100644 core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/PrefsStorableEnvironment.java 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 5ec0e518371..a659f351f55 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 @@ -10,6 +10,8 @@ *******************************************************************************/ package org.eclipse.cdt.core.envvar; +import java.io.ByteArrayInputStream; + import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; @@ -68,9 +70,14 @@ public class IEnvironmentVariableManagerTests extends TestCase { IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + assertFalse(descs[0].isModified()); + assertFalse(descs[1].isModified()); + // Try setting an environment variable final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR"); contribEnv.addVariable(var, prjDesc.getConfigurationById(id1)); + assertTrue(prjDesc.getConfigurationById(id1).isModified()); + assertFalse(prjDesc.getConfigurationById(id2).isModified()); // Check that the variable exists on the config1 & not in config2: IEnvironmentVariable var2 = envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id1), true); @@ -83,13 +90,100 @@ public class IEnvironmentVariableManagerTests extends TestCase { // Close and reopen, variables should still exist project.close(null); project.open(null); - descs = CoreModel.getDefault().getProjectDescription(project).getConfigurations(); + prjDesc = CoreModel.getDefault().getProjectDescription(project); var2 = envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id1), true); assertEquals(var2, var); var2 = envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id2), true); assertNull(var2); + assertFalse(prjDesc.getConfigurationById(id1).isModified()); + assertFalse(prjDesc.getConfigurationById(id2).isModified()); } + /** + * Tests that we can load the environment stored as a single (old-style) long XML string + * Also tests that an an external change to the settings file is correctly picked up. + * @throws Exception + */ + public void testOldStyleLoad() throws Exception { + final IProject project = ResourceHelper.createCDTProjectWithConfig("envProjectOldStyleLoad"); + + // Add another, derived configuration + ICProjectDescription prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription desc = prjDesc.getActiveConfiguration(); + final String id1 = desc.getId(); // Config 1's ID + final String id2 = "712427638"; // Config 2's ID + prjDesc.createConfiguration(id2, "config2", desc); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + final IEnvironmentVariable varOrig = new EnvironmentVariable("FOO", "ZOO"); + contribEnv.addVariable(varOrig, prjDesc.getConfigurationById(id2)); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + final String env = "#Mon Nov 16 21:47:46 GMT 2009\n" + + "eclipse.preferences.version=1\n" + + "environment/project/712427638=\\n" + + "\\n" + + "\\n" + + "\\n" + + "\\n\n"; + project.getFile(".settings/org.eclipse.cdt.core.prefs").setContents(new ByteArrayInputStream(env.getBytes("UTF-8")), true, false, null); + + final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR", IEnvironmentVariable.ENVVAR_APPEND, ";"); + final IEnvironmentVariable var1 = new EnvironmentVariable("FOO1", "BAR1"); + final IEnvironmentVariable var2 = new EnvironmentVariable("FOO2", "BAR2"); + + prjDesc = CoreModel.getDefault().getProjectDescription(project); + assertEquals(var, envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id2), true)); + assertEquals(var1, envManager.getVariable(var1.getName(), prjDesc.getConfigurationById(id2), true)); + assertEquals(var2, envManager.getVariable(var2.getName(), prjDesc.getConfigurationById(id2), true)); + } + + /** + * Test that an ovewrite of new style preferences is loaded correctly + * @throws Exception + */ + public void testNewStyleOverwrite() throws Exception { + final IProject project = ResourceHelper.createCDTProjectWithConfig("envProjectNewStyleLoad"); + + // Add another, derived configuration + ICProjectDescription prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription desc = prjDesc.getActiveConfiguration(); + final String id1 = desc.getId(); // Config 1's ID + final String id2 = "712427638"; // Config 2's ID + prjDesc.createConfiguration(id2, "config2", desc); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + // Variable which will be overwritten + final IEnvironmentVariable varOrig = new EnvironmentVariable("FOO", "ZOO"); + contribEnv.addVariable(varOrig, prjDesc.getConfigurationById(id2)); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + final String env = "environment/project/712427638/FOO/delimiter=;\n" + + "environment/project/712427638/FOO/operation=append\n" + + "environment/project/712427638/FOO/value=BAR\n" + + "environment/project/712427638/FOO1/delimiter=\\:\n" + + "environment/project/712427638/FOO1/operation=replace\n" + + "environment/project/712427638/FOO1/value=BAR1\n" + + "environment/project/712427638/FOO2/delimiter=\\:\n" + + "environment/project/712427638/FOO2/operation=replace\n" + + "environment/project/712427638/FOO2/value=BAR2\n" + + "environment/project/712427638/append=true\n" + + "environment/project/712427638/appendContributed=true\n"; + project.getFile(".settings/org.eclipse.cdt.core.prefs").setContents(new ByteArrayInputStream(env.getBytes("UTF-8")), true, false, null); + + final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR", IEnvironmentVariable.ENVVAR_APPEND, ";"); + final IEnvironmentVariable var1 = new EnvironmentVariable("FOO1", "BAR1"); + final IEnvironmentVariable var2 = new EnvironmentVariable("FOO2", "BAR2"); + + prjDesc = CoreModel.getDefault().getProjectDescription(project); + assertEquals(var, envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id2), true)); + assertEquals(var1, envManager.getVariable(var1.getName(), prjDesc.getConfigurationById(id2), true)); + assertEquals(var2, envManager.getVariable(var2.getName(), prjDesc.getConfigurationById(id2), true)); + } public void testNoChangeToOneVariable() throws Exception { final IProject project = ResourceHelper.createCDTProjectWithConfig("envProject"); @@ -105,7 +199,7 @@ public class IEnvironmentVariableManagerTests extends TestCase { // Try setting an environment variable final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR"); final IEnvironmentVariable var1 = new EnvironmentVariable("FOO1", "BAR1"); - final IEnvironmentVariable var2 = new EnvironmentVariable("FOO2", "BAR2"); + final IEnvironmentVariable var2 = new EnvironmentVariable("FOO2", "BAR2"); contribEnv.addVariable(var, prjDesc.getConfigurationById(id1)); contribEnv.addVariable(var1, prjDesc.getConfigurationById(id1)); contribEnv.addVariable(var2, prjDesc.getConfigurationById(id1)); @@ -138,7 +232,7 @@ public class IEnvironmentVariableManagerTests extends TestCase { project.close(null); project.open(null); prjDesc = CoreModel.getDefault().getProjectDescription(project); - + readVar = envManager.getVariable(var3.getName(), prjDesc.getConfigurationById(id1), true); assertEquals(var3, readVar); readVar = envManager.getVariable(var1.getName(), prjDesc.getConfigurationById(id1), true); @@ -146,12 +240,141 @@ public class IEnvironmentVariableManagerTests extends TestCase { readVar = envManager.getVariable(var2.getName(), prjDesc.getConfigurationById(id1), true); assertEquals(var2, readVar); } - + + /** + * tests the get / set append persisting + */ + public void testGetSetAppend() throws Exception { + final IProject project = ResourceHelper.createCDTProjectWithConfig("envProject"); + + // Add another, derived configuration + ICProjectDescription prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription desc = prjDesc.getActiveConfiguration(); + final String id1 = desc.getId(); // Config 1's ID + final String id2 = CDataUtil.genId(id1); // Config 2's ID + prjDesc.createConfiguration(id2, "config2", desc); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + // Get all the configurations + prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription[] descs = prjDesc.getConfigurations(); + assertEquals(2, descs.length); + + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + + assertFalse(descs[0].isModified()); + assertFalse(descs[1].isModified()); + + // Set append & append contributed on the 2 configs respectively + final boolean append = contribEnv.appendEnvironment(prjDesc.getConfigurationById(id2)); + contribEnv.setAppendEnvironment(!append, prjDesc.getConfigurationById(id2)); + assertEquals(!append, contribEnv.appendEnvironment(prjDesc.getConfigurationById(id2))); + assertFalse(prjDesc.getConfigurationById(id1).isModified()); + assertTrue(prjDesc.getConfigurationById(id2).isModified()); + + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + // Close and reopen, variables should still exist + project.close(null); + project.open(null); + prjDesc = CoreModel.getDefault().getProjectDescription(project); + assertEquals(!append, contribEnv.appendEnvironment(prjDesc.getConfigurationById(id2))); + assertFalse(prjDesc.getConfigurationById(id1).isModified()); + assertFalse(prjDesc.getConfigurationById(id2).isModified()); + } + + /** + * Tests file system change of the settings file + */ + public void testSettingsOverwrite() throws Exception { + final IProject project = ResourceHelper.createCDTProjectWithConfig("envProject"); + + // Add another, derived configuration + ICProjectDescription prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription desc = prjDesc.getActiveConfiguration(); + final String id1 = desc.getId(); // Config 1's ID + final String id2 = CDataUtil.genId(id1); // Config 2's ID + prjDesc.createConfiguration(id2, "config2", desc); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + // Get all the configurations + prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription[] descs = prjDesc.getConfigurations(); + assertTrue(descs.length == 2); + + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + + assertFalse(descs[0].isModified()); + assertFalse(descs[1].isModified()); + + // Try setting an environment variable + final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR"); + contribEnv.addVariable(var, prjDesc.getConfigurationById(id1)); + assertTrue(prjDesc.getConfigurationById(id1).isModified()); + assertFalse(prjDesc.getConfigurationById(id2).isModified()); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + // Backup the settings file + project.getFile(".settings/org.eclipse.cdt.core.prefs.bak").create( + project.getFile(".settings/org.eclipse.cdt.core.prefs").getContents(), true, null); + + // Change the environment variable + final IEnvironmentVariable var2 = new EnvironmentVariable("FOO", "BOO"); + contribEnv.addVariable(var2, prjDesc.getConfigurationById(id1)); + assertEquals(var2, envManager.getVariable(var2.getName(), prjDesc.getConfigurationById(id1), true)); + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + // Replace the settings with it's backup + project.getFile(".settings/org.eclipse.cdt.core.prefs").setContents( + project.getFile(".settings/org.eclipse.cdt.core.prefs.bak").getContents(), true, false, null); + prjDesc = CoreModel.getDefault().getProjectDescription(project); + assertEquals(var, envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id1), true)); + } + + /** + * Test that on deleting and recreating the project variables haven't persisted + * @throws Exception + */ + public void testBrokenCaching() throws Exception { + final IProject project = ResourceHelper.createCDTProjectWithConfig("envProject"); + + // Add another, derived configuration + ICProjectDescription prjDesc = CoreModel.getDefault().getProjectDescription(project); + ICConfigurationDescription desc = prjDesc.getActiveConfiguration(); + final String id1 = desc.getId(); + + IEnvironmentVariableManager envManager = CCorePlugin.getDefault().getBuildEnvironmentManager(); + IContributedEnvironment contribEnv = envManager.getContributedEnvironment(); + // At the moment 0 variables are set + assertEquals(0, contribEnv.getVariables(desc).length); + + // Try setting an environment variable + final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR"); + contribEnv.addVariable(var, prjDesc.getConfigurationById(id1)); + assertEquals(1, contribEnv.getVariables(desc).length); + // Check that the variable exists on config1 + IEnvironmentVariable readVar = envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id1), true); + assertEquals(var, readVar); + + // Save the project description + CoreModel.getDefault().setProjectDescription(project, prjDesc); + + ResourceHelper.cleanUp(); + assertFalse(project.exists()); + ResourceHelper.createCDTProjectWithConfig("envProject"); + assertTrue(project.exists()); + // Fetch the current configuration + prjDesc = CoreModel.getDefault().getProjectDescription(project); + desc = prjDesc.getActiveConfiguration(); + assertEquals(0, contribEnv.getVariables(desc).length); + } /** * This bug checks for an environment load race during project open / import. * - * This occurs because enviornment is stored using platform Preferences (persisted in + * This occurs because environment is stored using platform Preferences (persisted in * the .settings directory) and, when background refresh is enabled this is loaded * asynchronously. * @@ -172,7 +395,7 @@ public class IEnvironmentVariableManagerTests extends TestCase { // Try setting an environment variable final IEnvironmentVariable var = new EnvironmentVariable("FOO", "BAR"); final IEnvironmentVariable var1 = new EnvironmentVariable("FOO1", "BAR1"); - final IEnvironmentVariable var2 = new EnvironmentVariable("FOO2", "BAR2"); + final IEnvironmentVariable var2 = new EnvironmentVariable("FOO2", "BAR2"); contribEnv.addVariable(var, prjDesc.getConfigurationById(id1)); // Check that the variable exists on config1 @@ -221,7 +444,7 @@ public class IEnvironmentVariableManagerTests extends TestCase { assertNull(readVar); readVar = envManager.getVariable(var2.getName(), prjDesc.getConfigurationById(id1), true); assertEquals(var3, readVar); - + // Get project description should only have the persisted envvar prjDesc = CoreModel.getDefault().getProjectDescription(project); readVar = envManager.getVariable(var.getName(), prjDesc.getConfigurationById(id1), true); diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java index b2d58724b85..7f2692f5e6b 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/settings/model/CConfigurationSpecSettings.java @@ -37,6 +37,7 @@ import org.eclipse.cdt.internal.core.CExtensionInfo; import org.eclipse.cdt.internal.core.COwner; import org.eclipse.cdt.internal.core.COwnerConfiguration; import org.eclipse.cdt.internal.core.cdtvariables.StorableCdtVariables; +import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.utils.envvar.StorableEnvironment; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.QualifiedName; @@ -161,7 +162,7 @@ public class CConfigurationSpecSettings implements ICSettingsStorage{ fSessionPropertiesMap = (HashMap)base.fSessionPropertiesMap.clone(); if(base.fEnvironment != null) - fEnvironment = new StorableEnvironment(base.fEnvironment, des.isReadOnly()); + fEnvironment = EnvironmentVariableManager.fUserSupplier.cloneEnvironmentWithContext(fCfg, base.fEnvironment, des.isReadOnly()); fOwnerId = base.fOwnerId; fOwner = base.fOwner; 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 new file mode 100644 index 00000000000..21b98e57ec2 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/PrefsStorableEnvironment.java @@ -0,0 +1,507 @@ +/******************************************************************************* + * Copyright (c) 2009 Broadcom 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: + * James Blackburn (Broadcom Corp.) + *******************************************************************************/ +package org.eclipse.cdt.utils.envvar; + +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.ICStorageElement; +import org.eclipse.cdt.utils.envvar.StorableEnvironmentLoader.ISerializeInfo; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent; +import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.prefs.Preferences; + +/** + * This class represents the set of environment variables that could be loaded + * and stored from a IEclipsePreferences store. It acts like an OverlayStore caching + * outstanding changes while not yet serialized, as well as responding to change + * in the Preference store itself. + * + * fCachedSerialEnv is a cache of the contents of the preference store + * fVariables (in parent) contains runtime added / changed variables + * fDeleteVaraibles contains delete variable names + * When serialize is called, all changes in Variables / Delete are serialized to the + * ISerializeInfo store, Cached is updated, and fVariables and fDeletedVariables cleared. + * + * StorableEnvironment stores the Preferences in a single XML encoded String in + * ISerializeInfo.getNode().get(ISerializeInfo.getName()) + * This class defaults to storing the environment as 'Raw' items in the Preferences + * under: + * ISerializeInfo.getNode().node(ISerializeInfo.getName()) + * + * @since 5.2 + * @noextend This class is not intended to be subclassed by clients. + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class PrefsStorableEnvironment extends StorableEnvironment { + + /** Handle on the storage */ + private ISerializeInfo fSerialEnv; + + /** Set of 'deleted' variables (to be removed from the backing store) */ + protected Set fDeletedVariables; + + // State to manage and handle external changes to the environment + + /** Cache of Environment as loaded from the {@link ISerializeInfo} + * contains no non-persisted changes */ + private Map fCachedSerialEnv = new HashMap(); + + // State to track whether API users have changed these boolean values + private boolean fAppendChanged = false; + private boolean fAppendContributedChanged = false; + + /** A listener for changes in the backing store */ + private static class PrefListener implements IPreferenceChangeListener, INodeChangeListener { + + /** boolean indicating whether preferences have changed */ + private volatile boolean prefsChanged = true; + + /** The node we're registered on */ + volatile IEclipsePreferences prefs; + + public PrefListener(ISerializeInfo info) { + register (info); + } + + /** + * Remove the listener + */ + public void remove() { + if (prefs != null) { + try { + prefs.removePreferenceChangeListener(this); + prefs.removeNodeChangeListener(this); + } catch (Exception e) { + // Catch all exceptions, this is called during parent finalization which we don't want to prevent... + // e.g. IllegalStateException may occur during de-register + } + prefs = null; + } + } + + /** + * Register the Prefs change listener + */ + private void register(ISerializeInfo info) { + if (prefs != null) + return; + prefs = (IEclipsePreferences)info.getNode(); + if (prefs != null) { + prefs.addPreferenceChangeListener(this); + prefs.addNodeChangeListener(this); + } + prefsChanged = true; + } + + /** + * Return & unset flag indicating if there has been a change in the backing store + * @return whether there's been a change in the backing store + */ + public boolean preferencesChanged(ISerializeInfo info) { + if (prefs == null) + register(info); + + boolean retVal = prefsChanged; + // If we're registered for change, then unset + if (prefs != null) + prefsChanged = false; + return retVal; + } + + public void preferenceChange(PreferenceChangeEvent event) { + prefsChanged = true; + } + public void added(NodeChangeEvent event) { + prefsChanged = true; + } + public void removed(NodeChangeEvent event) { + prefsChanged = true; + } + } + private PrefListener fPrefsChangedListener; + + /** + * The set of variables which have been 'deleted' by the user. + * @return the live removed {@link IEnvironmentVariable} map + */ + private Set getDeletedSet(){ + if(fDeletedVariables == null) + fDeletedVariables = new HashSet(); + return fDeletedVariables; + } + + /** + * Copy constructor. + * + * Creates a new StorableEnvironment from an existing StorableEnvironment. Settings + * are copied wholesale from the previous enviornment. + * + * Note that the previous environment's {@link ISerializeInfo} isn't copied + * over, as it's expected this environment's settings will be stored elsewhere + * + * @param env + * @param isReadOnly + */ + PrefsStorableEnvironment(StorableEnvironment env, ISerializeInfo serializeInfo, boolean isReadOnly) { + super(isReadOnly); + + // Copy shared mutable state + fAppend = env.fAppend; + fAppendContributedEnv = env.fAppendContributedEnv; + + // If base is a PrefsStorableEnv, add other internal data + if (env instanceof PrefsStorableEnvironment) { + PrefsStorableEnvironment other = (PrefsStorableEnvironment)env; + fAppendChanged = other.fAppendChanged; + fAppendContributedChanged = other.fAppendContributedChanged; + + // If this environemnt is a *copy* of another environment + // then copy over *all* variables into our live variable map + if (serializeInfo == null || other.fSerialEnv == null || + !serializeInfo.getPrefName().equals(other.fSerialEnv.getPrefName())) + fVariables = env.getAllVariablesMap(); + else { + // Just a runtime copy using existing ISerializeInfo Store, just clone runtime + // runtime changed state + if (other.fVariables != null) + getMap().putAll(other.fVariables); + if (other.fDeletedVariables != null) + getDeletedSet().addAll(other.fDeletedVariables); + } + } else { + // add all variables + if(env.fVariables != null) + fVariables = env.getAllVariablesMap(); + // Assume the append & appendContributed are changed + fAppendChanged = true; + fAppendContributedChanged = true; + } + // Set the serializeInfo on this env + setSerializeInfo(serializeInfo); + } + + /** + * Create a StorableEnvironment backed by this ISerializeInfo. + * + * This StorabelEnvironment will respond to changes in the backing store + * + * @param serializeInfo + * @since 5.2 + */ + PrefsStorableEnvironment(ISerializeInfo serializeInfo, boolean isReadOnly) { + super(isReadOnly); + setSerializeInfo(serializeInfo); + } + + /** + * Set the {@link ISerializeInfo} which persists this environment + * @param serializeInfo + */ + private void setSerializeInfo(ISerializeInfo serializeInfo) { + if (fPrefsChangedListener != null) + fPrefsChangedListener.remove(); + fSerialEnv = serializeInfo; + fPrefsChangedListener = new PrefListener(fSerialEnv); + + // Update the cached state + checkBackingSerializeInfo(); + } + + /** + * Check and update the state of the backing {@link ISerializeInfo} cache + * Acts as a reconciler, keeping the environment up to date as it's updated externally + */ + private void checkBackingSerializeInfo() { + // Any change? + if (!fPrefsChangedListener.preferencesChanged(fSerialEnv)) + return; + + Preferences topNode = fSerialEnv.getNode(); + if (topNode == null) + return; + + try { + if (topNode.get(fSerialEnv.getPrefName(), "").length() != 0) //$NON-NLS-1$ + migrateOldStylePrefs(); + + // Does our storage node exist? + if (topNode.nodeExists(fSerialEnv.getPrefName())) { + // Clear the cache + fCachedSerialEnv.clear(); + // new style preferences are stored individually in the node + Preferences prefs = topNode.node(fSerialEnv.getPrefName()); + try { + for (String child : prefs.childrenNames()) { + String name = getNameForMap(child); + IEnvironmentVariable env = new StorableEnvVar(child, prefs.node(child)); + addVariable(fCachedSerialEnv, env); + if (env.equals(getMap().get(name))) + getMap().remove(name); + } + } catch (BackingStoreException e) { + CCorePlugin.log(e); + } + // Remove deleted items no longer in the map + if (fDeletedVariables != null) { + Iterator it = fDeletedVariables.iterator(); + while(it.hasNext()) { + String name = it.next(); + if (!fCachedSerialEnv.containsKey(name)) + it.remove(); + } + if (fDeletedVariables.isEmpty()) + fDeletedVariables = null; + } + // Update flag variables + boolean append = prefs.getBoolean(ATTRIBUTE_APPEND, DEFAULT_APPEND); + if (!fAppendChanged || fAppend == append) { + fAppend = append; + fAppendChanged = false; + } + append = prefs.getBoolean(ATTRIBUTE_APPEND_CONTRIBUTED, DEFAULT_APPEND); + if (!fAppendContributedChanged || fAppendContributedEnv == append) { + fAppendContributedEnv = append; + fAppendContributedChanged = false; + } + } + } catch (BackingStoreException e) { + // Unexpected... + CCorePlugin.log(e); + } + } + + /** + * Migrates an old style preference storage. + * - Previously preferences were encoded in an ICStorageElement XML string in a text element under key {@link ISerializeInfo#getPrefName()} + * in {@link ISerializeInfo#getNode()} + * - Now they're stored directly in the preference Node ISerializeInfo#getNode()#node(ISerializeInfo#getPrefName()) + */ + private void migrateOldStylePrefs() { + // Fall-back to loading Preferences from the old style encoded XML String + // topNode.get(fSerialEnv.getPrefName(), def) + String envString = StorableEnvironmentLoader.loadPreferenceNode(fSerialEnv); + ICStorageElement element = StorableEnvironmentLoader.environmentStorageFromString(envString); + try { + if (element != null) { + Preferences oldNode = fSerialEnv.getNode(); + oldNode.put(fSerialEnv.getPrefName(), ""); //$NON-NLS-1$ + oldNode.flush(); + + // New Preferences node + Preferences newNode = fSerialEnv.getNode().node(fSerialEnv.getPrefName()); + StorableEnvironment oldEnv = new StorableEnvironment(element, false); + for (Map.Entry e : oldEnv.getMap().entrySet()) + ((StorableEnvVar)e.getValue()).serialize(newNode.node(e.getKey())); + fCachedSerialEnv.putAll(oldEnv.getMap()); + if (!fAppendChanged) + fAppend = oldEnv.fAppend; + newNode.putBoolean(ATTRIBUTE_APPEND, fAppend); + if (!fAppendContributedChanged) + fAppendContributedEnv = oldEnv.fAppendContributedEnv; + newNode.putBoolean(ATTRIBUTE_APPEND_CONTRIBUTED, fAppendContributedEnv); + // flush + fSerialEnv.getNode().flush(); + } + } catch (BackingStoreException e) { + CCorePlugin.log(e); + } + } + + /** + * Serialize the Storable environment into the ICStorageElement + * + * NB assumes that any variables part of the ISerializeInfo will continue to be serialized + * Use #serialize instead for persisting into the Preference store + * @param element + * @deprecated + */ + @Deprecated + @Override + public void serialize(ICStorageElement element){ + checkBackingSerializeInfo(); + Map map = getAllVariablesMap(); + + element.setAttribute(ATTRIBUTE_APPEND, Boolean.valueOf(fAppend).toString()); + element.setAttribute(ATTRIBUTE_APPEND_CONTRIBUTED, Boolean.valueOf(fAppendContributedEnv).toString()); + if(!map.isEmpty()){ + Iterator iter = map.values().iterator(); + while(iter.hasNext()){ + StorableEnvVar var = (StorableEnvVar)iter.next(); + ICStorageElement varEl = element.createChild(StorableEnvVar.VARIABLE_ELEMENT_NAME); + var.serialize(varEl); + } + } + } + + /** + * Serialize the element into the current Preference node + * + * At the end of this fCacheSerialEnv represents the state of the world + * and the runtime state {@link #fDeletedVariables} && {@link #fVariables} + * are empty + */ + void serialize() { + if (!isDirty()) + return; + Preferences element = fSerialEnv.getNode().node(fSerialEnv.getPrefName()); + element.putBoolean(ATTRIBUTE_APPEND, fAppend); + fAppendChanged = false; + element.putBoolean(ATTRIBUTE_APPEND_CONTRIBUTED, fAppendContributedEnv); + fAppendContributedChanged = false; + // Need to remove the delete elements + try { + if (fDeletedVariables != null) { + for (String delete : fDeletedVariables) { + element.node(delete).removeNode(); + fCachedSerialEnv.remove(delete); + } + fDeletedVariables.clear(); + } + } catch (BackingStoreException e) { + CCorePlugin.log(e); + } + // Only need to serialize the 'changed' elements + if(fVariables != null) { + for (Map.Entry e : fVariables.entrySet()) + ((StorableEnvVar)e.getValue()).serialize(element.node(e.getKey())); + fCachedSerialEnv.putAll(fVariables); + fVariables.clear(); + } + try { + element.flush(); + } catch (BackingStoreException e) { + CCorePlugin.log(e); + } + } + + @Override + public IEnvironmentVariable createVariable(String name, String value, int op, String delimiter){ + IEnvironmentVariable var = super.createVariable(name, value, op, delimiter); + if (var != null) { + if (fDeletedVariables != null) + fDeletedVariables.remove(getNameForMap(name)); + // If this variable is identical to one in the map, then no change... + if (var.equals(fCachedSerialEnv.get(getNameForMap(name)))) + getMap().remove(getNameForMap(name)); + } + return var; + } + + /** + * @param name + * @return the environment variable with the given name, or null + */ + @Override + public IEnvironmentVariable getVariable(String name){ + name = getNameForMap(name); + IEnvironmentVariable var = super.getVariable(name); + if (var != null) + return var; + + if (fDeletedVariables != null && fDeletedVariables.contains(name)) + return null; + + checkBackingSerializeInfo(); + return fCachedSerialEnv.get(name); + } + + /** + * @return cloned map of all variables set on this storable environment runtime variables + backing store vars + */ + @Override + Map getAllVariablesMap() { + checkBackingSerializeInfo(); + // Get all the environment from the backing store first + Map vars = new HashMap(); + if (fCachedSerialEnv != null) + vars.putAll(fCachedSerialEnv); + if (fDeletedVariables != null) + for (String name : fDeletedVariables) + vars.remove(name); + + // Now overwrite with the live variables set, and return + vars.putAll(getMap()); + return vars; + } + + @Override + public IEnvironmentVariable deleteVariable(String name) { + name = getNameForMap(name); + IEnvironmentVariable var = super.deleteVariable(name); + if (name == null) + return null; + getDeletedSet().add(name); + return var; + } + + @Override + public boolean deleteAll(){ + boolean change = super.deleteAll(); + + // Change should include any cached variables we're overwriting + change = change || !getDeletedSet().equals(fCachedSerialEnv.keySet()); + getDeletedSet().addAll(fCachedSerialEnv.keySet()); + if (change) { + fIsChanged = true; + } + return change; + } + + @Override + public void setAppendEnvironment(boolean append){ + boolean prevVal = fAppend; + super.setAppendEnvironment(append); + if (prevVal != fAppend) + fAppendChanged = true; + } + + @Override + public void setAppendContributedEnvironment(boolean append){ + boolean prevVal = fAppendContributedEnv; + super.setAppendContributedEnvironment(append); + if (prevVal != fAppendContributedEnv) + fAppendContributedChanged = true; + } + + @Override + public void restoreDefaults(){ + super.restoreDefaults(); + fAppendChanged = false; + fAppendContributedChanged = false; + } + + @Override + public boolean isDirty() { + return fAppendChanged || fAppendContributedChanged || + (fVariables != null && !fVariables.isEmpty()) || + (fDeletedVariables != null && !fDeletedVariables.isEmpty()); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + // Remove the preference change listener when this Storable Environment + // is no longer referenced... + if (fPrefsChangedListener != null) + fPrefsChangedListener.remove(); + } + +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvVar.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvVar.java index 0dc747cb989..bb1cbac3491 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvVar.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvVar.java @@ -1,24 +1,25 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 Intel Corporation and others. + * Copyright (c) 2005, 2009 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 + * James Blackburn (Broadcom Corp.) *******************************************************************************/ package org.eclipse.cdt.utils.envvar; import org.eclipse.cdt.core.envvar.EnvironmentVariable; import org.eclipse.cdt.core.settings.model.ICStorageElement; +import org.osgi.service.prefs.Preferences; /** * This class represents the Environment variable that could be loaded * and stored in XML * * @since 3.0 - * */ public class StorableEnvVar extends EnvironmentVariable { public static final String VARIABLE_ELEMENT_NAME = "variable"; //$NON-NLS-1$ @@ -47,12 +48,12 @@ public class StorableEnvVar extends EnvironmentVariable { public StorableEnvVar(String name, String value, String delimiter){ this(name,value,ENVVAR_REPLACE,delimiter); } - + + /** + * Load the environment variable from the ICStorageElement + * @param element + */ public StorableEnvVar(ICStorageElement element){ - load(element); - } - - private void load(ICStorageElement element){ fName = element.getAttribute(NAME); fValue = element.getAttribute(VALUE); @@ -63,6 +64,20 @@ public class StorableEnvVar extends EnvironmentVariable { if("".equals(fDelimiter)) //$NON-NLS-1$ fDelimiter = null; } + + /** + * Load the Environment Variable directly from a Preference element + * @param name + * @param element + * @since 5.2 + */ + public StorableEnvVar(String name, Preferences element){ + fName = name; + fValue = element.get(VALUE, null); + fOperation = opStringToInt(element.get(OPERATION, null)); + fDelimiter = element.get(DELIMITER, null); + } + private int opStringToInt(String op){ int operation; @@ -106,4 +121,20 @@ public class StorableEnvVar extends EnvironmentVariable { if(fDelimiter != null) element.setAttribute(DELIMITER,fDelimiter); } + + /** + * Serialize this Preference straight into the Preferences element. + * It's assumed that the Preference node represents this StorableEnvVar's name + * @param element + * @since 5.2 + */ + public void serialize(Preferences element) { + if(fValue != null) + element.put(VALUE, fValue); + + element.put(OPERATION, opIntToString(fOperation)); + + if(fDelimiter != null) + element.put(DELIMITER, fDelimiter); + } } diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironment.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironment.java index 32b009b4e10..69d360d44a1 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironment.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironment.java @@ -16,7 +16,6 @@ import java.util.Iterator; import java.util.Map; import org.eclipse.cdt.core.envvar.IEnvironmentVariable; -import org.eclipse.cdt.core.envvar.IEnvironmentVariableManager; import org.eclipse.cdt.core.settings.model.ICStorageElement; import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager; import org.eclipse.cdt.internal.core.settings.model.ExceptionFactory; @@ -28,136 +27,81 @@ import org.eclipse.cdt.utils.envvar.StorableEnvironmentLoader.ISerializeInfo; * * @since 3.0 */ -public class StorableEnvironment /*implements Cloneable*/{ +public class StorableEnvironment { public static final String ENVIRONMENT_ELEMENT_NAME = "environment"; //$NON-NLS-1$ - private static final String ATTRIBUTE_APPEND = "append"; //$NON-NLS-1$ - private static final String ATTRIBUTE_APPEND_CONTRIBUTED = "appendContributed"; //$NON-NLS-1$ - private static final boolean DEFAULT_APPEND = true; + static final String ATTRIBUTE_APPEND = "append"; //$NON-NLS-1$ + static final String ATTRIBUTE_APPEND_CONTRIBUTED = "appendContributed"; //$NON-NLS-1$ + static final boolean DEFAULT_APPEND = true; /** The map of in-flight environment variables */ - private HashMap fVariables; - /** Map of 'deleted' variables (which shouldn't be updated by a backing store change) */ - private HashMap fDeletedVariables; + Map fVariables; private boolean fIsDirty = false; - private boolean fIsChanged = false; - private final boolean fIsReadOnly; - private boolean fAppend = DEFAULT_APPEND; - private boolean fAppendContributedEnv = DEFAULT_APPEND; - - - // State to manage and handle external changes to the environment - - /** A cache copy of the environment as stored in the {@link ISerializeInfo} - * used to work-out whether the cachedStorableEnvironment map needs refreshing */ - private String fCachedSerialEnvString; - /** Map of Environment as loaded from the {@link ISerializeInfo} */ - private HashMap fCachedSerialEnv; - private ISerializeInfo fSerialEnv; - // State to track whether API users have changed these boolean values - private boolean fAppendChanged = false; - private boolean fAppendContributedChanged = false; - + boolean fIsChanged = false; + final boolean fIsReadOnly; + boolean fAppend = DEFAULT_APPEND; + boolean fAppendContributedEnv = DEFAULT_APPEND; /** * @return the live {@link IEnvironmentVariable} map */ - private Map getMap(){ + Map getMap(){ if(fVariables == null) fVariables = new HashMap(); return fVariables; } - /** - * @return the live removed {@link IEnvironmentVariable} map - */ - private Map getDeletedMap(){ - if(fDeletedVariables == null) - fDeletedVariables = new HashMap(); - return fDeletedVariables; - } + /** + * + * @param variables + * @param isReadOnly + */ public StorableEnvironment(IEnvironmentVariable variables[], boolean isReadOnly) { setVariales(variables); fIsReadOnly = isReadOnly; } + /** + * Create new empty StorableEnvironment + * @param isReadOnly + */ public StorableEnvironment(boolean isReadOnly) { fIsReadOnly = isReadOnly; } + /** + * Copy constructor. + * + * Creates a new StorableEnvironment from an existing StorableEnvironment. Settings + * are copied wholesale from the previous enviornment. + * + * Note that the previous environment's {@link ISerializeInfo} isn't copied + * over, as it's expected this environment's settings will be stored elsewhere + * + * @param env + * @param isReadOnly + */ public StorableEnvironment(StorableEnvironment env, boolean isReadOnly) { - if(env.fVariables != null) { - @SuppressWarnings("unchecked") - final HashMap clone = (HashMap)env.fVariables.clone(); - fVariables = clone; - } - if(env.fDeletedVariables != null) { - @SuppressWarnings("unchecked") - final HashMap clone = (HashMap)env.fDeletedVariables.clone(); - fDeletedVariables = clone; - } - fSerialEnv = env.fSerialEnv; + if(env.fVariables != null) + fVariables = env.getAllVariablesMap(); fAppend = env.fAppend; - fAppendChanged = env.fAppendChanged; fAppendContributedEnv = env.fAppendContributedEnv; - fAppendContributedChanged = env.fAppendContributedChanged; fIsReadOnly = isReadOnly; fIsDirty = env.isDirty(); } + /** + * Initialize the StorableEnvironment from an ICStorageElement tree + * @param element + * @param isReadOnly + */ public StorableEnvironment(ICStorageElement element, boolean isReadOnly) { load(element); fIsReadOnly = isReadOnly; } /** - * Create a StorableEnvironment backed by this ISerializeInfo. - * - * @param serializeInfo - * @since 5.2 + * Load the preferences from an {@link ICStorageElement} + * @param element */ - StorableEnvironment(ISerializeInfo serializeInfo, boolean isReadOnly) { - fIsReadOnly = isReadOnly; - fSerialEnv = serializeInfo; - - // Update the cached state - checkBackingSerializeInfo(); - } - - /** - * Check and update the state of the backing {@link ISerializeInfo} cache - */ - private void checkBackingSerializeInfo() { - String envString = StorableEnvironmentLoader.loadPreferenceNode(fSerialEnv); - - // Has anything changed? - if (envString == null || envString.equals(fCachedSerialEnvString)) - return; - fCachedSerialEnvString = envString; - - ICStorageElement element = StorableEnvironmentLoader.environmentStorageFromString(fCachedSerialEnvString); - if (element == null) - return; - - // Now update the cached environment - if (fCachedSerialEnv == null) - fCachedSerialEnv = new HashMap(); - else - fCachedSerialEnv.clear(); - - for (ICStorageElement child : element.getChildren()) - if (child.getName().equals(StorableEnvVar.VARIABLE_ELEMENT_NAME)) - addVariable(fCachedSerialEnv, new StorableEnvVar(child)); - - // If user hasn't changed fAppend or fAppend Contributed, then update - if (!fAppendChanged) { - String append = element.getAttribute(ATTRIBUTE_APPEND); - fAppend = append != null ? Boolean.valueOf(append).booleanValue() : DEFAULT_APPEND; - } - if (!fAppendContributedChanged) { - String append = element.getAttribute(ATTRIBUTE_APPEND_CONTRIBUTED); - fAppendContributedEnv = append != null ? Boolean.valueOf(append).booleanValue() : DEFAULT_APPEND; - } - } - private void load(ICStorageElement element){ ICStorageElement children[] = element.getChildren(); for (int i = 0; i < children.length; ++i) { @@ -186,22 +130,10 @@ public class StorableEnvironment /*implements Cloneable*/{ * @param element */ public void serialize(ICStorageElement element){ - checkBackingSerializeInfo(); - Map map = new HashMap(); - if (fCachedSerialEnv != null) - map.putAll(fCachedSerialEnv); - if (fDeletedVariables != null) { - for (String rem : fDeletedVariables.keySet()) - map.remove(rem); - fDeletedVariables.clear(); - } - if (fVariables != null) - map.putAll(fVariables); - element.setAttribute(ATTRIBUTE_APPEND, Boolean.valueOf(fAppend).toString()); element.setAttribute(ATTRIBUTE_APPEND_CONTRIBUTED, Boolean.valueOf(fAppendContributedEnv).toString()); - if(!map.isEmpty()){ - Iterator iter = map.values().iterator(); + if(fVariables != null){ + Iterator iter = fVariables.values().iterator(); while(iter.hasNext()){ StorableEnvVar var = (StorableEnvVar)iter.next(); ICStorageElement varEl = element.createChild(StorableEnvVar.VARIABLE_ELEMENT_NAME); @@ -217,14 +149,10 @@ public class StorableEnvironment /*implements Cloneable*/{ * @param map * @param var */ - private void addVariable(Map map, IEnvironmentVariable var){ - String name = var.getName(); - if(name == null) + void addVariable(Map map, IEnvironmentVariable var){ + String name = getNameForMap(var.getName()); + if (name == null) return; - IEnvironmentVariableManager provider = EnvironmentVariableManager.getDefault(); - if(!provider.isVariableCaseSensitive()) - name = name.toUpperCase(); - map.put(name,var); } @@ -239,9 +167,6 @@ public class StorableEnvironment /*implements Cloneable*/{ if(var == null){ var = new StorableEnvVar(name, value, op, delimiter); addVariable(getMap(), var); - // Variable added, ensure it's not in the removed set - if (fDeletedVariables != null) - fDeletedVariables.remove(EnvironmentVariableManager.getDefault().isVariableCaseSensitive() ? name : name.toUpperCase()); fIsDirty = true; fIsChanged = true; } @@ -327,23 +252,10 @@ public class StorableEnvironment /*implements Cloneable*/{ * @return the environment variable with the given name, or null */ public IEnvironmentVariable getVariable(String name){ - if(name == null || "".equals(name = name.trim())) //$NON-NLS-1$ + name = getNameForMap(name); + if (name == null) return null; - IEnvironmentVariableManager provider = EnvironmentVariableManager.getDefault(); - if(!provider.isVariableCaseSensitive()) - name = name.toUpperCase(); - - IEnvironmentVariable var = getMap().get(name); - if (var != null) - return var; - - if (fDeletedVariables != null && fDeletedVariables.containsKey(name)) - return null; - - checkBackingSerializeInfo(); - if (fCachedSerialEnv != null) - return fCachedSerialEnv.get(name); - return null; + return getMap().get(name); } /** @@ -383,33 +295,44 @@ public class StorableEnvironment /*implements Cloneable*/{ vars[i].getDelimiter()); } - public IEnvironmentVariable[] getVariables(){ - checkBackingSerializeInfo(); - // Get all the environment from the backing store first + /** + * @return cloned map of all variables set on this storable environment runtime variables + backing store vars + */ + Map getAllVariablesMap() { Map vars = new HashMap(); - if (fCachedSerialEnv != null) - vars.putAll(fCachedSerialEnv); - if (fDeletedVariables != null) - for (String name : fDeletedVariables.keySet()) - vars.remove(name); - - // Now overwrite with the live variables set, and return vars.putAll(getMap()); + return vars; + } + public IEnvironmentVariable[] getVariables(){ + Map vars = getAllVariablesMap(); return vars.values().toArray(new IEnvironmentVariable[vars.size()]); } + /** + * Returns the unique canonical form of the variable name for storage in the Maps. + * + * The name will be trimmed, and, if the var manager isn't case sensitive, made upper case + * + * @param name + * @return canonical name, or null + */ + String getNameForMap(String name) { + if (name == null || (name = name.trim()).length() == 0) + return null; + if (!EnvironmentVariableManager.getDefault().isVariableCaseSensitive()) + return name.toUpperCase(); + return name; + } + public IEnvironmentVariable deleteVariable(String name){ if(fIsReadOnly) throw ExceptionFactory.createIsReadOnlyException(); - if(name == null || "".equals(name = name.trim())) //$NON-NLS-1$ + name = getNameForMap(name); + if(name == null) return null; - IEnvironmentVariableManager provider = EnvironmentVariableManager.getDefault(); - if(!provider.isVariableCaseSensitive()) - name = name.toUpperCase(); IEnvironmentVariable var = getMap().remove(name); - getDeletedMap().put(name, var); if(var != null){ fIsDirty = true; fIsChanged = true; @@ -422,10 +345,9 @@ public class StorableEnvironment /*implements Cloneable*/{ if(fIsReadOnly) throw ExceptionFactory.createIsReadOnlyException(); Map map = getMap(); - if(map.size() > 0){ + if(map.size() > 0) { fIsDirty = true; fIsChanged = true; - getDeletedMap().putAll(map); map.clear(); return true; } @@ -449,7 +371,6 @@ public class StorableEnvironment /*implements Cloneable*/{ throw ExceptionFactory.createIsReadOnlyException(); fAppend = append; - fAppendChanged = true; fIsDirty = true; } @@ -465,7 +386,6 @@ public class StorableEnvironment /*implements Cloneable*/{ throw ExceptionFactory.createIsReadOnlyException(); fAppendContributedEnv = append; - fAppendContributedChanged = true; fIsDirty = true; } @@ -475,13 +395,4 @@ public class StorableEnvironment /*implements Cloneable*/{ fAppendContributedEnv = DEFAULT_APPEND; } -/* public Object clone(){ - try { - StorableEnvironment env = (StorableEnvironment)super.clone(); - env.fVariables = (HashMap)fVariables.clone(); - } catch (CloneNotSupportedException e) { - } - return null; - } -*/ } diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironmentLoader.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironmentLoader.java index 3c3b8724b9b..16a4e86738f 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironmentLoader.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/envvar/StorableEnvironmentLoader.java @@ -33,6 +33,7 @@ import org.eclipse.cdt.internal.core.settings.model.xml.XmlStorageElement; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import org.w3c.dom.Document; @@ -45,7 +46,6 @@ import org.xml.sax.SAXException; * storing and loading environment variable settings from eclipse properties * * @since 3.0 - * */ public abstract class StorableEnvironmentLoader { @@ -56,17 +56,34 @@ public abstract class StorableEnvironmentLoader { * @noimplement This interface is not intended to be implemented by clients. */ public interface ISerializeInfo{ + /** + * {@link IEclipsePreferences} root node in the Preference store * @return the Preferences Node into which environment should be (de) serialized */ Preferences getNode(); /** + * Name in the preference store * @return the key in the preference node to use for loading preferences */ String getPrefName(); } + /** + * Creates the StorableEnvironment clone for a new configuration, say, + * based on an existing configuration + * + * @param context the configuration / workspace context the configuration is to be cloned for + * @param base the base environment to copy + * @return a StorableEnvironment clone of the configuration's environment + * @since 5.2 + */ + public StorableEnvironment cloneEnvironmentWithContext(Object context, StorableEnvironment base, boolean isReadOnly) { + PrefsStorableEnvironment env = new PrefsStorableEnvironment(base, getSerializeInfo(context), isReadOnly); + return env; + } + /** * this method should return the ISerializeInfo representing the information * of where the variable should be stored and loaded @@ -85,12 +102,12 @@ public abstract class StorableEnvironmentLoader { * @param readOnly * @return StorableEnvironment */ - protected StorableEnvironment loadEnvironment(Object context, boolean readOnly){ + protected StorableEnvironment loadEnvironment(Object context, boolean readOnly) { ISerializeInfo serializeInfo = getSerializeInfo(context); if(serializeInfo == null) return null; - return new StorableEnvironment(serializeInfo, readOnly); + return new PrefsStorableEnvironment(serializeInfo, readOnly); } /* @@ -103,13 +120,18 @@ public abstract class StorableEnvironmentLoader { ISerializeInfo serializeInfo = getSerializeInfo(context); if(serializeInfo == null) return; - - ByteArrayOutputStream stream = storeEnvironmentToStream(env); - if(stream == null) - return; - storeOutputStream(stream,serializeInfo.getNode(), serializeInfo.getPrefName(), flush); - - env.setDirty(false); + + if (env instanceof PrefsStorableEnvironment) { + ((PrefsStorableEnvironment)env).serialize(); + } else { + // Backwards compatibility + ByteArrayOutputStream stream = storeEnvironmentToStream(env); + if(stream == null) + return; + storeOutputStream(stream,serializeInfo.getNode(), serializeInfo.getPrefName(), flush); + + env.setDirty(false); + } } /** @@ -189,6 +211,7 @@ public abstract class StorableEnvironmentLoader { } /** + * Preferences can be encoded as a single long ICStorageElement String * @return String value stored in the node or null if no such value exists. */ static String loadPreferenceNode(ISerializeInfo serializeInfo) {