/* * Process Hacker - * settings base class * * Copyright (C) 2009 wj32 * * This file is part of Process Hacker. * * Process Hacker 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. * * Process Hacker 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 Process Hacker. If not, see . */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; namespace ProcessHacker.Common.Settings { /// /// Provides the base class used to support settings. /// public abstract class SettingsBase { private ISettingsStore _store; private Dictionary _settings = new Dictionary(); private Dictionary _modifiedSettings = new Dictionary(); private Dictionary _defaultsCache = new Dictionary(); private Dictionary _typeCache = new Dictionary(); /// /// Creates a settings class with the specified storage provider. /// /// The storage provider. public SettingsBase(ISettingsStore store) { _store = store; } /// /// Gets or sets a setting. /// /// The name of the setting. /// The value of the setting. public object this[string name] { get { return this.GetValue(name); } set { this.SetValue(name, value); } } /// /// Gets the underlying storage for the settings class. /// public ISettingsStore Store { get { return _store; } } /// /// Converts a string to an instance of another type. /// /// The string to convert. /// The type of the output value. /// The converted value. protected virtual object ConvertFromString(string value, Type valueType) { if (valueType.IsPrimitive) return Convert.ChangeType(value, valueType); else if (valueType == typeof(string)) return value; var converter = TypeDescriptor.GetConverter(valueType); // Since all types can convert to System.String, we also need to make sure they can // convert from System.String. if (converter.CanConvertFrom(typeof(string)) && converter.CanConvertTo(typeof(string))) return converter.ConvertFromInvariantString(value); throw new InvalidOperationException("The setting '" + value + "' has an unsupported type."); } /// /// Converts an object to a string. /// /// The object to convert. /// The type of the input value. /// The string representation of the object. protected virtual string ConvertToString(object value, Type valueType) { if (valueType.IsPrimitive) return (string)Convert.ChangeType(value, typeof(string)); else if (valueType == typeof(string)) return (string)value; var converter = TypeDescriptor.GetConverter(valueType); // Since all types can convert to System.String, we also need to make sure they can // convert from System.String. if (converter.CanConvertFrom(typeof(string)) && converter.CanConvertTo(typeof(string))) return converter.ConvertToInvariantString(value); throw new InvalidOperationException("The setting '" + value + "' has an unsupported type."); } /// /// Gets the default value of a setting. /// /// The name of the setting. /// A string representation of the setting's default value. private string GetSettingDefault(string name) { lock (_defaultsCache) { if (!_defaultsCache.ContainsKey(name)) { var property = this.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public); var attributes = property.GetCustomAttributes(typeof(SettingDefaultAttribute), true); if (attributes.Length == 1) { _defaultsCache.Add(name, (attributes[0] as SettingDefaultAttribute).Value); } else { _defaultsCache.Add(name, null); } } return _defaultsCache[name]; } } /// /// Gets the type of a setting. /// /// The name of the setting. /// The type of the setting. private Type GetSettingType(string name) { lock (_typeCache) { if (!_typeCache.ContainsKey(name)) { var property = this.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public); _typeCache.Add(name, property.PropertyType); } return _typeCache[name]; } } /// /// Gets the value of a setting. /// /// The name of the setting. /// The value of the setting. private object GetValue(string name) { object value; string settingValue; Type settingType; lock (_modifiedSettings) { if (_modifiedSettings.ContainsKey(name)) return _modifiedSettings[name]; } lock (_settings) { if (_settings.ContainsKey(name)) return _settings[name]; } settingValue = _store.GetValue(name); if (settingValue == null) settingValue = this.GetSettingDefault(name); if (settingValue == null) settingValue = ""; settingType = this.GetSettingType(name); try { value = this.ConvertFromString(settingValue, settingType); } catch { // The stored value must be invalid. Return the default value. value = this.ConvertFromString(this.GetSettingDefault(name), settingType); } lock (_settings) { if (!_settings.ContainsKey(name)) _settings.Add(name, value); } return value; } /// /// Causes all cached setting values to be invalidated. /// public virtual void Invalidate() { } /// /// Discards any unsaved changes made to settings. /// public void Reload() { lock (_modifiedSettings) _modifiedSettings.Clear(); lock (_settings) _settings.Clear(); this.Invalidate(); } /// /// Resets all settings to their default values. /// public void Reset() { lock (_modifiedSettings) _modifiedSettings.Clear(); lock (_settings) _settings.Clear(); _store.Reset(); this.Invalidate(); } /// /// Saves settings to persistent storage. /// public void Save() { lock (_modifiedSettings) { foreach (var pair in _modifiedSettings) { _store.SetValue(pair.Key, this.ConvertToString(pair.Value, this.GetSettingType(pair.Key))); } _modifiedSettings.Clear(); } _store.Flush(); } /// /// Sets the value of a setting. /// /// The name of the setting. /// The new value of the setting. private void SetValue(string name, object value) { lock (_modifiedSettings) { if (_modifiedSettings.ContainsKey(name)) _modifiedSettings[name] = value; else _modifiedSettings.Add(name, value); } lock (_settings) { if (_settings.ContainsKey(name)) _settings[name] = value; } } } }