org.eclim.plugin.core.preference.Preferences.java Source code

Java tutorial

Introduction

Here is the source code for org.eclim.plugin.core.preference.Preferences.java

Source

/**
 * Copyright (C) 2005 - 2012  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.apache.commons.lang.ArrayUtils;

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;

import com.google.gson.Gson;

/**
 * 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 final String GLOBAL = "_global_";

    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.
     *
     * @return A map of key values.
     */
    public Map<String, String> getValues() throws Exception {
        return getValues(null);
    }

    /**
     * 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 {
        String cacheKey = project != null ? project.getName() : GLOBAL;

        // eclim preferences
        Map<String, String> prefVals = preferenceValues.get(cacheKey);
        if (prefVals == null) {
            prefVals = new HashMap<String, String>();
            preferenceValues.put(cacheKey, 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));
            }

            // project
            if (project != null) {
                context = new ProjectScope(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(cacheKey);
        if (optVals == null) {
            optVals = new HashMap<String, String>();
            optionValues.put(cacheKey, optVals);
            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) {
                        optVals.putAll(ops);
                    }
                }
            }
        }

        Map<String, String> all = new HashMap<String, String>(preferenceValues.size() + optionValues.size());
        all.putAll(optVals);
        all.putAll(prefVals);
        return all;
    }

    /**
     * Gets the value of an option/preference.
     *
     * @param name The name of the option/preference.
     * @return The value or null if not found.
     */
    public String getValue(String name) throws Exception {
        return getValues(null).get(name);
    }

    /**
     * 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 integer value of an option/preference.
     *
     * @param name The name of the option/preference.
     * @return The value or -1 if not found.
     */
    public int getIntValue(String name) throws Exception {
        return getIntValue(null, name);
    }

    /**
     * Gets the integer value of a project option/preference.
     *
     * @param project The project.
     * @param name The name of the option/preference.
     * @return The value or -1 if not found.
     */
    public int getIntValue(IProject project, String name) throws Exception {
        String value = getValues(project).get(name);
        return value != null ? Integer.parseInt(value) : -1;
    }

    /**
     * Gets the array value of an option/preference.
     *
     * @param name The name of the option/preference.
     * @return The array value or null if not found
     */
    public String[] getArrayValue(String name) throws Exception {
        return getArrayValue(null, name);
    }

    /**
     * Gets the array value of a project option/preference.
     *
     * @param project The project.
     * @param name The name of the option/preference.
     * @return The array value or and empty array if not found.
     */
    public String[] getArrayValue(IProject project, String name) throws Exception {
        String value = getValues(project).get(name);
        if (value != null && value.trim().length() != 0) {
            return new Gson().fromJson(value, String[].class);
        }
        return ArrayUtils.EMPTY_STRING_ARRAY;
    }

    /**
     * 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));
    }
}