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); } } }