com.mousefeed.eclipse.preferences.PreferenceAccessor.java Source code

Java tutorial

Introduction

Here is the source code for com.mousefeed.eclipse.preferences.PreferenceAccessor.java

Source

/*
 * Copyright (C) Heavy Lifting Software 2007, Robert Wloch 2012.
 *
 * This file is part of MouseFeed.
 *
 * MouseFeed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MouseFeed 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with MouseFeed.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.mousefeed.eclipse.preferences;

import static com.mousefeed.eclipse.preferences.PreferenceConstants.P_CONFIGURE_KEYBOARD_SHORTCUT_ENABLED;
import static com.mousefeed.eclipse.preferences.PreferenceConstants.P_CONFIGURE_KEYBOARD_SHORTCUT_THRESHOLD;
import static com.mousefeed.eclipse.preferences.PreferenceConstants.P_DEFAULT_ON_WRONG_INVOCATION_MODE;
import static com.mousefeed.eclipse.preferences.PreferenceConstants.P_INVOCATION_CONTROL_ENABLED;
import static com.mousefeed.eclipse.preferences.PreferenceConstants.P_SHOW_USED_KEYBOARD_SHORTCUT;
import static org.apache.commons.lang.Validate.notNull;

import com.mousefeed.client.OnWrongInvocationMode;
import com.mousefeed.eclipse.Activator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;

/**
 * Provides access to the plugin preferences.
 * Singleton.
 * 
 * @author Andriy Palamarchuk
 * @author Robert Wloch
 */
public class PreferenceAccessor {
    /**
     * Name of the file to store action-specific behavior when the actions
     * are invoked wrong way.
     */
    static final String ACTIONS_WRONG_INVOCATION_MODE_FILE = "actionsWrongInvocationMode.xml";

    /**
     * The singleton instance.
     */
    private static final PreferenceAccessor INSTANCE = new PreferenceAccessor();

    /**
     * The root document for the action-specific wrong invocation mode
     * preferences.
     */
    private static final String TAG_ACTIONS_WRONG_INVOCATION_MODE = "actionsWrongInvocationMode";

    /**
     * The action tag name.
     */
    private static final String TAG_ACTION = "action";

    /**
     * The action id tag name.
     */
    private static final String TAG_ACTION_ID = "id";

    /**
     * The action label tag name.
     */
    private static final String TAG_ACTION_LABEL = "label";

    /**
     * The on wrong invocation mode handling approach.
     */
    private static final String TAG_ON_WRONG_INVOCATION_MODE = "onWrongInvocationMode";

    /**
     * Actions on wrong invocation mode settings. Keys - action ids, values -
     * the settings.   
     */
    private final Map<String, ActionOnWrongInvocationMode> actionsOnWrongMode = new HashMap<String, ActionOnWrongInvocationMode>();

    /**
     * Creates new preference accessor.
     * Because this class is a singleton, the constructor normally should not be
     * used. It is exposed for testing purposes only.
     */
    PreferenceAccessor() {
        loadActionsOnWrongInvocationMode();
    }

    /**
     * The singleton instance.
     * @return the singleton instance. Never <code>null</code>.
     */
    public static PreferenceAccessor getInstance() {
        return INSTANCE;
    }

    /**
     * Whether invocation control is enabled preference.
     * The preference indicates whether to help to learn the desired way to
     * invoke actions
     * @return current preference value whether invocation control is enabled.
     */
    public boolean isInvocationControlEnabled() {
        return getPreferenceStore().getBoolean(P_INVOCATION_CONTROL_ENABLED);
    }

    /**
     * @param invocationControlEnabled the new value for the setting returned by
     * {@link #isInvocationControlEnabled()}.
     * @see #isInvocationControlEnabled()
     */
    public void storeInvocationControlEnabled(final boolean invocationControlEnabled) {
        getPreferenceStore().setValue(P_INVOCATION_CONTROL_ENABLED, invocationControlEnabled);
    }

    /**
     * Whether keyboard shortcut configuration is enabled preference.
     * The preference indicates whether to show the Keys preference page
     * for often used actions without a shortcut.
     * @return current preference value whether keyboard shortcut configuration
     * is enabled.
     */
    public boolean isConfigureKeyboardShortcutEnabled() {
        return getPreferenceStore().getBoolean(P_CONFIGURE_KEYBOARD_SHORTCUT_ENABLED);
    }

    /**
     * @param configureKeyboardShortcutEnabled the new value for the setting returned by
     * {@link #isConfigureKeyboardShortcutEnabled()}.
     * @see #isConfigureKeyboardShortcutEnabled()
     */
    public void storeConfigureKeyboardShortcutEnabled(final boolean configureKeyboardShortcutEnabled) {
        getPreferenceStore().setValue(P_CONFIGURE_KEYBOARD_SHORTCUT_ENABLED, configureKeyboardShortcutEnabled);
    }

    /**
     * The default preference of the threshold for the action invocation counter
     * above which keyboard shortcut configuration is enabled.
     * @return the global configure keyboard shortcut threshold preference.
     * @see PreferenceConstants#P_CONFIGURE_KEYBOARD_SHORTCUT_THRESHOLD
     */
    public int getConfigureKeyboardShortcutThreshold() {
        return getPreferenceStore().getInt(P_CONFIGURE_KEYBOARD_SHORTCUT_THRESHOLD);
    }

    /**
     * @param configureKeyboardShortcutThreshold the new value for the setting returned by
     * {@link #getConfigureKeyboardShortcutThreshold()}.
     * @see #getConfigureKeyboardShortcutThreshold()
     */
    public void storeConfigureKeyboardShortcutThreshold(final int configureKeyboardShortcutThreshold) {
        getPreferenceStore().setValue(P_CONFIGURE_KEYBOARD_SHORTCUT_THRESHOLD, configureKeyboardShortcutThreshold);
    }

    /**
     * Whether the used keyboard shortcut should be shown is enabled preference.
     * The preference indicates whether to show the used keyboard shortcut for
     * e.g. screencasts or coding dojos performed on a projector.
     * @return current preference value whether keyboard shortcut should be shown.
     */
    public boolean isShowUsedKeyboardShortcutEnabled() {
        return getPreferenceStore().getBoolean(P_SHOW_USED_KEYBOARD_SHORTCUT);
    }

    /**
     * @param showUsedKeyboardShortcutEnabled the new value for the setting returned by
     * {@link #isShowUsedKeyboardShortcutEnabled()}.
     * @see #isShowUsedKeyboardShortcutEnabled()
     */
    public void storeShowUsedKeyboardShortcutEnabled(final boolean showUsedKeyboardShortcutEnabled) {
        getPreferenceStore().setValue(P_SHOW_USED_KEYBOARD_SHORTCUT, showUsedKeyboardShortcutEnabled);
    }

    /**
     * The default preference what to do by default on wrong invocation mode.
     * @return the global invocation mode preference. Never <code>null</code>.
     * @see PreferenceConstants#P_DEFAULT_ON_WRONG_INVOCATION_MODE
     */
    public OnWrongInvocationMode getOnWrongInvocationMode() {
        final String stored = getPreferenceStore().getString(P_DEFAULT_ON_WRONG_INVOCATION_MODE);
        return stored == null ? OnWrongInvocationMode.DEFAULT : OnWrongInvocationMode.valueOf(stored);
    }

    /**
     * The preference what to do on wrong invocation mode for the specified
     * action.
     * @param actionId the id of the action get preferences for.
     * Not <code>null</code>.
     * @return the invocation mode preference.
     * <code>null</code> if there is no action-specific setting.
     * In this case use the default preference value.
     */
    public OnWrongInvocationMode getOnWrongInvocationMode(final String actionId) {
        notNull(actionId);
        final ActionOnWrongInvocationMode mode = actionsOnWrongMode.get(actionId);
        return mode == null ? null : mode.getOnWrongInvocationMode();
    }

    /**
     * Saves action-specific on wrong invocation mode settings.
     * @param settings the new value. Not <code>null</code>.
     */
    public void setOnWrongInvocationMode(final ActionOnWrongInvocationMode settings) {
        notNull(settings);
        actionsOnWrongMode.put(settings.getId(), settings);
        saveActionsOnWrongInvocationMode();
    }

    /**
     * Returns action-specific wrong invocation mode handling for all actions.
     * @return the action-specific settings. Read-only.
     * The collection nor its elements should not be changed.
     * Note, the collection and the objects in it should be cloned for using
     * for any significant period of time. 
     * Never <code>null</code>, can be empty if no action-specific settings were
     * defined.
     * All objects in the collection have unique ids.
     */
    public Collection<ActionOnWrongInvocationMode> getActionsOnWrongInvocationMode() {
        return actionsOnWrongMode.values();
    }

    /**
     * Replaces the action-specific settings.
     * @param settings the new settings. Not <code>null</code>.
     * Changes to the data passed to this method won't affect the values stored
     * in this class.
     * @see #getActionsOnWrongInvocationMode()
     */
    public void setActionsOnWrongInvocationMode(final Collection<ActionOnWrongInvocationMode> settings) {
        this.actionsOnWrongMode.clear();
        for (ActionOnWrongInvocationMode mode : settings) {
            final ActionOnWrongInvocationMode clone;
            try {
                clone = (ActionOnWrongInvocationMode) mode.clone();
            } catch (final CloneNotSupportedException e) {
                throw new RuntimeException();
            }
            this.actionsOnWrongMode.put(clone.getId(), clone);
        }
        saveActionsOnWrongInvocationMode();
    }

    /**
     * Removes action-specific on wrong invocation mode setting.
     * After calling this method when the action with the specified id is
     * handled using the default settings. 
     * @param actionId the action id. not <code>null</code>.
     */
    public void removeOnWrongInvocaitonMode(final String actionId) {
        notNull(actionId);
        actionsOnWrongMode.remove(actionId);
        saveActionsOnWrongInvocationMode();
    }

    /**
     * Stores the provided preference.
     * @param value the new value. Not <code>null</code>.
     */
    public void storeOnWrongInvocationMode(final OnWrongInvocationMode value) {
        notNull(value);
        getPreferenceStore().setValue(P_DEFAULT_ON_WRONG_INVOCATION_MODE, value.name());
    }

    /**
     * Loads preferences for the {@link #getOnWrongInvocationMode(String)}.
     */
    private void loadActionsOnWrongInvocationMode() {
        final File file = getActionsWrongInvocationModeFile();
        if (!file.exists() || file.length() == 0) {
            // the file not initialized yet
            return;
        }
        Reader reader = null;
        try {
            reader = new FileReader(file);
            final XMLMemento memento = XMLMemento.createReadRoot(reader);
            loadActionsOnWrongInvocationMode(memento);
        } catch (final FileNotFoundException ignore) {
            // the file does not exist yet 
        } catch (final WorkbenchException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Loads action-specific invocation handling.
     * @param memento the memento to load the data from.
     * Assumed not <code>null</code>.
     * @see #loadActionsOnWrongInvocationMode()
     */
    private void loadActionsOnWrongInvocationMode(final XMLMemento memento) {
        actionsOnWrongMode.clear();
        final IMemento[] children = memento.getChildren(TAG_ACTION);
        for (IMemento child : children) {
            final ActionOnWrongInvocationMode mode = new ActionOnWrongInvocationMode();
            mode.setLabel(child.getString(TAG_ACTION_LABEL));
            mode.setId(child.getString(TAG_ACTION_ID));
            mode.setOnWrongInvocationMode(
                    OnWrongInvocationMode.valueOf(child.getString(TAG_ON_WRONG_INVOCATION_MODE)));
            actionsOnWrongMode.put(mode.getId(), mode);
        }
    }

    /**
     * Saves settings loaded by {@link #loadActionsOnWrongInvocationMode()}.
     */
    private void saveActionsOnWrongInvocationMode() {
        final XMLMemento memento = createActionsOnWrongInvocationModeMemento();
        Writer writer = null;
        try {
            writer = new FileWriter(getActionsWrongInvocationModeFile());
            memento.save(writer);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Generates XML memento with the actions wrong invocation mode data. 
     * @return the memento. Never <code>null</code>.
     */
    private XMLMemento createActionsOnWrongInvocationModeMemento() {
        final XMLMemento memento = XMLMemento.createWriteRoot(TAG_ACTIONS_WRONG_INVOCATION_MODE);
        for (ActionOnWrongInvocationMode val : actionsOnWrongMode.values()) {
            final IMemento actionMemento = memento.createChild(TAG_ACTION);
            actionMemento.putString(TAG_ACTION_ID, val.getId());
            actionMemento.putString(TAG_ACTION_LABEL, val.getLabel());
            actionMemento.putString(TAG_ON_WRONG_INVOCATION_MODE, val.getOnWrongInvocationMode().name());
        }
        return memento;
    }

    /**
     * The plugin preference store.
     * @return never <code>null</code>.
     */
    private IPreferenceStore getPreferenceStore() {
        return Activator.getDefault().getPreferenceStore();
    }

    /**
     * File storing action-specific preferences for action invocation mode.
     * @return the file for action-specific preferences when the actions are
     * invoked with a wrong invocation mode.
     */
    File getActionsWrongInvocationModeFile() {
        if (Activator.getDefault() == null) {
            return new File("nonexisting");
        }
        return Activator.getDefault().getStateLocation().append(ACTIONS_WRONG_INVOCATION_MODE_FILE).toFile();
    }
}