using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.IO;
namespace SuperSocket.Common
{
///
/// Configuration extension class
///
public static class ConfigurationExtension
{
///
/// Gets the value from namevalue collection by key.
///
/// The collection.
/// The key.
///
public static string GetValue(this NameValueCollection collection, string key)
{
return GetValue(collection, key, string.Empty);
}
///
/// Gets the value from namevalue collection by key.
///
/// The collection.
/// The key.
/// The default value.
///
public static string GetValue(this NameValueCollection collection, string key, string defaultValue)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
if (collection == null)
return defaultValue;
var e = collection[key];
if (e == null)
return defaultValue;
return e;
}
///
/// Deserializes the specified configuration section.
///
/// The type of the element.
/// The section.
/// The reader.
public static void Deserialize(this TElement section, XmlReader reader)
where TElement : ConfigurationElement
{
if (section is ConfigurationElementCollection)
{
var collectionType = section.GetType();
var att = collectionType.GetCustomAttributes(typeof(ConfigurationCollectionAttribute), true).FirstOrDefault() as ConfigurationCollectionAttribute;
if (att != null)
{
var property = collectionType.GetProperty("AddElementName", BindingFlags.NonPublic | BindingFlags.Instance);
property.SetValue(section, att.AddItemName, null);
property = collectionType.GetProperty("RemoveElementName", BindingFlags.NonPublic | BindingFlags.Instance);
property.SetValue(section, att.RemoveItemName, null);
property = collectionType.GetProperty("ClearElementName", BindingFlags.NonPublic | BindingFlags.Instance);
property.SetValue(section, att.ClearItemsName, null);
}
}
var deserializeElementMethod = typeof(TElement).GetMethod("DeserializeElement", BindingFlags.NonPublic | BindingFlags.Instance);
deserializeElementMethod.Invoke(section, new object[] { reader, false });
}
///
/// Deserializes the child configuration.
///
/// The type of the configuration.
/// The child configuration string.
///
public static TConfig DeserializeChildConfig(string childConfig)
where TConfig : ConfigurationElement, new()
{
// removed extra namespace prefix
childConfig = childConfig.Replace("xmlns=\"http://schema.supersocket.net/supersocket\"", string.Empty);
XmlReader reader = new XmlTextReader(new StringReader(childConfig));
var config = new TConfig();
reader.Read();
config.Deserialize(reader);
return config;
}
///
/// Gets the child config.
///
/// The type of the config.
/// The child elements.
/// Name of the child config.
///
public static TConfig GetChildConfig(this NameValueCollection childElements, string childConfigName)
where TConfig : ConfigurationElement, new()
{
var childConfig = childElements.GetValue(childConfigName, string.Empty);
if (string.IsNullOrEmpty(childConfig))
return default(TConfig);
return DeserializeChildConfig(childConfig);
}
///
/// Gets the config source path.
///
/// The config.
///
public static string GetConfigSource(this ConfigurationElement config)
{
var source = config.ElementInformation.Source;
if (!string.IsNullOrEmpty(source) || !Platform.IsMono)
return source;
var configProperty = typeof(ConfigurationElement).GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic);
if (configProperty == null)
return string.Empty;
var configuration = (Configuration)configProperty.GetValue(config, null);
return configuration.FilePath;
}
///
/// Loads configuration element's node information from a model.
///
/// The config element.
/// The source.
/// Cannot find expected property 'Item' from the type 'ConfigurationElement'.
public static void LoadFrom(this ConfigurationElement configElement, object source)
{
// get index property setter
var indexPropertySetter = configElement.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetProperty)
.FirstOrDefault(p =>
{
if (!p.Name.Equals("Item"))
return false;
var parameters = p.GetIndexParameters();
if (parameters == null || parameters.Length != 1)
return false;
return parameters[0].ParameterType == typeof(string);
});
if (indexPropertySetter == null)
throw new Exception("Cannot find expected property 'Item' from the type 'ConfigurationElement'.");
// source properties
var properties = source.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
var targetProperties = configElement.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty)
.ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
var emptyObjectArr = new object[0];
var writableAttrs = new List>();
foreach (var sourceProperty in properties)
{
if (!sourceProperty.PropertyType.IsSerializable)
continue;
PropertyInfo targetProperty;
if (targetProperties.TryGetValue(sourceProperty.Name, out targetProperty))
{
if (targetProperty.CanWrite)
{
writableAttrs.Add(new KeyValuePair(sourceProperty, targetProperty));
continue;
}
}
var value = sourceProperty.GetValue(source, emptyObjectArr);
// lower the first char
var nameChars = sourceProperty.Name.ToArray();
nameChars[0] = char.ToLower(nameChars[0]);
var propertyName = new string(nameChars);
indexPropertySetter.SetValue(configElement, value, new object[] { propertyName });
}
foreach (var pair in writableAttrs)
{
var value = pair.Key.GetValue(source, emptyObjectArr);
pair.Value.SetValue(configElement, value, emptyObjectArr);
}
}
///
/// Gets the current configuration of the configuration element.
///
/// The current configuration.
/// Configuration element.
public static Configuration GetCurrentConfiguration(this ConfigurationElement configElement)
{
var configElementType = typeof(ConfigurationElement);
var configProperty = configElementType.GetProperty("CurrentConfiguration", BindingFlags.Instance | BindingFlags.Public);
if(configProperty == null)
configProperty = configElementType.GetProperty("Configuration", BindingFlags.Instance | BindingFlags.NonPublic);
if (configProperty == null)
return null;
return (Configuration)configProperty.GetValue(configElement, null);
}
private static void ResetConfigurationForMono(AppDomain appDomain, string configFilePath)
{
appDomain.SetupInformation.ConfigurationFile = configFilePath;
var configSystem = typeof(ConfigurationManager)
.GetField("configSystem", BindingFlags.Static | BindingFlags.NonPublic)
.GetValue(null);
// clear previous state
typeof(ConfigurationManager)
.Assembly.GetTypes()
.Where(x => x.FullName == "System.Configuration.ClientConfigurationSystem")
.First()
.GetField("cfg", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(configSystem, null);
}
private static void ResetConfigurationForDotNet(AppDomain appDomain, string configFilePath)
{
appDomain.SetData("APP_CONFIG_FILE", configFilePath);
// clear previous state
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Static;
typeof(ConfigurationManager)
.GetField("s_initState", flags)
.SetValue(null, 0);
typeof(ConfigurationManager)
.GetField("s_configSystem", flags)
.SetValue(null, null);
typeof(ConfigurationManager)
.Assembly.GetTypes()
.Where(x => x.FullName == "System.Configuration.ClientConfigPaths")
.First()
.GetField("s_current", flags)
.SetValue(null, null);
}
///
/// Reset application's configuration to a another config file
///
/// the assosiated AppDomain
/// the config file path want to reset to
public static void ResetConfiguration(this AppDomain appDomain, string configFilePath)
{
if (Platform.IsMono)
ResetConfigurationForMono(appDomain, configFilePath);
else
ResetConfigurationForDotNet(appDomain, configFilePath);
}
}
}