/**
* Copyright (C) 2005 - 2011 Eric Van Dewoestine
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.eclim.plugin.core.preference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.eclim.Services;
import org.eclim.logging.Logger;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
/**
* Class for handling preferences for eclim.
*
* This class uses the word 'option' for a built in eclipse options (like the
* jdt compiler source version), and the word 'preference' for eclim provided
* key values.
*
* @author Eric Van Dewoestine
*/
public class Preferences
{
private static final Logger logger = Logger.getLogger(Preferences.class);
public static final String USERNAME_PREFERENCE = "org.eclim.user.name";
public static final String USEREMAIL_PREFERENCE = "org.eclim.user.email";
public static final String PROJECT_COPYRIGHT_PREFERENCE =
"org.eclim.project.copyright";
private static final String NODE_NAME = "org.eclim";
private static final String CORE = "core";
private static Preferences instance = new Preferences();
private static Map<String, OptionHandler> optionHandlers =
new HashMap<String, OptionHandler>();
private Map<String, Preference> preferences = new HashMap<String, Preference>();
private Map<String, Option> options = new HashMap<String, Option>();
// cache preference values
private Map<String, Map<String, String>> preferenceValues =
new HashMap<String, Map<String, String>>();
// cache option values
private Map<String, Map<String, String>> optionValues =
new HashMap<String, Map<String, String>>();
private Preferences() {}
/**
* Gets the Preferences instance.
*
* @return The Preferences singleton.
*/
public static Preferences getInstance()
{
return instance;
}
/**
* Adds the supplied OptionHandler to manage eclipse options with the
* specified prefix.
*
* @param prefix The prefix.
* @param handler The OptionHandler.
* @return The OptionHandler.
*/
public static OptionHandler addOptionHandler(
String prefix, OptionHandler handler)
{
optionHandlers.put(prefix, handler);
return handler;
}
/**
* Adds an eclim preference to be made available.
*
* @param preference The preference to add.
*/
public void addPreference(Preference preference)
{
preferences.put(preference.getName(), preference);
preferenceValues.clear();
}
/**
* Gets an array of configured preference names.
*
* @return Array of preference names.
*/
public String[] getPreferenceNames()
{
return preferences.keySet().toArray(new String[0]);
}
/**
* Adds an eclipse option to be configurable via eclim.
*
* @param option The option.
*/
public void addOption(Option option)
{
options.put(option.getName(), option);
optionValues.clear();
}
/**
* Gets an array of configured option names.
*
* @return Array of option names.
*/
public String[] getOptionNames()
{
return options.keySet().toArray(new String[0]);
}
/**
* Clear cached option/preference values.
*
* @param project The project.
*/
public void clearProjectValueCache(IProject project)
{
preferenceValues.remove(project.getName());
optionValues.remove(project.getName());
}
/**
* Gets a map of all options/preferences.
*
* @param project The current project.
* @return A map of key values.
*/
public Map<String, String> getValues(IProject project)
throws Exception
{
// eclim preferences
Map<String, String> prefVals = preferenceValues.get(project.getName());
if(prefVals == null){
prefVals = new HashMap<String, String>();
preferenceValues.put(project.getName(), prefVals);
IScopeContext context = InstanceScope.INSTANCE;
// global
IEclipsePreferences globalPrefs = context.getNode(NODE_NAME);
initializeDefaultPreferences(globalPrefs);
for(String key : globalPrefs.keys()){
prefVals.put(key, globalPrefs.get(key, null));
}
context = new ProjectScope(project);
// project
IEclipsePreferences projectPrefs = context.getNode(NODE_NAME);
for(String key : projectPrefs.keys()){
prefVals.put(key, projectPrefs.get(key, null));
}
}
// eclipse option
Map<String, String> optVals = optionValues.get(project.getName());
if(optVals == null){
optVals = new HashMap<String, String>();
optionValues.put(project.getName(), optVals);
for(OptionHandler handler : optionHandlers.values()){
String nature = handler.getNature();
if(CORE.equals(nature) || project.getNature(nature) != null){
optVals.putAll(handler.getValues(project));
}
}
}
Map<String, String> all =
new HashMap<String, String>(preferenceValues.size() + optionValues.size());
all.putAll(optVals);
all.putAll(prefVals);
return all;
}
/**
* Gets the value of a project option/preference.
*
* @param project The project.
* @param name The name of the option/preference.
* @return The value or null if not found.
*/
public String getValue(IProject project, String name)
throws Exception
{
return getValues(project).get(name);
}
/**
* Gets the global Option/Preference objects.
*
* @return Array of Option.
*/
public Option[] getOptions()
throws Exception
{
return getOptions(null);
}
/**
* Gets the Option/Preference objects.
*
* @param project The project scope or null for global.
* @return Array of Option.
*/
public Option[] getOptions(IProject project)
throws Exception
{
ArrayList<OptionInstance> results = new ArrayList<OptionInstance>();
Map<String, String> options = new HashMap<String, String>();
// global
IScopeContext context = InstanceScope.INSTANCE;
IEclipsePreferences globalPrefs = context.getNode(NODE_NAME);
initializeDefaultPreferences(globalPrefs);
for(String key : globalPrefs.keys()){
options.put(key, globalPrefs.get(key, null));
}
// project
if (project != null){
context = new ProjectScope(project);
IEclipsePreferences projectPrefs = context.getNode(NODE_NAME);
for(String key : projectPrefs.keys()){
options.put(key, projectPrefs.get(key, null));
}
}
for(OptionHandler handler : optionHandlers.values()){
String nature = handler.getNature();
if (CORE.equals(nature) ||
project == null ||
project.getNature(nature) != null)
{
Map<String, String> ops = project == null ?
handler.getValues() : handler.getValues(project);
if (ops != null){
options.putAll(ops);
}
}
}
for(String key : options.keySet()){
String value = options.get(key);
Option option = this.options.get(key);
if(option == null){
option = this.preferences.get(key);
}
if(option != null && value != null){
String nature = option.getNature();
if (CORE.equals(nature) ||
project == null ||
project.getNature(nature) != null)
{
OptionInstance instance = new OptionInstance(option, value);
results.add(instance);
}
}
}
return results.toArray(new Option[results.size()]);
}
/**
* Sets the supplied option/preference value.
*
* @param name The option/preference name.
* @param value The option/preference value.
*/
public void setValue(String name, String value)
throws Exception
{
setValue(null, name, value);
}
/**
* Set the supplied value.
*
* @param project The project to set the value for or null for global.
* @param name The name of the option/preference.
* @param value The value of the option/preference.
*/
public void setValue(IProject project, String name, String value)
throws IllegalArgumentException, Exception
{
if(name.startsWith(NODE_NAME)){
setPreference(NODE_NAME, project, name, value);
}else{
validateValue(options.get(name), name, value);
OptionHandler handler = null;
for(Object k : optionHandlers.keySet()){
String key = (String)k;
if(name.startsWith(key)){
handler = (OptionHandler)optionHandlers.get(key);
break;
}
}
if(handler != null){
if (project == null){
handler.setOption(name, value);
}else{
handler.setOption(project, name, value);
}
optionValues.clear();
}else{
logger.warn("No handler found for option '{}'", name);
}
}
}
/**
* Sets an eclim preference value.
*
* @param nodeName The name of the preferences node to write the preference
* to.
* @param project The project to set the value for or null to set globally.
* @param name The name of the preference.
* @param value The value of the preference.
*/
public void setPreference(
String nodeName, IProject project, String name, String value)
throws IllegalArgumentException, Exception
{
IScopeContext context = InstanceScope.INSTANCE;
IEclipsePreferences globalPrefs = context.getNode(nodeName);
initializeDefaultPreferences(globalPrefs);
Option pref = preferences.get(name);
if (pref == null){
pref = options.get(name);
}
// set global
if (project == null){
validateValue(pref, name, value);
globalPrefs.put(name, value);
globalPrefs.flush();
}else{
context = new ProjectScope(project);
IEclipsePreferences projectPrefs = context.getNode(nodeName);
// if project value is the same as the global, then remove it.
if(value.equals(globalPrefs.get(name, null))){
projectPrefs.remove(name);
projectPrefs.flush();
// if project value differs from global, then persist it.
}else{
validateValue(pref, name, value);
projectPrefs.put(name, value);
projectPrefs.flush();
}
}
preferenceValues.clear();
}
/**
* Initializes the default preferences.
* Note: should only be run against the global preferences (not project, etc.).
*
* @param preferences The eclipse preferences.
*/
private void initializeDefaultPreferences(IEclipsePreferences preferences)
throws Exception
{
String node = preferences.name();
for(Preference preference : this.preferences.values()){
String name = preference.getName();
if (name.startsWith(node) && preferences.get(name, null) == null){
preferences.put(preference.getName(), preference.getDefaultValue());
}
}
preferences.flush();
}
/**
* Validates that the supplied value is valid for the specified
* option/preference.
*
* @param option The option/preference instance.
* @param name The name of the option/preference.
* @param value The value of the option/preference.
*/
private void validateValue(Option option, String name, String value)
throws IllegalArgumentException
{
if(option != null){
Validator validator = option.getValidator();
if (validator == null || validator.isValid(value)){
return;
}
throw new IllegalArgumentException(
Services.getMessage("setting.invalid",
name, validator.getMessage(name, value)));
}
throw new IllegalArgumentException(
Services.getMessage("setting.not.found", name));
}
}