/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.imp.preferences;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lpg.runtime.IAst;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.imp.preferences.PreferenceValueParser.ASTNode;
import org.eclipse.imp.preferences.PreferenceValueParser.AbstractVisitor;
import org.eclipse.imp.preferences.PreferenceValueParser.escapedChar;
import org.eclipse.imp.preferences.PreferenceValueParser.optParameter;
import org.eclipse.imp.preferences.PreferenceValueParser.simpleStringPrefixed;
import org.eclipse.imp.preferences.PreferenceValueParser.substPrefixed;
import org.eclipse.imp.preferences.PreferenceValueParser.substitution;
import org.eclipse.imp.preferences.PreferenceValueParser.substitutionList;
import org.eclipse.imp.runtime.RuntimePlugin;
import org.osgi.framework.Bundle;
import org.osgi.service.prefs.BackingStoreException;
/**
* An implementation of the IMP interface IPreferencesService, which gives IMP clients access
* to language-specific and global preference settings. An adaptation of the Eclipse
* PreferencesService class, and built on top of it, with simplifications appropriate to IMP use.
* <p>To instantiate, you may supply a project, but you must supply a language ID. These parameters
* serve to qualify the preference keys used in queries to (a) properly separate the use of common
* keys by multiple language IDEs, and (b) to permit distinct preference settings for any given
* project in the user's workspace.
*
* <p>This class is intended to be instantiated by IMP clients. It is not intended to be sub-classed.
* @see IPreferencesService
*
* @author sutton
* @author rfuhrer
*/
public class PreferencesService implements IPreferencesService {
private IProject project = null;
private String projectName = null;
private String languageName = null;
org.eclipse.core.runtime.preferences.IPreferencesService preferencesService = null;
IEclipsePreferences preferencesRoot = null;
// Scopes at the four standard preference levels
// All are unique but the project scope
private ProjectScope projectScope = null;
private ConfigurationScope configurationScope = new ConfigurationScope();
private InstanceScope instanceScope = new InstanceScope();
private DefaultScope defaultScope = new DefaultScope();
/*
* Constructors
*/
public PreferencesService() {
getPreferencesServiceAndRoot();
}
/**
* @param projectName the name of a workspace project to use to qualify preference
* queries; may be null/empty
*/
public PreferencesService(String projectName) {
this();
setProjectName(projectName);
}
/**
* @param project the project to use to qualify preference queries; may be null
*/
public PreferencesService(IProject project) {
this();
setProject(project);
}
/**
* @param project the project to use to qualify preference queries; may be null
* @param languageName the language ID to use to qualify preference queries; must not be null
* @see org.eclipse.imp.language.Language
*/
public PreferencesService(IProject project, String languageName) {
this(project);
setLanguageName(languageName);
}
/*
* Utilities for constructors and (re)initializations
*/
private void getPreferencesServiceAndRoot() {
preferencesService = Platform.getPreferencesService();
preferencesRoot = (preferencesService != null) ? preferencesService.getRootNode() : null;
}
private static IProject getProjectFromName(String name) {
if (name == null || name.equals("")) return null;
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject project = workspaceRoot.getProject(name);
if (project.exists()) return project;
return null;
}
/*
* Methods to get and set project, project name, and language name
* @see org.eclipse.imp.preferences.service.IPreferencesService#setLanguageName(java.lang.String)
*/
// TODO: Add (or refine) error checks!
public void setLanguageName(String languageName) {
this.languageName = languageName;
}
public String getLanguageName() { return languageName; }
public void setProjectName(String projectName) {
// if (projectName == null || projectName.equals("")) {
// System.out.println("PreferencesService.setProjectName: name is null or empty; clearing project, project name, and project scope");
// }
if (projectName != null && projectName.equals("")) projectName = null;
this.projectName = projectName;
project = getProjectFromName(projectName);
if (project != null ) {
ProjectScope oldProjectScope = projectScope;
projectScope = new ProjectScope(project);
toggleProject(oldProjectScope, projectScope, projectName);
}
else projectScope = null;
}
private void toggleProject(ProjectScope oldProjectScope, ProjectScope newProjectScope, String projectName) {
ProjectSelectionEvent event = null;
if (projectName == null)
event = new ProjectSelectionEvent(null, null);
else if (oldProjectScope == null && newProjectScope == null)
event = new ProjectSelectionEvent(null, null);
else if (oldProjectScope == null && newProjectScope != null)
event = new ProjectSelectionEvent(null, newProjectScope.getNode(projectName));
else if (oldProjectScope != null && newProjectScope == null)
event = new ProjectSelectionEvent(oldProjectScope.getNode(projectName), null);
else if (oldProjectScope != null && newProjectScope != null)
event = new ProjectSelectionEvent(oldProjectScope.getNode(projectName), newProjectScope.getNode(projectName));
// so now event should be defined ...
fireProjectSelectionEvent(event);
}
public String getProjectName() { return projectName; }
public void setProject(IProject project) {
ProjectScope oldProjectScope = projectScope;
this.project = project;
if (project != null) {
projectName = project.getName();
// oldProjectScope = projectScope;
projectScope = new ProjectScope(project);
//toggleProject(oldProjectScope, projectScope, projectName);
} else {
//System.out.println("PreferencesService.setProject: project is null; clearing project, project name, and project scope");
projectName = null;
projectScope = null;
}
toggleProject(oldProjectScope, projectScope, projectName);
}
public IProject getProject() { return project; }
/*
* Get and set preferences by batch by level
*/
public IEclipsePreferences getPreferences(String level) {
if (level == null) {
throw new IllegalArgumentException("PreferencesService.getPreferences: given level is null");
}
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException("PreferencesService.getPreferences: given level is not valid (level = " + level);
}
IEclipsePreferences preferences = null;
if (level.equals(PROJECT_LEVEL)) {
if (projectScope != null && languageName != null) {
preferences = projectScope.getNode(languageName);
}
} else if (level.equals(INSTANCE_LEVEL)) {
preferences = instanceScope.getNode(languageName);
} else if (level.equals(CONFIGURATION_LEVEL)) {
preferences = configurationScope.getNode(languageName);
} else if (level.equals(DEFAULT_LEVEL)) {
preferences = defaultScope.getNode(languageName);
}
return preferences;
}
/**
* Set the preferences at the given level to the given new preferences,
* clearing any existing preferences. If the new preferences are null,
* just clear the existing preferences.
*
* @param level the level at which preferences are to be set; must be
* one of the values defined in IPreferencesService
* @param newPreferences the new preferences to be set at the given level (or
* null if the existing preferences are just to be cleared)
* @throws IllegalArgumentException
* if the given level is null or not one of the values
* defined in IPreferencesService
*/
public void setPreferences(String level, IEclipsePreferences newPreferences) {
if (level == null) {
throw new IllegalArgumentException(
"PreferencesService.getPreferences: given level is null");
}
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException(
"PreferencesService.getPreferences: given level is not valid (level = " + level);
}
IEclipsePreferences node = clearPreferencesAtLevel(level);
if (newPreferences == null) return;
String[] keys = null;
try {
keys = newPreferences.keys();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("BackingStoreException getting keys; returning (note: preferences for this node may have been cleared)", e);
return;
}
for (int i = 0; i < keys.length; i++) {
// Assume that if we get values out they're legal, and
// and that if they're legal we can safely put them in
String val = newPreferences.get(keys[i], null);
node.put(keys[i], val);
}
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("BackingStoreException flushing new preferences; returning (notepreferences for this node may have been cleared and not set)", e);
return;
}
}
/*
* Documentary interlude:
*
* The methods to get an applicable preference by type make use of the corresponding
* type-specific "get" methods in the Eclipse PreferencesService. Those methods take
* a qualifier (i.e., that designates a name space), a key, a default value, and
* a (possibly empty) list of scope contexts to be checked ahead of the standard
* contexts that are automatically checked in the standard order (project, instance,
* configuration, and default).
*
* Recall that, while the PreferencesService allows preference values to be gotten,
* it provides no way to set preference values. The setting of preference values
* has to be done through preference nodes that are associated with scope contexts.
*
* We would like to be able to use a single qualifier to get the preferences for
* a particular language (e.g., qualifier "X10" for language X10). When we set
* a preference at a level, we can provide just such a simple qualifier since
* we know the language name, e.g., in
* setIntPreference(String level, String key, int value)
* we can get the scope for the level, get the preferences node for the language-
* name qualifier, and set the value in the node. That's simple. If the level
* is (for example) "instance", then the path to the target preference node in the
* preferences model is
* /root/instance/<languageName>
* but if the level is "project" then the path to the target preference node is
* /root/project/<projectName>/<languageName>
* However, when you retrieve the preference from the Eclipse PreferencesService
* using the language name as qualifier and the appropriate key, the service
* will search on the project level for
* /root/project/<languageName>
* In other words, the project is not taken into account by the service and that
* level is ignored in the preferences model.
*
* The Eclipse PreferencesService can accommodate project names as part of the
* qualifier. So, given a preference stored in the project node as above, it
* can be retrieved using <projectName>/<languageName> as the qualifer. This leads
* the service to search the preferences model along the path
* root/project/<projectName>/<languageName>
* where it will be found. However, if you add the project name to the language
* name in the qualifier on the other preferences levels, that will lead to
* searching on paths like
* /root/instance/<projectName>/<languageName>
* which is not how the preferences would have been stored on the other levels
* if the qualifier was just the language name.
*
* It's possible to use the project name as part of the qualifier at the instance,
* configuration, and default levels. That would allow the same qualifier to be
* used at all levels. However, that would result, in effect, in project-specific
* preferences at levels that are supposed to be project independent. It would
* also complicate the setting of preferences, which, at the project-independent
* levels, would have to be done consistently for each project where a language
* is used rather than just once for all projects where a language is used.
*
* Alternatively, it's possible to use different qualifiers at the project level
* and project-independent levels, but that complicates the use of the preferences
* service, requiring clients to query for preferences using different qualifiers
* in different cases.
*
* To keep things simple for clients, I've taken advantage of the ability to
* provide the Eclipse PreferencesService with scopes to be searched ahead of
* the standard scopes. That is, each of the type-specific "get" methods defined
* here calls the corresponding Eclipse-service get method, providing it with
* the project-specific scope in which the simple language-name qualifier will
* be recognized. Users can thus use the language name as a qualifier that will
* be meaningful across all scope levels. They will not have to worry about whether
* different qualifiers are needed on different levels and whether the project name
* should or should not be part of a qualifier.
*/
/*
* Get applicable preferences by type
*/
private IScopeContext[] specificProjectScope() {
if (projectScope == null) return new IScopeContext[0];
return new IScopeContext[] { getScopeForLevel(IPreferencesService.PROJECT_LEVEL)};
}
public boolean getBooleanPreference(String key) {
return preferencesService.getBoolean(languageName, key, false, specificProjectScope());
}
/*
* The implementation of the "get" method for byte arrays is more complicated
* than for other types due to the way in which the preferences nodes encode
* byte arrays while the preferences service ignores encoding.
*
* Things considered in this implementation:
* - Byte arrays are encoded when written to a preferences node and
* decoded when read from the node but not from the Eclipse Preferences
* Service; that is addressed here by getting the value from the
* preferences node rather than from the preferences service
* - The IMP Preferences Service and the Eclipse Preferences Service
* both have the same four standard levels and these are by default
* in the same standard order
* - But the Eclipse levels may have been reordered (although I'm not
* doing that), so this implementation does not assume that they're
* in the same order
* - The levels are named in different ways in the two services, e.g.,
* "project" in Eclipse and "PROJECT_LEVEL" in IMP, so that has to
* be accommodated
* Things not considered:
* - Additional levels that may have been added to the preferences model
* (but I'm certainly not adding any!)
*
* @see org.eclipse.imp.preferences.service.IPreferencesService#getByteArrayPreference(java.lang.String)
*/
public byte[] getByteArrayPreference(String key) {
byte[] result = null;
String[] lookupOrder = preferencesService.getLookupOrder(languageName, key);
String[] levels = IPreferencesService.levels;
outer: for (int i = 0; i < lookupOrder.length; i++) {
for (int j = 0; j < levels.length; j++) {
String level = null;
if (levels[j].startsWith(lookupOrder[i].toUpperCase())) {
level = levels[j];
IScopeContext context = getScopeForLevel(level);
IEclipsePreferences node = context.getNode(languageName);
result = node.getByteArray(key, new byte[0]);
if (result.length == 0) continue;
break outer;
}
}
}
return result;
}
public double getDoublePreference(String key) {
return preferencesService.getDouble(languageName, key, 0, specificProjectScope());
}
public float getFloatPreference(String key) {
return preferencesService.getFloat(languageName, key, 0, specificProjectScope());
}
public int getIntPreference(String key) {
return preferencesService.getInt(languageName, key, 0, specificProjectScope());
}
public long getLongPreference(String key) {
return preferencesService.getLong(languageName, key, 0, specificProjectScope());
}
public String getStringPreference(String key) {
String result = preferencesService.getString(languageName, key, null, specificProjectScope());
//if (result.equals("")) {
// result = null;
//}
return performSubstitutions(result);
}
public String getRawStringPreference(String key) {
String result = preferencesService.getString(languageName, key, null, specificProjectScope());
return result;
}
/*
* Get applicable preferences by project and type
*
* Requests are evaluated relative to an implicit "current language."
* It is an error to request a preference when the current langauge is
* not defined, because all preferences are assumed to be defined relative
* to some language.
*
* Requests are evaluated relative to a given project, regardless of
* whether there is a "current project" defined.
*
* Preferences are returned from the lowest level at which set. If no
* preference is defined for a given key at all levels, then an apporpriate
* default value is returned. This is 0 for the numeric preferences,
* false for boolean preferences, the empty string for String preferences,
* and the empty array for byte[].
*/
public boolean getBooleanPreference(IProject project, String key) {
return preferencesService.getBoolean(languageName, key, false, new IScopeContext[] { new ProjectScope(project) } );
}
/*
* This is more complicated than retrieving values for the other types because of
* the encoding that is done (or not done) differently between the preferences service
* and preferences nodes. See the version of getByteArrayPreference(..) that only takes
* a key for more detailed comments.
*
* @see org.eclipse.imp.preferences.IPreferencesService#getByteArrayPreference(org.eclipse.core.resources.IProject, java.lang.String)
*/
public byte[] getByteArrayPreference(IProject project, String key) {
byte[] result = null;
String[] lookupOrder = preferencesService.getLookupOrder(languageName, key);
String[] levels = IPreferencesService.levels;
outer: for (int i = 0; i < lookupOrder.length; i++) {
for (int j = 0; j < levels.length; j++) {
String level = null;
if (levels[j].startsWith(lookupOrder[i].toUpperCase())) {
level = levels[j];
IScopeContext context = null;
if (level.equals(PROJECT_LEVEL)) {
context = getScopeForProject(project);
} else {
context = getScopeForLevel(level);
}
IEclipsePreferences node = context.getNode(languageName);
result = node.getByteArray(key, new byte[0]);
if (result.length == 0) continue;
break outer;
}
}
}
return result;
}
public double getDoublePreference(IProject project, String key) {
return preferencesService.getDouble(languageName, key, 0, specificProjectScope());
}
public float getFloatPreference(IProject project, String key) {
return preferencesService.getFloat(languageName, key, 0, specificProjectScope());
}
public int getIntPreference(IProject project, String key) {
return preferencesService.getInt(languageName, key, 0, specificProjectScope());
}
public long getLongPreference(IProject project, String key) {
return preferencesService.getLong(languageName, key, 0, specificProjectScope());
}
public String getRawStringPreference(IProject project, String key) {
return preferencesService.getString(languageName, key, null, specificProjectScope());
}
public String getStringPreference(IProject project, String key) {
String value= getRawStringPreference(project, key);
String result= performSubstitutions(value, project);
return result;
}
/**
* An evaluator for non-parameterized (constant) references.
*/
private interface ConstantEvaluator {
String getValue();
}
private static final Map<String,ConstantEvaluator> sConstantMap= new HashMap<String, ConstantEvaluator>();
static {
sConstantMap.put("workspaceLoc", new ConstantEvaluator() {
public String getValue() {
return ResourcesPlugin.getWorkspace().getRoot().getLocation().toPortableString();
}
});
sConstantMap.put("os", new ConstantEvaluator() {
public String getValue() {
return Platform.getOS();
}
});
sConstantMap.put("arch", new ConstantEvaluator() {
public String getValue() {
return Platform.getOSArch();
}
});
sConstantMap.put("nl", new ConstantEvaluator() {
public String getValue() {
return Platform.getNL();
}
});
sConstantMap.put("ws", new ConstantEvaluator() {
public String getValue() {
return Platform.getWS();
}
});
}
/**
* An evaluator for parameterized value references.
*/
private interface ParamEvaluator {
String getValue(String param);
}
private static final Map<String,ParamEvaluator> sParamMap= new HashMap<String, ParamEvaluator>();
static {
sParamMap.put("pluginLoc", new ParamEvaluator() {
public String getValue(String pluginID) {
Bundle bundle= Platform.getBundle(pluginID);
if (bundle == null) {
return "<no such plugin: " + pluginID + ">";
}
try {
String bundleLoc= FileLocator.toFileURL(bundle.getEntry("")).getFile();
return bundleLoc;
} catch (IOException e) {
return "<error determining location of plugin: " + pluginID + ">";
}
// Bundle[] fragments= Platform.getFragments(bundle);
}
});
sParamMap.put("pluginResource", new ParamEvaluator() {
public String getValue(String pluginResourceLoc) {
int idx= pluginResourceLoc.indexOf('/');
if (idx <= 0) {
return "<error in pluginResource specification: no plugin ID found: " + pluginResourceLoc + ">";
}
String pluginID= pluginResourceLoc.substring(0, idx);
String resourcePath= pluginResourceLoc.substring(idx+1);
Bundle bundle= Platform.getBundle(pluginID);
if (bundle == null) {
return "<no such plugin: " + pluginID + ">";
}
try {
URL resourceEntry= bundle.getEntry(resourcePath);
if (resourceEntry == null) {
Bundle[] fragments= Platform.getFragments(bundle);
if (fragments != null) {
for(int i= 0; i < fragments.length; i++) {
Bundle bundleFrag= fragments[i];
resourceEntry= bundleFrag.getEntry(resourcePath);
if (resourceEntry != null) {
break;
}
}
}
}
if (resourceEntry == null) {
return "<error: no resource '" + resourcePath + "' in plugin " + pluginID + " or its fragments>";
}
String resourceLoc= FileLocator.toFileURL(resourceEntry).getFile();
// RMF 3/17/2009 Make sure the path is really a valid one: toFileURL().getFile()
// for some reason sometimes produces invalid paths like "/C:/..." on Win32.
// Passing this through Path.toPortableString() seems to address the problem.
resourceLoc= new Path(resourceLoc).toPortableString();
return resourceLoc;
} catch (IOException e) {
return "<error determining location of plugin: " + pluginID + ">";
}
}
});
sParamMap.put("pluginVersion", new ParamEvaluator() {
public String getValue(String pluginID) {
Bundle bundle= Platform.getBundle(pluginID);
if (bundle == null) {
return "<no such plugin: " + pluginID + ">";
}
return (String) bundle.getHeaders().get("Bundle-Version");
}
});
sParamMap.put("projectLoc", new ParamEvaluator() {
public String getValue(String projectName) {
IProject project= getProjectFromName(projectName);
if (project == null) {
return "<no such project: " + projectName + ">";
}
return project.getLocation().toPortableString();
}
});
}
private static void initializeForTesting() {
sConstantMap.clear();
sConstantMap.put("workspaceLoc", new ConstantEvaluator() {
public String getValue() {
return "~/eclipse/workspace";
}
});
sConstantMap.put("os", new ConstantEvaluator() {
public String getValue() {
return "macosx";
}
});
sConstantMap.put("arch", new ConstantEvaluator() {
public String getValue() {
return "x86";
}
});
sConstantMap.put("nl", new ConstantEvaluator() {
public String getValue() {
return "enUS";
}
});
sConstantMap.put("ws", new ConstantEvaluator() {
public String getValue() {
return "mac";
}
});
sParamMap.clear();
sParamMap.put("pluginLoc", new ParamEvaluator() {
public String getValue(String pluginID) {
return "/System/Library/eclipse-3.3.2/plugins/" + pluginID;
}
});
sParamMap.put("pluginResource", new ParamEvaluator() {
public String getValue(String pluginResourceLoc) {
int idx= pluginResourceLoc.indexOf('/');
if (idx <= 0) {
return "<error in pluginResource specification: no plugin ID found: " + pluginResourceLoc + ">";
}
String pluginID= pluginResourceLoc.substring(0, idx);
String resourcePath= pluginResourceLoc.substring(idx+1);
return "/System/Library/eclipse-3.3.2/plugins/" + pluginID + "/" + resourcePath;
}
});
sParamMap.put("pluginVersion", new ParamEvaluator() {
public String getValue(String pluginID) {
return "1.0.0";
}
});
sParamMap.put("projectLoc", new ParamEvaluator() {
public String getValue(String projectName) {
return "~/eclipse/workspace/" + projectName;
}
});
}
public static void main(String args[]) {
initializeForTesting();
PreferencesService svc= new PreferencesService();
String[][] testPairs= new String[][] {
{ "foo", "foo", },
{ "${os}", "macosx" },
{ "foo-${os}", "foo-macosx" },
{ "${os}-bar", "macosx-bar" },
{ "foo-${os}-bar", "foo-macosx-bar" },
{ "${os}-${arch}", "macosx-x86" },
{ "${os}${arch}", "macosxx86" },
{ "${projectLoc:foo}", "~/eclipse/workspace/foo" },
{ "${pluginResource:lpg.runtime/templates}", "/System/Library/eclipse-3.3.2/plugins/lpg.runtime/templates" },
{ "${pluginResource:lpg.runtime/lpgexe/lpg-${os}_${arch}.exe}", "/System/Library/eclipse-3.3.2/plugins/lpg.runtime/lpgexe/lpg-macosx_x86.exe" },
{ "\\$", "$" },
{ "\\$abc", "$abc" },
{ "\\$\\$", "$$" },
{ "\\${blah\\}", "${blah}" },
{ "\\a", "a" },
{ "\\a\\b", "ab" },
{ "\\a-\\b", "a-b" },
{ "${blah", "Invalid preference: '${blah': unexpected end of string" },
{ "$blah}", "Invalid preference: '$blah}': unexpected character 'b' at offset 1" },
{ "${}", "Invalid preference: '${}': unexpected character '}' at offset 2" },
{ "$", "Invalid preference: '$': unexpected end of string" },
};
int failedCount= 0;
for(int i= 0; i < testPairs.length; i++) {
String input= testPairs[i][0];
String expectedOutput= testPairs[i][1];
String result= svc.performSubstitutions(input);
System.out.println("Input = '" + input + "'");
System.out.println("Result = '" + result + "'");
if (!expectedOutput.equals(result)) {
System.err.println("Test failed: expected output '" + expectedOutput + "'!");
failedCount++;
} else {
System.out.println("Test passed.");
}
System.out.println();
}
if (failedCount > 0) {
System.err.println("" + failedCount + " tests failed.");
}
}
public String performSubstitutions(String value) {
return performSubstitutions(value, null);
}
public String performSubstitutions(final String value, final IProject project) {
if (value == null || value.length() == 0) {
return value;
}
PreferenceValueParser parser= new PreferenceValueParser();
ASTNode ast= parser.parser(value);
if (ast == null) { return "Invalid preference: '" + value + "': " + parser.getErrorMessage(); }
final Map<IAst,String> valueMap= new HashMap<IAst, String>();
ast.accept(new AbstractVisitor() {
public void postVisit(IAst node) {
// System.out.println("post-visiting node '" + node + "'");
if (node instanceof escapedChar) {
escapedChar ch= (escapedChar) node;
String nodeStr= nodeString(ch);
if (nodeStr.charAt(1) == '$') {
valueMap.put(node, nodeStr.substring(1));
} else {
valueMap.put(node, nodeStr);
}
} else if (node instanceof simpleStringPrefixed) {
simpleStringPrefixed strPref= (simpleStringPrefixed) node;
String valString= nodeString((ASTNode) strPref.getvalStringNoSubst());
valString= valString.replaceAll("\\\\\\$", "$").replace("\\\\}", "}");
if (strPref.getsubstPrefixed() != null) {
String result= valString + valueMap.get(strPref.getsubstPrefixed());
valueMap.put(node, result);
} else {
valueMap.put(node, valString);
}
} else if (node instanceof substPrefixed) {
substPrefixed subPref= (substPrefixed) node;
if (subPref.getsimpleStringPrefixed() != null) {
String result= valueMap.get(subPref.getsubstitutionList()) + valueMap.get(subPref.getsimpleStringPrefixed());
valueMap.put(node, result);
} else {
valueMap.put(node, valueMap.get(subPref.getsubstitutionList()));
}
} else if (node instanceof substitutionList) {
substitutionList subList= (substitutionList) node;
StringBuilder sb= new StringBuilder();
for(int i=0; i < subList.size(); i++) {
sb.append(valueMap.get(subList.getsubstitutionAt(i)));
}
valueMap.put(node, sb.toString());
} else if (node instanceof substitution) {
substitution sub= (substitution) node;
String id= nodeString(sub.getident());
optParameter parm= sub.getoptParameter();
if (parm != null) {
ParamEvaluator e= sParamMap.get(id);
String parmStr= valueMap.get(parm.getvalue());
String parmVal= (e != null) ? e.getValue(parmStr) : ("<no such preference: " + id + ">");
valueMap.put(node, parmVal);
} else {
if (sConstantMap.containsKey(id)) {
String constVal= sConstantMap.get(id).getValue();
valueMap.put(node, constVal);
} else {
String refVal= (project != null) ? getStringPreference(project, id) : getStringPreference(id);
valueMap.put(node, refVal);
}
}
}
}
public boolean preVisit(IAst element) {
return true;
}
private String nodeString(ASTNode node) {
return value.substring(node.leftIToken.getStartOffset(), node.rightIToken.getEndOffset()+1);
}
@Override
public void unimplementedVisitor(String s) { }
});
String result= valueMap.get(ast);
return result;
}
/*
* Get preferences at a given level by type
*/
// SMS 19 Sep 2006: Are these all wrong????
public boolean getBooleanPreference(String level, String key) {
//return preferencesService.getBoolean(languageName, key, false, new IScopeContext[] { getScopeForLevel(level)} );
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
boolean result = node.getBoolean(key, false);
return result;
}
public byte[] getByteArrayPreference(String level, String key) {
// SMS 19 Sep 2006: The following comment is now somewhat outdated in that
// direct access to the preferences nodes is used in all of the methods in this
// group, but the comment may still be informative with respect to particular
// issues that affect byte arrays.
//
// Why do the following instead of getting the value directly from the preferences service
// as is done with the other preferences types? Note that, while values are retrieved here
// from the preferences service, values are set through the preferences nodes for the individual
// scope contexts. (That is because the preferences service allows preferences to be retrieved
// directly but not set directly.) Also recall that all preferences are stored interanlly as
// Strings. When a byte array is set or retrieved through a preferences context, the byte
// array is encoded as a Base64 String. However, when a byte array is retrieved from the
// preferences service, it is just converted to a string in the "native" representation
// (that is, without regard to any specific encoding). Apparently the native encoding is not
// always Base64. Consequently, a byte array may be stored through the preferences node in
// one representation retrieved from the preferences service in another representation, so
// from the client's perspective the values stored and retrieved may be different. Since we
// elsewhere store byte array preferences through the node we retrieve them here from the node.
IScopeContext context = getScopeForLevel(level);
IEclipsePreferences node = context.getNode(languageName);
byte[] result = node.getByteArray(key, new byte[0]);
return result;
}
public double getDoublePreference(String level, String key) {
//return preferencesService.getDouble(languageName, key, 0, new IScopeContext[] { getScopeForLevel(level)} );
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
double result = node.getDouble(key, 0);
return result;
}
public float getFloatPreference(String level, String key) {
//return preferencesService.getFloat(languageName, key, 0, new IScopeContext[] { getScopeForLevel(level)} );
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
float result = node.getFloat(key, 0);
return result;
}
public int getIntPreference(String level, String key) {
//return preferencesService.getInt(languageName, key, 0, new IScopeContext[] { getScopeForLevel(level)} );
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
int result = node.getInt(key, 0);
return result;
}
public long getLongPreference(String level, String key) {
//return preferencesService.getLong(languageName, key, 0, new IScopeContext[] { getScopeForLevel(level)} );
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
long result = node.getLong(key, 0);
return result;
}
public String getStringPreference(String level, String key) {
/*
if (!isaPreferencesLevel(level)) return null;
String[] previousLevels = preferencesService.getDefaultLookupOrder(languageName, key);
String[] newLevels = new String[4];
for (int i = 0; i < newLevels.length; i++) {
newLevels[i] = level;
}
preferencesService.setDefaultLookupOrder(languageName, key, newLevels);
String result = preferencesService.getString(languageName, key, null, new IScopeContext[] { getScopeForLevel(level)} );
preferencesService.setDefaultLookupOrder(languageName, key, previousLevels);
// SMS 8 Sep 2006: leftover from debugging
if (result != null && result.equals("")) {
result = null;
result = "";
}
return result;
*/
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
String result = node.get(key, null);
return performSubstitutions(result);
}
/*
* Return the "raw" (unsubstituted) version of the preference value.
* Needed at least by field editors that may want to inherit a raw
* value from a higher level.
* SMS 21 Feb 2008
*/
public String getRawStringPreference(String level, String key) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
String result = node.get(key, null);
return result;
}
/*
* Get preferences for a given project by type
*
* Requests are evaluated relative to an implicit "current language."
* It is an error to request a preference when the current langauge is
* not defined, because all preferences are assumed to be defined relative
* to some language.
*
* Requests are evaluated relative to the given project only. If a
* preference for the given key is defined for that project, then the
* associated value is returned. If no preferences is defined
* for the given key in the given project, then a suitable default value
* is returned. This is 0 for the numeric preferences, false for boolean
* preferences, the null string for String preferences, and the empty
* array for byte[].
*/
public boolean getBooleanPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
boolean result = node.getBoolean(key, false);
return result;
}
public byte[] getByteArrayPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
byte[] result = node.getByteArray(key, new byte[0]);
return result;
}
public double getDoublePreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
double result = node.getDouble(key, 0);
return result;
}
public float getFloatPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
float result = node.getFloat(key, 0);
return result;
}
public int getIntPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
int result = node.getInt(key, 0);
return result;
}
public long getLongPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
long result = node.getLong(key, 0);
return result;
}
public String getStringPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
String result = node.get(key, null);
return performSubstitutions(result, project);
}
public String getRawStringPreferenceForProject(IProject project, String key) {
IScopeContext scope = getScopeForProject(project);
IEclipsePreferences node = scope.getNode(languageName);
String result = node.get(key, null);
return result;
}
/*
* Set preferences at a given level by type
*/
public void setBooleanPreference(String level, String key, boolean value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.putBoolean(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setByteArrayPreference(String level, String key, byte[] value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.putByteArray(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setDoublePreference(String level, String key, double value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.putDouble(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setFloatPreference(String level, String key, float value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.putFloat(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setIntPreference(String level, String key, int value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.putInt(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setLongPreference(String level, String key, long value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.putLong(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setStringPreference(String level, String key, String value) {
IScopeContext scope = getScopeForLevel(level);
IEclipsePreferences node = scope.getNode(languageName);
node.put(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
/*
* Get preferences for a given level, language, and project by type
*/
public boolean getBooleanPreference(String languageName, String projectName, String level, String key, boolean def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.getBoolean(key, def);
}
public byte[] getByteArrayPreference(String languageName, String projectName, String level, String key, byte[] def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.getByteArray(key, def);
}
public double getDoublePreference(String languageName, String projectName, String level, String key, double def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.getDouble(key, def);
}
public float getFloatPreference(String languageName, String projectName, String level, String key, float def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.getFloat(key, def);
}
public int getIntPreference(String languageName, String projectName, String level, String key, int def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.getInt(key, def);
}
public long getLongPreference(String languageName, String projectName, String level, String key, long def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.getLong(key, def);
}
public String getStringPreference(String languageName, String projectName, String level, String key, String def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return performSubstitutions(node.get(key, def), getProjectFromName(projectName));
}
public String getRawStringPreference(String languageName, String projectName, String level, String key, String def) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
return node.get(key, def);
}
/*
* Set preferences for a given level, language, and project by type
*/
public void setBooleanPreference(String languageName, String projectName, String level, String key, boolean value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.putBoolean(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setByteArrayPreference(String languageName, String projectName, String level, String key, byte[] value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.putByteArray(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setDoublePreference(String languageName, String projectName, String level, String key, double value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.putDouble(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setFloatPreference(String languageName, String projectName, String level, String key, float value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.putFloat(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setIntPreference(String languageName, String projectName, String level, String key, int value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.putInt(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setLongPreference(String languageName, String projectName, String level, String key, long value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.putLong(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public void setStringPreference(String languageName, String projectName, String level, String key, String value) {
IEclipsePreferences node = getNodeForParameters(languageName, level, projectName, key);
node.put(key, value);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("Exception setting preference in project '" + projectName + "' at level = " + level + "; key = " + key + "; value = " + value, e);
}
}
public IScopeContext getPreferencesScope(String level, IProject proj) {
if (level.equals(PreferencesService.CONFIGURATION_LEVEL)) {
return configurationScope;
} else if (level.equals(PreferencesService.INSTANCE_LEVEL)) {
return instanceScope;
} else if (level.equals(PreferencesService.DEFAULT_LEVEL)) {
return defaultScope;
} else if (level.equals(PreferencesService.PROJECT_LEVEL)) {
return new ProjectScope(proj);
}
return null;
}
public void validateParameters(String languageName, String level, String projectName, String key) {
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException("Given level is not a valid preferences level: " + level);
}
if (languageName == null || languageName.equals("")) {
throw new IllegalArgumentException("Given language name is null or empty");
}
if (level.equals(PreferencesService.PROJECT_LEVEL)) {
if ((projectName == null) || projectName.equals("")) {
throw new IllegalArgumentException("Level is 'project' but project name is null or empty");
}
IProject proj = getProjectFromName(projectName);
if (proj == null) {
throw new IllegalArgumentException("Level is 'project' but project name '" + projectName + "' does not denote an existing project");
}
}
if (key == null || key.equals("")) {
throw new IllegalArgumentException("Key is null or empty");
}
}
public IEclipsePreferences getNodeForParameters(String languageName, String level, String projectName, String key) {
validateParameters(languageName, level, projectName, key);
IProject proj = getProjectFromName(projectName);
IScopeContext scope = getPreferencesScope(level, proj);
if (scope == null) {
throw new IllegalStateException("Unable to obtain valid preferences scope");
}
IEclipsePreferences node = scope.getNode(languageName);
if (node == null) {
throw new IllegalStateException("Unable to obtain valid preferences node");
}
return node;
}
/*
* Clear preferences at a given level
*/
/**
* Clear all of the preferences at a given level.
*
* @return the (cleared) preferences node for the given level
*/
public IEclipsePreferences clearPreferencesAtLevel(String level) {
return clearPreferencesAtLevel(project, level);
}
/**
* Clear all of the preferences at a given level, considering the
* given project if the level is the project level.
*
* @return the (cleared) preferences node for the given level
*/
public IEclipsePreferences clearPreferencesAtLevel(IProject project, String level) {
if (level == null) {
throw new IllegalArgumentException(
"PreferencesService.clearPreferencesAtLevel (with project): given level is null");
}
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException(
"PreferencesService.clearPreferencesAtLevel (with project): given level '" + level + "' is not a valid level");
}
if (project == null && level.equals(PROJECT_LEVEL)) {
//throw new IllegalStateException(
// "PreferencesService.clearPreferencesAtLevel (with project): given project is null when given level is 'project'");
return null;
}
IEclipsePreferences node = null;
try {
if (level.equals(PROJECT_LEVEL) && projectScope != null) {
node = projectScope.getNode(languageName);
} else if (level.equals(INSTANCE_LEVEL)) {
node = instanceScope.getNode(languageName);
} else if (level.equals(CONFIGURATION_LEVEL)) {
node = configurationScope.getNode(languageName);
} else if (level.equals(DEFAULT_LEVEL)) {
node = defaultScope.getNode(languageName);
}
if (node != null) {
//node.clear();
String[] keys = node.keys();
for (int i = 0; i < keys.length; i++) {
node.remove(keys[i]);
}
node.flush(); // SMS 28 Nov 2006
}
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("BackingStoreException clearing existing preferences; attempting to add new preferences anyway", e);
}
return node;
}
/**
* Clear the specific preference associated with a given key at
* a given level.
*
* @return the cleared value (may be null)
*/
public String clearPreferenceAtLevel(String level, String key) {
return clearPreferenceAtLevel(project, level, key);
}
/**
* Clear the specific preference associated with a given key at
* a given level. Consider the given project if the given level
* is the project level.
*
* @return the cleared value (may be null)
*/
public String clearPreferenceAtLevel(IProject project, String level, String key) {
if (level == null) {
throw new IllegalArgumentException(
"PreferencesService.clearPreferenceAtLevel (with project): given level is null");
}
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException(
"PreferencesService.clearPreferenceAtLevel (with project): given level '" + level + "' is not a valid level");
}
if (project == null && level.equals(PROJECT_LEVEL)) {
throw new IllegalStateException(
"PreferencesService.clearPreferenceAtLevel (with project): given project is null when given level is 'project'");
}
IScopeContext context = null;
if (level.equals(PROJECT_LEVEL)) {
context = getScopeForProject(project);
} else {
context = getScopeForLevel(level);
}
IEclipsePreferences node = context.getNode(languageName);
if (node == null) return null;
String preference = node.get(key, null);
node.remove(key);
try {
node.flush();
} catch (BackingStoreException e) {
RuntimePlugin.getInstance().logException("BackingStoreException trying to flush node with cleared preference;\n" +
"\tproject = " + project + "; level = " + level + "; key = " + key + "\n" +
"\tclearing may not have a persistent effect", e);
}
return preference;
}
/*
* Other useful things
*/
/**
* The standard search order of preference levels in the IMP preferences
* service. This is the default search order in the Eclipse PreferencesService
* and it is presumed not to change during the operation of the IMP service.
*
*/
public final static String[] levels = { PROJECT_LEVEL, INSTANCE_LEVEL, CONFIGURATION_LEVEL, DEFAULT_LEVEL };
public int indexForLevel(String levelName) {
for (int i = 0; i < levels.length; i++) {
if (levels[i].equals(levelName))
return i;
}
return -1;
}
/**
* Returns the first preference level in the standard search order
* at which the given key is defined for the current language.
* Returns null if the given key is not defined.
*/
public String getApplicableLevel(String key, String level) {
return getApplicableLevel(project, key, level);
}
/**
* Returns the first preference level in the standard search order
* at which the given key is defined for the current language.
* Returns null if the given key is not defined.
*/
public String getApplicableLevel(IProject project, String key, String level) {
// Level == null okay, as we can start at the bottom in that case
if (level != null && !isaPreferencesLevel(level)) {
throw new IllegalArgumentException("PreferencesService.getApplicableLevel (with project): given level '" + level + "' is not a real level");
}
if (key == null) {
throw new IllegalArgumentException("PreferencesService.getApplicableLevel (with project): given key is null");
}
if (level != null && level.equals(PROJECT_LEVEL) && projectScope == null) {
throw new IllegalStateException("PreferencesService.getApplicableLevel (with project): node for project requested when project scope is null");
}
int startIndex = level == null ? (project != null ? PROJECT_INDEX : INSTANCE_INDEX) : getIndexForLevel(level);
if (startIndex == PROJECT_INDEX) {
IScopeContext context = getScopeForProject(project);
IEclipsePreferences node = context.getNode(languageName);
String[] keys;
try {
keys = node.keys();
} catch (BackingStoreException e) {
keys = new String[0];
}
for (int i = 0; i < keys.length; i++) {
if (keys[i].equals(key)) return PROJECT_LEVEL;
}
startIndex++;
}
for (int i = startIndex; i < levels.length; i++) {
// Since all preference values are stored as strings,
// regardless of attributed base type, I believe that
// all can be safely retrieved as strings, so long as
// we don't care about their actual values
String pref = getStringPreference(levels[i], key);
if (pref == null) continue;
return levels[i];
}
return null;
}
public boolean isDefault(String key, String level) {
return isDefault(project, key, level);
}
public boolean isDefault(IProject project, String key, String level) {
return DEFAULT_LEVEL.equals(getApplicableLevel(project, key, level));
}
public boolean isDefined(String key) {
return isDefined(project, key);
}
public boolean isDefined(IProject project, String key) {
return getApplicableLevel(project, key, null) != null;
}
public boolean isaPreferencesLevel(String possibleLevel) {
for (int i = 0; i < levels.length; i++) {
if (levels[i].equals(possibleLevel)) return true;
}
return false;
}
public IEclipsePreferences getRootNode() {
return preferencesService.getRootNode();
}
public IEclipsePreferences getNodeForLevel(String level) {
if (level == null) {
throw new IllegalArgumentException("PreferencesService.getNodeForLevel: given level is null");
}
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException("PreferencesService.getNodeForLevel: given level '" + level + "' is not a real level");
}
if (level.equals(PROJECT_LEVEL) && projectScope == null) {
throw new IllegalStateException("PreferencesService.getNodeForLevel: node for project requested when project scope is null");
}
IEclipsePreferences node = null;
if (level.equals(PROJECT_LEVEL)) {
node = projectScope.getNode(languageName);
} else if (level.equals(INSTANCE_LEVEL)) {
node = instanceScope.getNode(languageName);
} else if (level.equals(CONFIGURATION_LEVEL)) {
node = configurationScope.getNode(languageName);
} else if (level.equals(DEFAULT_LEVEL)) {
node = defaultScope.getNode(languageName);
}
return node;
}
public IEclipsePreferences[] getNodesForLevels() {
if (languageName == null || languageName.equals("")) {
throw new IllegalStateException("PreferencesService.getNodesForLevels: language name is invalid (null or empty); no associated preferences nodes");
}
IEclipsePreferences[] nodes = new IEclipsePreferences[4];
if (projectScope != null)
nodes[0] = projectScope.getNode(languageName);
else
nodes[0] = null;
nodes[1] = instanceScope.getNode(languageName);
nodes[2] = configurationScope.getNode(languageName);
nodes[3] = defaultScope.getNode(languageName);
return nodes;
}
public IScopeContext getScopeForLevel(String level) {
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException("PreferencesService.getScopeForLevel: level = '" + level + "' is not a valid level");
}
//if (level.equals(PROJECT_LEVEL) && projectScope == null) {
// throw new IllegalStateException("PreferencesService.scopeForLevel: scope for project requested when project scope is null");
//}
IScopeContext result = null;
//if (!isaPreferencesLevel(level)) return result;
if (level.equals(PROJECT_LEVEL)) {
if (projectScope == null) { return null; }
else { return projectScope; }
} else if (level.equals(INSTANCE_LEVEL)) {
return instanceScope;
} else if (level.equals(CONFIGURATION_LEVEL)) {
return configurationScope;
} else if (level.equals(DEFAULT_LEVEL)) {
return defaultScope;
}
return result;
}
public IScopeContext getScopeForProject(IProject project) {
if (project == null) {
throw new IllegalArgumentException("PreferencesService.getScopeForProject: given project is null");
}
return new ProjectScope(project);
}
public IEclipsePreferences[] getNodesForLevels(IProject project) {
if (project == null) {
throw new IllegalArgumentException("PreferencesService.getNodesForLevels: given project is null");
}
IEclipsePreferences[] nodes = new IEclipsePreferences[4];
nodes[0] = new ProjectScope(project).getNode(languageName);
nodes[1] = instanceScope.getNode(languageName);
nodes[2] = configurationScope.getNode(languageName);
nodes[3] = defaultScope.getNode(languageName);
return nodes;
}
public IEclipsePreferences getNodeForProject(IProject project) {
if (project == null) {
throw new IllegalArgumentException("PreferencesService.getNodeForProject: given project is null");
}
return new ProjectScope(project).getNode(languageName);
}
public int getIndexForLevel(String level) {
// Special case, e.g., for "applicable" preferences
if (level == null) return 0;
if (!isaPreferencesLevel(level)) {
throw new IllegalArgumentException("PreferencesService.getIndexForLevel: level = '" + level + "' is not a valid level");
}
// Maybe better let people get the index for the project level
// even if there is not project assigned (index considered not harmful)
//if (level.equals(PROJECT_LEVEL) && projectScope == null) {
// throw new IllegalStateException("PreferencesService.getIndexForLevel: scope for project requested when project scope is null");
//}
if (level.equals(PROJECT_LEVEL))return 0;
else if (level.equals(INSTANCE_LEVEL)) return 1;
else if (level.equals(CONFIGURATION_LEVEL)) return 2;
else if (level.equals(DEFAULT_LEVEL)) return 3;
throw new IllegalStateException("PreferencesService.getIndexForLevel: found no index to return for level = " + level);
}
/*
* For listeners
*/
private List<IProjectSelectionListener> projectSelectionListeners = new ArrayList<IProjectSelectionListener>();
public void addProjectSelectionListener(IProjectSelectionListener listener) {
projectSelectionListeners.add(listener);
}
public void removeProjectSelectionListener(IProjectSelectionListener listener) {
projectSelectionListeners.remove(listener);
}
protected void fireProjectSelectionEvent(final ProjectSelectionEvent event) {
if (projectSelectionListeners == null || projectSelectionListeners.size() == 0)
return;
//Object[] listeners = projectSelectionListeners.toArray();
for (int i = 0; i < projectSelectionListeners.size(); i++) {
final IProjectSelectionListener listener = projectSelectionListeners.get(i);
ISafeRunnable job = new ISafeRunnable() {
public void handleException(Throwable exception) {
// already logged in Platform#run()
}
public void run() throws Exception {
listener.selection(event);
}
};
Platform.run(job);
}
}
}