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