User Settings: FAQ
last updated: January 19, 2005
General:
  1. What is a scope?
  2. What scopes are contributed by the Platform?
  3. This is confusing, how can I picture this in my mind?
  4. How do I know if auto-build is turned on? (How do I get a preference value?)
  5. How can I set the auto-build preference? (How do I set a preference value?)
  6. Why doesn't the backwards compatibility layer check all the scopes for a value?
  7. When are my preference changes saved to disk?
  8. How does the plug-in customization story work?
Conversion:
  1. What should I do to convert my code to the new mechanism?
  2. How do I convert my project property page to use project preferences?
  3. How do I convert my IMemento values?
Listeners:
  1. How do I know when changes have happened to my preference?
  2. But I want to listen to changes in ALL scopes, not just one.
Nodes:
  1. Why should I use nodes?
  2. Why am I getting an IllegalStateException when calling APIs?
Extensions:
  1. How do I extend the preferences and add my own scope?

What is a scope?

In Eclipse 2.1 there was only one place to store preferences via the Runtime APIs. Users would ask a particular plug-in for its preferences (Plugin.getPluginPreferences()) and the preferences would be store per plug-in, per workspace. This allowed some conveniences but it was obvious that the new preference mechanism needed to be extensible and allow settings to be stored in different places...users want to share their preferences between workspaces, configurations, and even share them with other users. This is the main reason that we created "scopes" for the preference store.

A scope basically refers to where the preferences are stored. Different preference scopes can be contributed by plug-ins so the number of scopes available to clients is not limited to those defined by the platform.


What scopes are contributed by the Platform?

INSTANCE - The instance scope can also be thought of as "workspace". That is, the preferences which are stored in this scope are stored per workspace, or per running instance of Eclipse. This scope corresponds to the default location of preferences in Eclipse 2.1. The instance area is derived from the location returned by org.eclipse.core.runtime.Platform#getInstanceLocation(). Preferences stored in this scope will not be available to other running instances of Eclipse.

CONFIGURATION - The configuration scope is used to store preferences on a per configuration level. Being able store preferences per configuration means that all workspaces share the same preferences. For instance, if you are an Eclipse developer and you have a single Eclipse install but you have multiple workspaces for different projects/branches that you are working on, the preferences stored in the configuration scope will be shared between these workspaces. The configuration area is derived from the location returned by org.eclipse.core.runtime.Platform#getConfigurationLocation().

DEFAULT - The default preference scope was initially created to provide a backwards compatibility story for the plug-in customization code in Eclipse 2.1. Preferences stored in the default scope are not persisted to disk and are a part of the plug-in customization story.

PROJECT - There was a request for per project preferences so the project scope handles this. Preferences which are stored in this scope are shared stored in the project content area and are therefore automatically shared with the repository. This enables items like java compiler settings to be shared between team members working on the same project.

This is confusing, how can I picture this in my mind?

The preferences can be thought of as a tree with each node having a name and children. The "path" of a node is the names of it and all its ancestors separated by a path separator.

Start at the root node. The root node's name is empty and has a path of "/". The root node's children are the names of the scopes which are defined. (e.g. instance, configuration, user, default, project, etc) Therefore their paths are: /instance, /configuration, /user, /default, and /project.

The path structure under the scope name is defined by the scope itself. The scopes contributed by the Platform Runtime (instance, configuration, user, default) define the next segement to be a qualifier. (like a plug-in or bundle id) An example of the full path for the autobuild preference for the resources plug-in in the instance scope would be:
    /instance/org.eclipse.core.resources/description.autobuild

The project scope (as defined by the resources plug-in) defines its path structure to be /project/<projectName>/<qualifier>/key and therefore the full path for the resources' plug-in auto-build preference for the project "MyProject" would be:
    /project/MyProject/org.eclipse.core.resources/description.autobuild


How do I know if auto-build is turned on? (How do I get a preference value?)

1. Through the backwards compatibility layer.

All the plug-in preference APIs which existed in Eclipse 2.1 still exist and are fully supported through a backwards compatibility layer. All the client has to do is ask the org.eclipse.core.resources plug-in for its preferences.

The downside to this is that not all of the preference scopes are checked for a value. In this case first the INSTANCE scope is checked, and then the DEFAULT scope. If a value doesn't exist in either scope, then the default-default value for that type is returned.

String key = ResourcesPlugin.PREF_AUTO_BUILDING;
return ResourcesPlugin.getPluginPreferences().getBoolean(key);

2. Via navigation from the root node.

The new Eclipse preference mechanism is structured in a hierarchial means and therefore the user is able to retrieve the root of the hierarchy and navigate to the particular preference node that they desire. This is, of course, assuming the know the path structure of the node they are trying to retrieve. The example below retrives the value from the INSTANCE scope.

String qualifier = ResourcesPlugin.getDescriptor().getUniqueIdentifier();
String key = ResourcesPlugin.PREF_AUTO_BUILDING;
boolean defaultValue = false;
Preferences node = Platform.getPreferencesService().getRootNode().node(InstanceScope.SCOPE).node(qualifier);
return node.getBoolean(key, defaultValue);

3. Through the context objects.

If the user knows which preference scope their preference is in but they don't know the hierarchy structure of the scope (or don't want to have to deal with it), then they are able to use the scope context API to retrieve the correct node for their preferences.

String qualifier = ResourcesPlugin.getDescriptor().getUniqueIdentifier();
String key = ResourcesPlugin.PREF_AUTO_BUILDING;
boolean defaultValue = false;
Preferences node = new InstanceScope().getNode(qualifier);
if (node != null)
  return node.getBoolean(key, defaultValue);

4a. Search through the preferences service. (manual lookup order)

Having preferences stored in a single location, although useful, is uninteresting compared to being able to store preferences in multiple scopes. If a user has a collection of multiple preference nodes that they would like to check for a particular preference value, then they are able to use the API on IPreferencesService to check the multiple nodes and return the first found value, or the specified default if it is not found. The example below first checks the INSTANCE scope and then checks the USER scope.

IPreferencesService service = Platform.getPreferencesService();
String qualifier = ResourcesPlugin.getDescriptor().getUniqueIdentifier();
String key = ResourcesPlugin.PREF_AUTO_BUILDING;
String defaultValue = "false";
Preferences root = service.getRootNode();
Preferences instanceNode = root.node(InstanceScope.SCOPE).node(qualifier);
Preferences userNode = root.node(UserScope.SCOPE).node(qualifier);
Preferences[] nodes = new Preferences[] {instanceNode, userNode};
return service.get(key, defaultValue, nodes);

4b. Search through the preferences service. (default lookup order)

Navigation from the root node to other nodes in the preference hierarchy can be cumbersome so we expect most preference users to take advantage of this second API. It is a convenience method to the above method.

First they (in most cases the plug-in developer who is contributing the preference) set the scope lookup order (if none is specified then the default-default {project, instance, configuration, user, default} is used.

Some scopes require extra context to determine which preference nodes are the right ones to look at. So if necessary, callers are able to create IScopeContext instances to aid in lookup. Note that these are not necessary for the INSTANCE, CONFIGURATION, USER, and DEFAULT scopes.

In the example below, we first look to see if the preference has been set on the project preferences for "MyProject" and then we check the INSTANCE preferences.

IPreferencesService service = Platform.getPreferencesService();
String qualifier = ResourcesPlugin.getDescriptor().getUniqueIdentifier();
String key = ResourcesPlugin.PREF_AUTO_BUILDING;
boolean defaultValue = false;
String[] lookupOrder = new String[] {ProjectScope.SCOPE, InstanceScope.SCOPE};
service.setDefaultLookupOrder(qualifier, key, lookupOrder);
IScopeContext contexts = new IScopeContext[] {new ProjectScope("MyProject")};
return service.getBoolean(qualifier, key, defaultValue, contexts);


How can I set the auto-build preference? (How do I set a preference value?)
1. Through the backwards compatibility layer.

Again, all the old APIs exist and are fully functional.

String key = ResourcesPlugin.PREF_AUTO_BUILDING;
boolean value = true;
ResourcesPlugin.getPluginPreferences().setValue(key, value);

2. Via navigation from the root node.

If the user knows which scope they wish to store the preference in and knows the path structure for the hierarchy of that scope, then they are able to navigate directly to that preference node and set their preference value. In the example below we are setting the auto-build setting in the INSTANCE scope.

String qualifier = ResourcesPlugin.getDescriptor().getUniqueIdentifier();
String key = ResourcesPlugin.PREF_AUTO_BUILDING;
boolean value = true;
IEclipsePreferences root = Platform.getPreferencesService().getRootNode();
root.node(InstanceScope.SCOPE).node(qualifier).putBoolean(key, value);

3. Through the scope context.

If the user knows which scope they wish to set the preference value in but doesn't know the path structure for that scope, (or prefers not to navigate it themselves) then they can use the convenience methods in IScopeContext to determine the correct preference node. This is the method in which we envision most clients setting preference values. In the example below we are storing the preference in the project preferences for "MyProject".

String qualifier = ResourcesPlugin.getDescriptor().getUniqueIdentifier();
String key = ResourcesPlugin.PREF_AUTO_BUILDING;
boolean value = true;
IScopeContext context = new ProjectScope(MyProject);
IEclipsePreferences node = context.getNode(qualifier);
if (node != null)
    node.putBoolean(key, value);


What should I do to convert my code to the new mechanism?

Getting a value using the old code only checks the INSTANCE and DEFAULT scopes:
  ResourcesPlugin.getPluginPreferences().getBoolean(key);

Getting a value using the new code looks in all scopes as defined by the look-up order set in the preferences service:
  Platform.getPreferencesService().getBoolean(ResourcesPlugin.PI_RESOURCES, key, defaultValue);

Setting a value using the old code either puts the value in the INSTANCE scope or clears it if it is the same as the value in the DEFAULT scope:
  ResourcesPlugin.getPluginPreferences().setValue(key, value);

Setting a value using the new code is a bit more complicated. Clients must know which scope they wish to store their value in. For instance:
  Preferences node = new InstanceScope().getNode(ResourcesPlugin.PI_RESOURCES);
  if (node != null)
    node.put(key, value);


How do I convert my project property page to use project preferences?

Rather than use the generalized look-up mechanism to find out your project-specific values, I would create a project context and do the lookup from there. This ensures that you either get the value that is defined in the project scope or nothing. (the default value that you specify) For example:

// setup
IScopeContext context = new ProjectScope(MyProject);
IEclipsePreferences node = context.getNode("org.eclipse.jdt.core");
int defaultValue = JavaCore.SEVERITY_IGNORE;
String key = JavaCore.NON_NLS_SEVERITY;
int value = defaultValue;

...

// get the value
if (node != null)
  value = node.getInt(key, defaultValue);
if (value == defaultValue) {
  // value isn't set in the project scope.
  // value is "inherited" from another scope.
} else {
  // we have a value set in the project scope
}

...

// set the value
if (node != null) {
  if (value == defaultValue) {
    // value is same as default so no need to store it
    node.remove(key);
  } else {
    // set a project-specific value
    node.put(key, value);
}

...

// save the changes
if (node != null) {
  // save the project specific values
  node.flush();
}


How do I convert my IMemento values?

Its easy! IMementos are a method of storing hierarchial data and that's just what the node hierarchy is.

To set a complex IMemento preference value using Eclipse 2.1 preferences:

String key = "providers";
XMLMemento root = XMLMemento.createWriteRoot(key);
IMemento dev = root.createChild("dev.eclipse.org");
dev.putString("connnectionType", "pserver");
dev.putString("login", "anonymous");
dev.putString("path", "/cvsroot/eclipse");
IMemento example = root.createChild("cvs.example.com");
example.putString("connnectionType", "pserver");
example.putString("login", "anonymous");
example.putString("path", "/home/cvs");
Writer writer = new StringWriter();
try {
  root.save(writer);
} finally {
  writer.close();
}
MyPlugin.getDefault().getPluginPreferences().setValue(key, writer.toString());
...
MyPlugin.getDefault().savePluginPreferences();

To set the same complex values using Eclipse 3.0 preferences:

// Note: can create other context here including Project
IScopeContext context = new InstanceScope();
Preferences root = context.getNode("org.eclipse.team");
// Note: should not be null
if (root != null) {
  Preferences dev = root.node("providers/dev.eclipse.org");
  dev.put("connectionType", "pserver");
  dev.put("login", "anonymous");
  dev.put("path", "/cvsroot/eclipse");
  Preferences example = root.node("providers/cvs.example.com");
  example.put("connectionType", "pserver");
  example.put("login", "anonymous");
  example.put("path", "/home/cvs");
  ...
  root.flush();
}

To get a value using the Eclipse 2.1 preferences:

String xmlString = MyPlugin.getDefault().getPluginPreferences().getString("providers");
Reader reader = new StringReader(xmlString);
XMLMemento root = XMLMemento.createReadRoot(reader);
IMemento dev = root.getChild("dev.eclipse.org");
return dev.getString("connnectionType");

To get the same value using Eclipse 3.0 preferences:

// Note: can create other context here including Project
IScopeContext context = new InstanceScope();
Preferences root = context.getNode("org.eclipse.team");
// Note: should not be null
if (root != null) {
  Preferences node = root.node("providers/dev.eclipse.org");
  return node.get("connectionType", null);
}


Why doesn't the backwards compatibility layer check all the scopes for a value?

The backwards compatibility layer only checks for preference values in the INSTANCE and DEFAULT scopes for two main reasons.

Firstly. we wanted to maintain true backwards compatibility. If the "get" methods checked for preference values in scopes other than INSTANCE and DEFAULT, then the behaviour would not be the same as was in Eclipse 2.1. That is, if there was a value set for another scope it would be returned.

The second reason is that it confuses the current UI story. People have created many preference pages which show the current preference values. This maps on to the preferences stored in the INSTANCE scope. If we changed the backwards compatibility story to look in the other scopes for preference values, then the values presented to the user would be mixed...some would be from the INSTANCE scope, some from the USER scope, etc etc.

The short story is that we have to try to develop a UI which presents preferences and their scopes to the user in a simple and managable way.


When are my preference changes saved to disk?

The short answer is "whenever you change them you need to save them". In Eclipse 2.1 there was API to save plug-in preferences (Plugin.savePluginPreferences()) which clients were encouraged to call on plug-in shutdown and when they changed their preference values (like in a preference page).

It is important to note that this old API now corresponds to saving the preferences for that plug-in in the INSTANCE scope only.

Now, it is recommended that people save their preference changes to disk by calling the Preferences.flush() method. For instance, preference page owners would call this after setting all the preference values when the user hits "OK".


Why should I use nodes?

For most plug-ins, simple key/value pairs are well suited for their preference values and they will never have to take advantage of the node hierarchy other than the basic traversal. The interesting use case is for those clients who previously used mark-up (like XML) to encode their preference values. XML and its elements are essentially containers...a way to group together similar values in a hierarchical manner. The use of nodes can simplify this for clients.

Each node in the hierarchy has a name and its path is represented by its name and all its parents' names separated by the path separator. This allows preference keys at arbitrary depths in the tree so a node's children nodes could be used in a "contains" relationship.

Think, for instance, of the list of Team provider connections in Eclipse. Rather than having a single preference key and the value being wrapped in a wad of XML, it could be split across multiple nodes and be more accessible. For instance consider the following:

  /instance/org.eclipse.team/providers/example.com/path=/cvsroot/eclipse
  /instance/org.eclipse.team/providers/example.com/login=anonymous
  /instance/org.eclipse.team/providers/example.com/connectionType=pserver
  /instance/org.eclipse.team/providers/dev.eclipse.org/path=/cvsroot/eclipse
  /instance/org.eclipse.team/providers/dev.eclipse.org/login=anonymous
  /instance/org.eclipse.team/providers/dev.eclipse.org/connectionType=pserver

In this example the list of providers could be retrieved as children of the /instance/org.eclipse.team/providers node and then each individual provider's login information as preferences in children nodes.


Why am I getting an IllegalStateException when calling APIs?

Since the Preferences APIs include the ability to add nodes to the hierarchy, they also include methods to remove nodes from the hierarchy. If a node has been removed and a client still has a reference to it, the client will get an IllegalStateException when calling APIs on that node. The only valid methods to call on a removed node are: name(), absolutePath() and nodeExists().

If clients aren't sure whether or not their node may have been removed since the last time they accessed it, they are encouraged to re-retrieve the node from the hierarchy.

if (!node.nodeExists(""))
    node = service.getRootNode().node(node.absolutePath());


How does the plug-in customization story work?

Check out the separate document on plug-in customization. It explains the order that preference defaults are applied and compares this to the way it worked in Eclipse 2.1.


How do I extend the preferences and add my own scope?

1. Extend the org.eclipse.core.runtime.preferences extension point.

	<extension point="org.eclipse.core.runtime.preferences">
	    <scope name="myScope" class="com.example.preferences.MyScope"/>
	</extension>
      

2. Have your class implement IScope and IEclipsePreferences.

	  public class MyScope implements IScope, IEclipsePreferences {
	      ...
	  }
	  

Here is an example plug-in which creates a new scope.