/******************************************************************************* * Copyright (c) 2015, 2016 Ericsson and others. * 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: * Marc Dumais (Ericsson) - initial API and implementation (bug 460837) * Marc Dumais (Ericsson) - Bug 462353 *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.utils; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.dsf.gdb.multicorevisualizer.internal.ui.MulticoreVisualizerUIPlugin; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.osgi.service.prefs.BackingStoreException; /** * This class manages one or more PersistentParameter, PersistentListParameter, * PersistentMapParameter objects, using a common name-space and optionally an * instance id so that multiple instances can each have their own version of * the parameter persisted */ public class PersistentSettingsManager { /** Base class for a persistent parameter */ private abstract class AbstractPersistentParameter<T> { protected final Class<T> myClazz; protected final boolean m_perInstance; protected final String m_storeKey; /** Constructor */ public AbstractPersistentParameter(Class<T> clazz, boolean perInstance, String storeKey) { myClazz = clazz; m_perInstance = perInstance; m_storeKey = storeKey; } // accessors /** Returns whether this parameter is persisted independently for each client instance */ public boolean isPerInstance() { return m_perInstance; } /** Returns the class of the parameter */ public Class<T> getClazz() { return myClazz; } protected String getStoreKey() { return m_storeKey; } // misc @SuppressWarnings("unchecked") /** Converts a value from a String to its expected generic type. This is a base * implementation that converts some base types - Use/Override as needed for more complex * types, such as List or Map of these types */ protected T convertToT(String val) { // TODO: Add other types? Float, etc if (String.class.isAssignableFrom(getClazz())) { return (T) val; } else if (Integer.class.isAssignableFrom(getClazz())) { return (T) Integer.valueOf(val); } else if (Boolean.class.isAssignableFrom(getClazz())) { return (T) Boolean.valueOf(val); } return null; } /** Returns whether the wanted Class type is supported, to use as a persistent parameter */ protected boolean isTypeSupported(Class<T> clazz) { // TODO: Add other types? Float, etc if (String.class.isAssignableFrom(clazz) || Integer.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz)) { return true; } return false; } // TODO: add a way to notify clients that the value of a global (shared) parameter // has been updated, and that they should re-read it. } /** Class for a persistent parameter */ public class PersistentParameter<T> extends AbstractPersistentParameter<T> { private T m_value; private T m_defaultValue; /** * Constructor * @param name: unique (for the namespace) label that identifies this parameter * @param perInstance: whether the parameter's value is persisted per client instance or * globally (one common shared stored value for all instances) * @param storeKey : The key used to store the parameter in the store */ public PersistentParameter(Class<T> clazz, boolean perInstance, String storeKey) { super(clazz, perInstance, storeKey); } /** Sets the default value to use if no persistent * value is found for this parameter */ public void setDefault(T defaultValue) { m_defaultValue = defaultValue; } /** Sets the value to persist */ public void set(T value) { m_value = value; // save value in preference store persistParameter(value); } /** Returns the persistent value, if found, else the default value */ public T value() { if (m_value == null) { // attempt to get the value from the preference store m_value = restoreParameter(); } // parameter has one value for any/all instances else if(!isPerInstance()) { // do not rely on cached value, since another instance might have // changed it - reread from data store m_value = restoreParameter(); } return (m_value == null)? m_defaultValue : m_value; } /** * Gets the persistent value, optionally forcing re-reading stored value * @param forceRefresh whether to force to re-read memento in case value changed */ public T value(boolean forceRefresh) { if (forceRefresh) { m_value = null; } return value(); } /** Attempts to find the parameter in the preference store. Returns null if not found */ private T restoreParameter() { IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore(); String memento = store.get(getStoreKey(), null); if (memento == null) return null; String val = MementoUtils.decodeStringFromMemento(memento); T convertedVal = convertToT(val); return convertedVal; } /** Saves parameter's value in preference store */ private void persistParameter(T value) { // create memento String memento = MementoUtils.encodeStringIntoMemento(value.toString()); // save memento in store if (memento != null) { IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore(); store.put(getStoreKey(), memento); try { store.flush(); } catch (BackingStoreException e) { e.printStackTrace(); } } } } /** Class for a persistent {@literal List<T>} parameter */ public class PersistentListParameter<T> extends AbstractPersistentParameter<T> { private List<T> m_value; private List<T> m_defaultValue; public PersistentListParameter(Class<T> clazz, boolean perInstance, String storeKey) { super(clazz, perInstance, storeKey); } /** Sets the default value to use if no persistent * value is found for this parameter */ public void setDefault(List<T> defaultValues) { m_defaultValue = defaultValues; } /** Sets the value to persist */ public void set(List<T> values) { m_value = values; // save value in preference store persistParameter(values); } /** Returns the persistent value, if found, else the default value */ public List<T> value() { if (m_value == null) { // attempt to get the value from the preference store m_value = restoreParameter(); } // parameter has one value for any/all instances else if(!isPerInstance()) { // do not rely on cached value, since another instance might have // changed it - reread from data store m_value = restoreParameter(); } return (m_value == null)? m_defaultValue : m_value ; } /** * Gets the persistent value, optionally forcing re-reading stored value * @param forceRefresh whether to force to re-read memento in case value changed */ public List<T> value(boolean forceRefresh) { if (forceRefresh) { m_value = null; } return value(); } /** Attempts to find the parameter in the preference store. Returns null if not found */ private List<T> restoreParameter() { IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore(); String memento = store.get(getStoreKey(), null); if (memento == null) return null; List<String> vals = MementoUtils.decodeListFromMemento(memento); // convert from List<String> to List<T> List<T> convertedVal = convertToT(vals); return convertedVal; } /** Saves parameter's value in preference store */ private void persistParameter(List<T> values) { // Convert List<T> to List<String> List<String> strList = convertTListToStringList(values); // create memento from List<String> String memento = MementoUtils.encodeListIntoMemento(strList); // save memento in store if (memento != null) { IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore(); store.put(getStoreKey(), memento); try { store.flush(); } catch (BackingStoreException e) { e.printStackTrace(); } } } /** For list parameters, converts the restored values from String * to its expected generic type */ private List<T> convertToT(List<String> vals) { List<T> convertedList = new ArrayList<>(); for(String val : vals) { convertedList.add(convertToT(val)); } return convertedList; } /** Converts a list of generic type to a list of String */ private List<String> convertTListToStringList(List<T> tList) { List<String> strList = new ArrayList<>(); // convert list to list of String for(T elem : tList) { strList.add(elem.toString()); } return strList; } } /** Class for a persistent {@literal Map<String,T>} parameter */ public class PersistentMapParameter<T> extends AbstractPersistentParameter<T> { private Map<String,T> m_value; private Map<String,T> m_defaultValue; public PersistentMapParameter(Class<T> clazz, boolean perInstance, String storeKey) { super(clazz, perInstance, storeKey); } /** Sets the default value to use if no persistent * value is found for this parameter */ public void setDefault(Map<String,T> defaultValues) { m_defaultValue = defaultValues; } /** Sets the value to persist */ public void set(Map<String,T> values) { m_value = values; // save value in preference store persistParameter(values); } /** Returns the persistent value, if found, else the default value */ public Map<String,T> value() { if (m_value == null) { // attempt to get the value from the preference store m_value = restoreParameter(); } // parameter has one value for any/all instances else if(!isPerInstance()) { // do not rely on cached value, since another instance might have // changed it - reread from data store m_value = restoreParameter(); } return (m_value == null)? m_defaultValue : m_value ; } /** * Gets the persistent value, optionally forcing re-reading stored value * @param forceRefresh whether to force to re-read memento in case value changed */ public Map<String,T> value(boolean forceRefresh) { if (forceRefresh) { m_value = null; } return value(); } /** Attempts to find the parameter in the preference store. Returns null if not found */ private Map<String,T> restoreParameter() { IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore(); String memento = store.get(getStoreKey(), null); if (memento == null) return null; Map<String,String> vals = MementoUtils.decodeMapFromMemento(memento); // convert from Map<String,String> to Map<String,T> Map<String,T> convertedVal = convertToT(vals); return convertedVal; } /** Saves parameter's value in preference store */ private void persistParameter(Map<String,T> values) { // Convert Map<String,T> to Map<String,String> Map<String,String> strMap = convertTMapToStringMap(values); // create memento from Map String memento = MementoUtils.encodeMapIntoMemento(strMap); // save memento in store if (memento != null) { IEclipsePreferences store = MulticoreVisualizerUIPlugin.getEclipsePreferenceStore(); store.put(getStoreKey(), memento); try { store.flush(); } catch (BackingStoreException e) { e.printStackTrace(); } } } /** For Map parameters, converts the restored values from {@literal Map<String,String>} * to {@literal Map<String, T>} */ private Map<String,T> convertToT(Map<String,String> vals) { Map<String,T> convertedMap = new HashMap<>(); for(String key : vals.keySet()) { convertedMap.put(key, convertToT(vals.get(key))); } return convertedMap; } /** Converts a {@literal Map<String,T>} to a {@literal Map<String,String>} */ private Map<String,String> convertTMapToStringMap(Map<String,T> map) { Map<String,String> strMap = new HashMap<>(); // convert each entry for(String key : map.keySet()) { strMap.put(key, map.get(key).toString()); } return strMap; } } /** String that is used to insulate the namespace for the parameters * saved by a specific instance of the class */ private final String m_category; /** an identifier that differentiates different client instances. For example, to save the * value of a parameter that is applicable per-view, the view secondary id could be used so * that each view has its own stored value */ private final String m_instance; /** * Constructor * @param category : an optional id that is used to insulate the namespace for the parameters * saved by this instance of the class. Using different category values permits to distinguish * two or more parameters with the same label. Example: class name where the parameter is used. * This can be set to null if unused. * @param instance : a unique id that identifies the client's instance. Used when * a parameter is defined as per-instance */ public PersistentSettingsManager(String category, String instance) { m_category = category != null ? category : ""; //$NON-NLS-1$ m_instance = instance != null ? instance : ""; //$NON-NLS-1$ } /** Constructor * @param instance: a unique id that identifies the client's instance. Used when * a parameter is not global (i.e. meant to be persisted per instance). */ public PersistentSettingsManager(String instance) { this(null, instance); } /** Constructor */ public PersistentSettingsManager() { this(null, null); } /** * Creates a new persistent parameter, using the namespace and instance id of this manager. * @param clazz: the class of the persistent parameter. Supported types: String, Integer, Boolean * @param label: unique label that identifies this parameter. * @param perInstance: whether the parameter's value should be persisted per client instance or * globally (one common shared stored value for all instances) * @param defaultValue: default value to use (mandatory) */ public <T> PersistentParameter<T> getNewParameter(Class<T> clazz, String label, boolean perInstance, T defaultValue) { PersistentParameter<T> setting; // build the final store key with category, parameter label and specific instance, if applicable setting = new PersistentParameter<T>(clazz, perInstance, getStorageKey(perInstance) + "." + label); //$NON-NLS-1$ // check that we're dealing with one of a few supported types if (setting.isTypeSupported(clazz)) { setting.setDefault(defaultValue); return setting; } else { throw new InvalidParameterException("Unsupported class type: " + clazz.toString()); //$NON-NLS-1$ } } /** * Creates a new persistent {@literal List<T>} parameter, using the namespace and instance id of this manager. * @param clazz: the class of the persistent parameter List (e.g. List of that type). Supported types: String, Integer, Boolean * @param label: unique label that identifies this parameter. * @param perInstance: whether the parameter's value should be persisted per client instance or * globally (one common shared stored value for all instances) * @param defaultValue: default value to use (mandatory). */ public <T> PersistentListParameter<T> getNewListParameter(Class<T> clazz, String label, boolean perInstance, List<T> defaultValue) { PersistentListParameter<T> setting; // build the final store key with category, parameter label and specific instance, if applicable setting = new PersistentListParameter<T>(clazz, perInstance, getStorageKey(perInstance) + "." + label); //$NON-NLS-1$ // check that we're dealing with one of a few supported types if (setting.isTypeSupported(clazz)) { setting.setDefault(defaultValue); return setting; } else { throw new InvalidParameterException("Unsupported class type: " + clazz.toString()); //$NON-NLS-1$ } } /** * Creates a new persistent {@literal Map<String,T>} parameter, using the namespace and instance id of this manager. * @param clazz: the class of the persistent parameter List (e.g. List of that type). Supported types: String, Integer, Boolean * @param label: unique label that identifies this parameter. * @param perInstance: whether the parameter's value should be persisted per client instance or * globally (one common shared stored value for all instances) * @param defaultValue: default value to use (mandatory). */ public <T> PersistentMapParameter<T> getNewMapParameter(Class<T> clazz, String label, boolean perInstance, Map<String,T> defaultValue) { PersistentMapParameter<T> setting; // build the final store key with category, parameter label and specific instance, if applicable setting = new PersistentMapParameter<T>(clazz, perInstance, getStorageKey(perInstance) + "." + label); //$NON-NLS-1$ // check that we're dealing with one of a few supported types if (setting.isTypeSupported(clazz)) { setting.setDefault(defaultValue); return setting; } else { throw new InvalidParameterException("Unsupported class type: " + clazz.toString()); //$NON-NLS-1$ } } // ---- misc ---- /** Returns the key to be used to save parameter, taking into account the * instance id, if applicable */ private String getStorageKey(boolean perInstance) { return (perInstance ? m_instance : "") + (!m_category.isEmpty() ? "." + m_category : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } }