1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-23 17:05:26 +02:00

bug 403406: Notification mechanism for changes to environment variables.

This commit is contained in:
Andrew Gvozdev 2013-03-15 14:46:38 -04:00
parent 51a7a0f345
commit 993e26b0f9
6 changed files with 286 additions and 213 deletions

View file

@ -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);
}
}

View file

@ -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<IEnvironmentVariable> oldVars, Collection<IEnvironmentVariable> 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;
}
}

View file

@ -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<VarKey> newSet = new HashSet<VarKey>(newVars.length);
HashSet<VarKey> oldSet = new HashSet<VarKey>(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<VarKey> newSetCopy = (HashSet<VarKey>)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<VarKey> modifiedSet = new HashSet<VarKey>(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<VarKey> set) {
IEnvironmentVariable vars[] = new IEnvironmentVariable[set.size()];
int i = 0;
for(Iterator<VarKey> 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);
}
}
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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<String, IEnvironmentVariable> oldEnv = new HashMap<String, IEnvironmentVariable>(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();