Bug 543536: [REnv] Add support for programmatically contributed
R environments

Change-Id: Ida583d61a074c5d197362355ca0d95ad1445c723
diff --git a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RProcess.java b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RProcess.java
index 0b6c175..268e0b5 100644
--- a/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RProcess.java
+++ b/r/org.eclipse.statet.r.console.core/src/org/eclipse/statet/r/console/core/RProcess.java
@@ -32,26 +32,26 @@
 public class RProcess extends ToolProcess implements RConsoleTool {
 	
 	
-	private final IREnvConfiguration rEnvConfig;
+	private final @Nullable IREnvConfiguration rEnvConfig;
 	
 	
 	/**
 	 * Creates an new R process handle.
 	 * 
 	 * @param launch
-	 * @param renv
+	 * @param rEnvConfig
 	 * @param labelPrefix
 	 * @param name
 	 * @param address
 	 * @param wd
 	 * @param timestamp
 	 */
-	public RProcess(final ILaunch launch, final IREnvConfiguration renv,
+	public RProcess(final ILaunch launch, final @Nullable IREnvConfiguration rEnvConfig,
 			final String labelPrefix, final String name,
 			final String address, final String wd, final long timestamp) {
 		super(launch, RConsoleTool.TYPE, labelPrefix, name,
 				address, wd, timestamp);
-		this.rEnvConfig= renv;
+		this.rEnvConfig= rEnvConfig;
 	}
 	
 	
@@ -73,7 +73,6 @@
 	}
 	
 	
-	
 	@Override
 	@SuppressWarnings("unchecked")
 	public <T> @Nullable T getAdapter(final Class<T> adapterType) {
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvConfigurationImpl.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvConfigurationImpl.java
index 2ba5c51..c3fa068 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvConfigurationImpl.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvConfigurationImpl.java
@@ -286,10 +286,6 @@
 	private final PropertyChangeSupport beanSupport= new PropertyChangeSupport(this);
 	
 	
-	REnvConfigurationImpl(final ActualREnv rEnv, final PreferenceAccess prefs) {
-		this(null, rEnv, prefs, (String) null);
-	}
-	
 	protected REnvConfigurationImpl(final String type, final ActualREnv rEnv,
 			final PreferenceAccess prefs, final String key) {
 		super(rEnv, getStateRootDirectoryPath(rEnv));
@@ -403,7 +399,7 @@
 		resolvePaths();
 	}
 	
-	private void setType(final String type) {
+	private void setType(final @Nullable String type) {
 		final int flags;
 		if (type == USER_LOCAL_TYPE) {
 			flags= (LOCAL | SPEC_SETUP);
@@ -414,6 +410,9 @@
 		else if (type == EPLUGIN_LOCAL_TYPE) {
 			flags= (LOCAL | SPEC_SETUP);
 		}
+		else if (type != null && type.startsWith(CONTRIB_TEMP_REMOTE_TYPE)) {
+			flags= (REMOTE);
+		}
 		else {
 			flags= 0;
 		}
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvManagerImpl.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvManagerImpl.java
index bee75a5..91d1a57 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvManagerImpl.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/internal/r/core/renv/REnvManagerImpl.java
@@ -24,13 +24,10 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
-import com.ibm.icu.text.Collator;
-
 import org.osgi.service.prefs.BackingStoreException;
 import org.osgi.service.prefs.Preferences;
 
@@ -43,6 +40,7 @@
 
 import org.eclipse.statet.jcommons.collections.ImCollections;
 import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNull;
 import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ecommons.preferences.core.Preference;
@@ -60,6 +58,7 @@
 import org.eclipse.statet.r.core.renv.IREnvConfiguration.WorkingCopy;
 import org.eclipse.statet.r.core.renv.IREnvManager;
 import org.eclipse.statet.rj.renv.core.REnv;
+import org.eclipse.statet.rj.renv.core.REnvConfiguration;
 import org.eclipse.statet.rj.rsetups.RSetup;
 import org.eclipse.statet.rj.rsetups.RSetupUtil;
 
@@ -69,6 +68,13 @@
  */
 public class REnvManagerImpl implements IREnvManager {
 	
+	
+	private static final String USER_ENV_PREFIX= "user-"; //$NON-NLS-1$
+	private static final String USER_LOCAL_ID_PREFIX= IREnvConfiguration.USER_LOCAL_TYPE + '-';
+	private static final String USER_REMOTE_ID_PREFIX= IREnvConfiguration.USER_REMOTE_TYPE + '-';
+	private static final String CONTRIB_TEMP_REMOTE_ENV_ID_PREFIX= IREnvConfiguration.CONTRIB_TEMP_REMOTE_TYPE + '-';
+	
+	
 	private static final StringPref PREF_DEFAULT_CONFIGURATION_NAME= new StringPref(
 			IREnvManager.PREF_QUALIFIER, "default_configuration.name"); //$NON-NLS-1$
 	
@@ -87,8 +93,6 @@
 	private volatile int state;
 	private final ReadWriteLock lock;
 	
-	private Set<String> names;
-	private Map<String, REnv> nameMap;
 	private Map<String, REnvConfigurationImpl> idMap;
 	private final AliasREnv defaultEnv= new AliasREnv(RCore.DEFAULT_WORKBENCH_ENV_ID);
 	
@@ -107,18 +111,10 @@
 		this.lock.writeLock().lock();
 		try {
 			this.state= 2;
-			if (this.names != null) {
-				this.names.clear();
-				this.names= null;
-			}
 			if (this.idMap != null) {
 				this.idMap.clear();
 				this.idMap= null;
 			}
-			if (this.nameMap != null) {
-				this.nameMap.clear();
-				this.nameMap= null;
-			}
 		}
 		finally {
 			this.lock.writeLock().unlock();
@@ -137,7 +133,7 @@
 				}
 				catch (final BackingStoreException e) {
 					this.state= 101;
-					RCorePlugin.log(new Status(IStatus.ERROR, RCore.BUNDLE_ID, -1, Messages.REnvManager_error_Accessing_message, e));
+					RCorePlugin.logError(Messages.REnvManager_error_Accessing_message, e);
 				}
 			}
 		}
@@ -159,64 +155,79 @@
 		for (final IREnvConfiguration config : configs) {
 			newREnvs.add(config.getREnv().getId());
 		}
-		final Set<String> managedNames= new TreeSet<>(Collator.getInstance());
 		final Set<String> oldIds= new HashSet<>(this.idMap.keySet());
 		
 		// update or add configurations
 		boolean changed= false;
 		for (final IREnvConfiguration config : configs) {
-			final REnvConfigurationImpl newConfig= new REnvConfigurationImpl(config);
-			final ActualREnv rEnv= (ActualREnv) newConfig.getREnv();
-			IREnvConfiguration oldConfig= this.idMap.put(rEnv.getId(), newConfig);
-			if (oldConfig == null) {
-				final REnv altREnv= this.nameMap.get(newConfig.getName());
-				if (altREnv != null && !newREnvs.contains(altREnv.getId())) {
-					oldConfig= this.idMap.remove(altREnv.getId());
-				}
-			}
-			if (!changed && (oldConfig == null || !oldConfig.equals(newConfig))) {
-				changed= true;
-			}
-			rEnv.name= newConfig.getName();
-			rEnv.config= newConfig;
-			this.nameMap.put(rEnv.name, rEnv);
-			managedNames.add(rEnv.name);
+			changed= doAdd(config, changed);
+			final REnv rEnv= config.getREnv();
 			oldIds.remove(rEnv.getId());
 		}
 		// remove old configurations
 		for (final String id : oldIds) {
-			final REnvConfigurationImpl rEnvConfig= this.idMap.remove(id);
-			if (rEnvConfig != null) {
-				final ActualREnv rEnv= (ActualREnv) rEnvConfig.getREnv();
-				rEnv.isDeleted= true;
-//				rEnv.config= null;
-				changed |= true;
-			}
+			changed= doDelete(id, changed);
 		}
-//		changed |= this.idMap.keySet().retainAll(newREnvs);
-		changed |= this.nameMap.keySet().retainAll(managedNames);
-		this.names= managedNames;
 		
-		final REnv oldDefault= getActualREnv(this.defaultEnv);
-		updateDefault(defaultREnvId);
-		final REnv newDefault= getActualREnv(this.defaultEnv);
-		changed |= !Objects.equals(oldDefault, newDefault);
+		changed= doSetDefault(defaultREnvId, changed);
 		
 		// dirty?
 		return changed;
 	}
 	
+	private boolean doAdd(final IREnvConfiguration config, boolean changed) {
+		final REnvConfigurationImpl newConfig= new REnvConfigurationImpl(config);
+		final ActualREnv rEnv= (ActualREnv) newConfig.getREnv();
+		final IREnvConfiguration oldConfig= this.idMap.put(rEnv.getId(), newConfig);
+		
+		rEnv.name= newConfig.getName();
+		rEnv.config= newConfig;
+		
+		if (!changed && !Objects.equals(oldConfig, newConfig)) {
+			changed= true;
+		}
+		return changed;
+	}
+	
+	private boolean doDelete(final String id, boolean changed) {
+		final REnvConfigurationImpl rEnvConfig= this.idMap.remove(id);
+		if (rEnvConfig != null) {
+			final ActualREnv rEnv= (ActualREnv) rEnvConfig.getREnv();
+			rEnv.isDeleted= true;
+//			rEnv.config= null;
+			changed |= true;
+		}
+		
+		return changed;
+	}
+	
+	private boolean doSetDefault(final String id, boolean changed) {
+		final REnv oldDefault= getActualREnv(this.defaultEnv);
+		updateDefault(id);
+		final REnv newDefault= getActualREnv(this.defaultEnv);
+		
+		if (!changed && !Objects.equals(oldDefault, newDefault)) {
+			changed= true;
+		}
+		return changed;
+	}
+	
+	private boolean doRefreshDefault(boolean changed) {
+		final REnv oldDefault= getActualREnv(this.defaultEnv);
+		updateDefault((oldDefault != null) ? oldDefault.getId() : null);
+		final REnv newDefault= getActualREnv(this.defaultEnv);
+		
+		if (!changed && !Objects.equals(oldDefault, newDefault)) {
+			changed= true;
+		}
+		return changed;
+	}
+	
+	
 	private void updateDefault(final String defaultConfigId) {
 		IREnvConfiguration rEnvConfig= null;
-		if (defaultConfigId != null && defaultConfigId.length() > 0) {
+		if (defaultConfigId != null && !defaultConfigId.isEmpty()) {
 			rEnvConfig= this.idMap.get(defaultConfigId);
-			if (rEnvConfig == null) {
-				// migrate old preference (name -> id)
-				final REnv rEnv= this.nameMap.get(defaultConfigId);
-				if (rEnv != null) {
-					rEnvConfig= rEnv.get(IREnvConfiguration.class);
-				}
-			}
 		}
 		String name;
 		REnv rEnv;
@@ -224,7 +235,7 @@
 			name= rEnvConfig.getName();
 			rEnv= rEnvConfig.getREnv();
 		}
-		else if (this.names.size() > 0) {
+		else if (this.idMap.size() > 0) {
 			name= Messages.REnvManager_status_NoDefault_label;
 			rEnv= null;
 		}
@@ -237,9 +248,7 @@
 	}
 	
 	private void load() throws BackingStoreException {
-		this.nameMap= new HashMap<>();
 		this.idMap= new HashMap<>();
-		this.names= new HashSet<>();
 		
 		loadFromRegistry();
 		loadFromWorkspace();
@@ -261,7 +270,7 @@
 				for (final String name : names) {
 					final Preferences node= prefNode.node(name);
 					final String id= node.get("id", null); //$NON-NLS-1$
-					if (id != null && id.length() > 0 && id.startsWith(RCore.USER_ENV_ID_PREFIX)) {
+					if (id != null && id.length() > 0 && id.startsWith(USER_ENV_PREFIX)) {
 						final ActualREnv rEnv= new ActualREnv(id);
 						final REnvConfigurationImpl config= new REnvConfigurationImpl(
 								null, rEnv, prefs, name);
@@ -269,9 +278,7 @@
 							config.upgradePref();
 							rEnv.name= config.getName();
 							rEnv.config= config;
-							this.nameMap.put(rEnv.name, rEnv);
 							this.idMap.put(id, config);
-							this.names.add(rEnv.name);
 						}
 					}
 				}
@@ -286,16 +293,14 @@
 				}
 				final String[] names= prefNode.childrenNames();
 				for (final String id : names) {
-					if (id != null && id.length() > 0 && id.startsWith(RCore.USER_ENV_ID_PREFIX)) {
+					if (id != null && id.length() > 0 && id.startsWith(USER_ENV_PREFIX)) {
 						final ActualREnv rEnv= new ActualREnv(id);
 						final REnvConfigurationImpl config= new REnvConfigurationImpl(
-								rEnv, prefs);
+								null, rEnv, prefs, null);
 						if (config.getName() != null) {
 							rEnv.name= config.getName();
 							rEnv.config= config;
-							this.nameMap.put(rEnv.name, rEnv);
 							this.idMap.put(id, config);
-							this.names.add(rEnv.name);
 						}
 					}
 				}
@@ -306,23 +311,28 @@
 		updateDefault(prefs.getPreferenceValue(PREF_DEFAULT_CONFIGURATION_NAME));
 	}
 	
-	private void saveToWorkspace() throws BackingStoreException {
+	private void saveToWorkspace(final boolean envPrefs) throws BackingStoreException {
 		final IScopeContext context= PreferenceUtils.getInstancePrefs().getPreferenceContexts().get(0);
 		final IEclipsePreferences node= context.getNode(IREnvManager.PREF_QUALIFIER);
-		final List<String> oldNames= new ArrayList<>(Arrays.asList(node.childrenNames()));
-		oldNames.removeAll(this.idMap.keySet());
-		for (final String name : oldNames) {
-			if (node.nodeExists(name)) {
-				node.node(name).removeNode();
-			}
-		}
+		
 		final Map<Preference<?>, Object> map= new HashMap<>();
 		map.put(PREF_VERSION, 2);
 		map.put(STAMP_PREF, System.currentTimeMillis());
 		
-		for (final IREnvConfiguration config : this.idMap.values()) {
-			if (config instanceof REnvConfigurationImpl) {
-				((REnvConfigurationImpl) config).deliverToPreferencesMap(map);
+		if (envPrefs) {
+			final List<String> oldNames= new ArrayList<>(Arrays.asList(node.childrenNames()));
+			oldNames.removeAll(this.idMap.keySet());
+			for (final String name : oldNames) {
+				if (node.nodeExists(name)) {
+					node.node(name).removeNode();
+				}
+			}
+			
+			for (final IREnvConfiguration config : this.idMap.values()) {
+				if (config instanceof REnvConfigurationImpl
+						&& !config.getType().startsWith("contrib.temp-") ) {
+					((REnvConfigurationImpl) config).deliverToPreferencesMap(map);
+				}
 			}
 		}
 		{	// default
@@ -345,12 +355,25 @@
 			rEnv.name= config.getName();
 			rEnv.config= config;
 			
-			this.names.add(config.getName());
-			this.nameMap.put(config.getName(), rEnv);
 			this.idMap.put(rEnv.getId(), config);
 		}
 	}
 	
+	private void onChanged(final boolean envPrefs) throws BackingStoreException {
+		final PreferenceSetService preferenceSetService= PreferenceUtils.getPreferenceSetService();
+		final String sourceId= "REnv" + System.identityHashCode(this); //$NON-NLS-1$
+		final boolean resume= preferenceSetService.pause(sourceId);
+		try {
+			saveToWorkspace(envPrefs);
+			return;
+		}
+		finally {
+			if (resume) {
+				preferenceSetService.resume(sourceId);
+			}
+		}
+	}
+	
 	
 	@Override
 	public void set(final ImList<IREnvConfiguration> configs, final String defaultConfigId)
@@ -362,18 +385,7 @@
 				return;
 			}
 			
-			final PreferenceSetService preferenceSetService= PreferenceUtils.getPreferenceSetService();
-			final String sourceId= "REnv" + System.identityHashCode(this); //$NON-NLS-1$
-			final boolean resume= preferenceSetService.pause(sourceId);
-			try {
-				saveToWorkspace();
-				return;
-			}
-			finally {
-				if (resume) {
-					preferenceSetService.resume(sourceId);
-				}
-			}
+			onChanged(true);
 		}
 		catch (final BackingStoreException e) {
 			throw new CoreException(new Status(IStatus.ERROR, RCore.BUNDLE_ID, -1, Messages.REnvManager_error_Saving_message, e));
@@ -383,6 +395,59 @@
 		}
 	}
 	
+	@Override
+	public void add(final @NonNull REnvConfiguration rEnvConfig) {
+		final REnv rEnv= rEnvConfig.getREnv();
+		if (!rEnv.getId().startsWith(CONTRIB_TEMP_REMOTE_ENV_ID_PREFIX)) {
+			throw new IllegalArgumentException();
+		}
+		
+		checkAndLock(true);
+		try {
+			boolean changed= false;
+			changed= doAdd((IREnvConfiguration) rEnvConfig, changed);
+			if (!changed) {
+				return;
+			}
+			
+			changed= doRefreshDefault(changed);
+			
+			onChanged(false);
+		}
+		catch (final BackingStoreException e) {
+			RCorePlugin.logError(Messages.REnvManager_error_Saving_message, e);
+		}
+		finally {
+			this.lock.writeLock().unlock();
+		}
+	}
+	
+	@Override
+	public void delete(final @NonNull REnv rEnv) {
+		if (!rEnv.getId().startsWith(CONTRIB_TEMP_REMOTE_ENV_ID_PREFIX)) {
+			throw new IllegalArgumentException();
+		}
+		
+		checkAndLock(true);
+		try {
+			boolean changed= false;
+			changed= doDelete(rEnv.getId(), changed);
+			if (!changed) {
+				return;
+			}
+			
+			changed= doRefreshDefault(changed);
+			
+			onChanged(false);
+		}
+		catch (final BackingStoreException e) {
+			RCorePlugin.logError(Messages.REnvManager_error_Saving_message, e);
+		}
+		finally {
+			this.lock.writeLock().unlock();
+		}
+	}
+	
 //	public String[] getNames() {
 //		checkAndLock(false);
 //		try {
@@ -445,7 +510,7 @@
 	}
 	
 	@Override
-	public synchronized REnv get(final String id, final String name) {
+	public synchronized @Nullable REnv get(final String id, final String name) {
 		checkAndLock(false);
 		try {
 			if (id != null) {
@@ -455,7 +520,7 @@
 				}
 			}
 			if (name != null) {
-				return this.nameMap.get(name);
+				return getEnvByName(name);
 			}
 			return null;
 		}
@@ -464,7 +529,7 @@
 		}
 	}
 	
-	private REnv getEnv(final String id) {
+	private @Nullable REnv getEnv(final String id) {
 		if (id.equals(RCore.DEFAULT_WORKBENCH_ENV_ID)) {
 			return this.defaultEnv;
 		}
@@ -478,44 +543,13 @@
 		return null;
 	}
 	
-	public synchronized IREnvConfiguration getConfig(String id, final String name) {
-		id= resolveId(id);
-		checkAndLock(false);
-		try {
-			if (id != null) {
-				if (id.equals(RCore.DEFAULT_WORKBENCH_ENV_ID)) {
-					return this.defaultEnv.get(IREnvConfiguration.class);
-				}
-				final IREnvConfiguration config= this.idMap.get(id);
-				if (config != null) {
-					return config;
-				}
+	private @Nullable REnv getEnvByName(final String name) {
+		for (final REnvConfiguration config : this.idMap.values()) {
+			if (config.getName().equals(name)) {
+				return config.getREnv();
 			}
-			if (name != null) {
-				final REnv rEnv= this.nameMap.get(name);
-				if (rEnv != null) {
-					return getConfig(rEnv.getId(), null);
-				}
-			}
-			return null;
 		}
-		finally {
-			this.lock.readLock().unlock();
-		}
-	}
-	
-	private String resolveId(final String id) {
-		if (id == null) {
-			return null;
-		}
-		REnv rEnv= null;
-		if (id.equals(RCore.DEFAULT_WORKBENCH_ENV_ID)) {
-			rEnv= this.defaultEnv.resolve();
-		}
-		if (rEnv != null) {
-			return rEnv.getId();
-		}
-		return id;
+		return null;
 	}
 	
 	@Override
@@ -531,23 +565,13 @@
 	
 	@Override
 	public WorkingCopy newConfiguration(final String type) {
-		if ("user-remote".equals(type)) {
-			return new REnvConfigurationImpl.Editable(IREnvConfiguration.USER_REMOTE_TYPE, newUserRemoteLink());
-		}
-		return new REnvConfigurationImpl.Editable(IREnvConfiguration.USER_LOCAL_TYPE, newUserLocalLink());
+		return new REnvConfigurationImpl.Editable(type, newLink(type));
 	}
 	
-	private ActualREnv newUserLocalLink() {
-		return new ActualREnv(RCore.USER_LOCAL_ENV_ID_PREFIX + Long.toString(
+	private ActualREnv newLink(final String type) {
+		return new ActualREnv(type + '-' + Long.toString(
 				((long) System.getProperty("user.name").hashCode() << 32) | System.currentTimeMillis(), //$NON-NLS-1$
 				36 ));
 	}
 	
-	private ActualREnv newUserRemoteLink() {
-		return new ActualREnv(RCore.USER_REMOTE_ENV_ID_PREFIX + Long.toString(
-				((long) System.getProperty("user.name").hashCode() << 32) | System.currentTimeMillis(), //$NON-NLS-1$
-				36 ));
-	}
-	
-	
 }
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RCore.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RCore.java
index 83f8335..673781d 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RCore.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RCore.java
@@ -88,24 +88,6 @@
 	public static final String DEFAULT_WORKBENCH_ENV_ID= "default-workbench"; //$NON-NLS-1$
 	
 	
-	/**
-	 * The prefix of R environment ids for user defined configurations
-	 */
-	public static final String USER_ENV_ID_PREFIX= "user-"; //$NON-NLS-1$
-	
-	
-	/**
-	 * The prefix of R environment ids for user defined configurations of local R installations
-	 */
-	public static final String USER_LOCAL_ENV_ID_PREFIX= USER_ENV_ID_PREFIX + "local-"; //$NON-NLS-1$
-	
-	
-	/**
-	 * The prefix of R environment ids for user defined configurations of remote R installations
-	 */
-	public static final String USER_REMOTE_ENV_ID_PREFIX= USER_ENV_ID_PREFIX + "remote-"; //$NON-NLS-1$
-	
-	
 	public static final String DEFAULT_RHELP_BROWSE_URL= RHelpHttpService.PORTABLE_URI_SCHEME + ":/browse/" + DEFAULT_WORKBENCH_ENV_ID + '/'; //$NON-NLS-1$
 	
 	
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvConfiguration.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvConfiguration.java
index e778b68..02bc8bc 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvConfiguration.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvConfiguration.java
@@ -63,6 +63,8 @@
 	
 	String EPLUGIN_LOCAL_TYPE= "eplugin-local"; //$NON-NLS-1$
 	
+	String CONTRIB_TEMP_REMOTE_TYPE= "contrib.temp-remote"; //$NON-NLS-1$
+	
 	
 	static enum Exec {
 		COMMON,
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvManager.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvManager.java
index b6c174b..6e864dc 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvManager.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/renv/IREnvManager.java
@@ -20,9 +20,11 @@
 import org.eclipse.core.runtime.CoreException;
 
 import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNull;
 
 import org.eclipse.statet.r.core.RCore;
 import org.eclipse.statet.rj.renv.core.REnv;
+import org.eclipse.statet.rj.renv.core.REnvConfiguration;
 import org.eclipse.statet.rj.renv.core.REnvManager;
 
 
@@ -43,4 +45,17 @@
 	REnv getDefault();
 	IREnvConfiguration.WorkingCopy newConfiguration(String type);
 	
+	/**
+	 * {@inheritDoc}
+	 * <p>Only supported for IREnvConfiguration with type {@link IREnvConfiguration#CONTRIB_TEMP_REMOTE_TYPE}</p>
+	 **/
+	@Override
+	void add(@NonNull REnvConfiguration rEnvConfig);
+	/**
+	 * {@inheritDoc}
+	 * <p>Only supported for IREnvConfiguration with type {@link IREnvConfiguration#CONTRIB_TEMP_REMOTE_TYPE}</p>
+	 **/
+	@Override
+	void delete(@NonNull REnv rEnv);
+	
 }
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/REnvLabelProvider.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/REnvLabelProvider.java
index ab8910b..dc744f4 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/REnvLabelProvider.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/REnvLabelProvider.java
@@ -19,7 +19,6 @@
 import org.eclipse.jface.viewers.StyledCellLabelProvider;
 import org.eclipse.jface.viewers.ViewerCell;
 
-import org.eclipse.statet.r.core.RCore;
 import org.eclipse.statet.rj.renv.core.REnv;
 import org.eclipse.statet.rj.renv.core.REnvConfiguration;
 import org.eclipse.statet.rj.renv.core.RLibGroup;
@@ -75,14 +74,14 @@
 	}
 	
 	protected void update(final ViewerCell cell, final REnv rEnv) {
-		cell.setImage(RUI.getImage((rEnv.getId().startsWith(RCore.USER_REMOTE_ENV_ID_PREFIX)) ?
-				RUI.IMG_OBJ_R_RUNTIME_ENV : RUI.IMG_OBJ_R_RUNTIME_ENV ));
+		cell.setImage(RUI.getImage(/*(rEnv.getId()) ?
+				RUI.IMG_OBJ_R_REMOTE_ENV : */RUI.IMG_OBJ_R_RUNTIME_ENV ));
 		cell.setText(rEnv.getName());
 	}
 	
 	protected void update(final ViewerCell cell, final REnvConfiguration rEnvConfig) {
 		cell.setImage(RUI.getImage((rEnvConfig.isRemote()) ?
-				RUI.IMG_OBJ_R_RUNTIME_ENV : RUI.IMG_OBJ_R_RUNTIME_ENV ));
+				RUI.IMG_OBJ_R_REMOTE_ENV : RUI.IMG_OBJ_R_RUNTIME_ENV ));
 		cell.setText(rEnvConfig.getName());
 	}
 	
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/LocalREnvConfigDialog.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/LocalREnvConfigDialog.java
index b0f89ad..9fbca7e 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/LocalREnvConfigDialog.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/LocalREnvConfigDialog.java
@@ -287,11 +287,12 @@
 			label.setText(Messages.REnv_Detail_Name_label+':');
 			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
 			
-			this.nameControl= new Text(area, SWT.BORDER);
+			final Text text= new Text(area, SWT.BORDER);
 			final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false);
-			gd.widthHint= LayoutUtils.hintWidth(this.nameControl, 60);
-			this.nameControl.setLayoutData(gd);
-			this.nameControl.setEditable(this.configModel.isEditable());
+			gd.widthHint= LayoutUtils.hintWidth(text, 60);
+			text.setLayoutData(gd);
+			text.setEditable(this.configModel.isEditable());
+			this.nameControl= text;
 		}
 		
 		if (this.configModel.isEditable()) {
diff --git a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/RemoteREnvConfigDialog.java b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/RemoteREnvConfigDialog.java
index 60d8e94..379a6db 100644
--- a/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/RemoteREnvConfigDialog.java
+++ b/r/org.eclipse.statet.r.ui/srcDebug/org/eclipse/statet/internal/r/debug/ui/preferences/RemoteREnvConfigDialog.java
@@ -86,6 +86,8 @@
 	public void create() {
 		super.create();
 		
+		updateEditable();
+		
 		PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(), IRUIHelpContextIds.R_ENV);
 	}
 	
@@ -107,10 +109,11 @@
 			label.setText(Messages.REnv_Detail_Name_label+':');
 			label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
 			
-			this.nameControl= new Text(area, SWT.BORDER);
+			final Text text= new Text(area, SWT.BORDER);
 			final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false);
-			gd.widthHint= LayoutUtils.hintWidth(this.nameControl, 60);
-			this.nameControl.setLayoutData(gd);
+			gd.widthHint= LayoutUtils.hintWidth(text, 60);
+			text.setLayoutData(gd);
+			this.nameControl= text;
 		}
 		
 		LayoutUtils.addSmallFiller(area, false);
@@ -212,4 +215,15 @@
 						.observe(this.configModel) );
 	}
 	
+	private void updateEditable() {
+		final boolean isEditable= this.configModel.isEditable();
+		this.nameControl.setEditable(isEditable);
+		this.indexDirectorySelectionControl.setEnabled(isEditable
+				|| this.indexDirectorySelectionControl.getSelection() );
+		this.indexDirectorResourceControl.setEditable(isEditable);
+		this.indexServerSelectionControl.setEnabled(isEditable
+				|| this.indexServerSelectionControl.getSelection() );
+		this.indexServerUrlControl.setEditable(isEditable);
+	}
+	
 }