/// <summary>
        /// Updates the specified configuration loading settings from the configuration file.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// The configuration is a child configuration (try to load the root configuration instead).
        /// </exception>
        /// <exception cref="ConfigurationException">Loading the configuration file failed.</exception>
        public override void Load(CascadedConfiguration configuration)
        {
            if (configuration != configuration.RootConfiguration)
            {
                throw new InvalidOperationException("The configuration is a child configuration (try to load the root configuration instead).");
            }

            lock (configuration.Sync)
            {
                // load existing configuration file
                mXmlDocument = new XmlDocument();
                try
                {
                    if (File.Exists(mConfigurationFilePath))
                    {
                        mXmlDocument.Load(mConfigurationFilePath);
                    }
                }
                catch (Exception ex)
                {
                    throw new ConfigurationException(
                              string.Format("Loading configuration file ({0}) failed.", mConfigurationFilePath),
                              ex);
                }

                // read configuration from the xml document into the configuration (should not throw any exception)
                XmlElement root = mXmlDocument.SelectSingleNode("//ConfigurationFile") as XmlElement;
                if (root != null)
                {
                    LoadInternal(configuration, root);
                }
            }
        }
        /// <summary>
        /// Adds/sets an XML element ('Item') with the specified name in the 'name' attribute and the specified value
        /// as inner text of the 'Item' element.
        /// </summary>
        /// <param name="parent">The parent XML element of the 'Item' element to add/set.</param>
        /// <param name="configuration">Configuration the configuration item is in.</param>
        /// <param name="itemName">Name of the 'Item' element to add/set.</param>
        /// <param name="type">Type of the value to set.</param>
        /// <param name="value">Value of the 'Item' element to add/set (null to remove the item).</param>
        private XmlElement SetItem(XmlElement parent, CascadedConfiguration configuration, string itemName, Type type, object value)
        {
            if (type.IsArray && type.GetArrayRank() == 1)
            {
                // an hash value is stored using nested 'Item' elements
                Type       elementType  = type.GetElementType();
                Array      array        = value as Array;
                XmlElement arrayElement = SetItem(parent, itemName, null);

                // remove all old xml elements representing an hash element
                foreach (XmlNode node in arrayElement.SelectNodes("Item"))
                {
                    arrayElement.RemoveChild(node);
                }

                // add new xml elements, one for each hash element
                for (int i = 0; i < array.Length; i++)
                {
                    object obj = array.GetValue(i);
                    SetItem(arrayElement, configuration, null, elementType, obj);
                }

                return(arrayElement);
            }
            else if (type.IsEnum)
            {
                // an enumeration value should be stored as a string
                string s = value.ToString();
                return(SetItem(parent, itemName, s));
            }
            else
            {
                // the configuration item contains a value that is not covered by the standard types

                // try to get a converter that has been registered with the configuration
                IConverter converter = GetValueConverter(type);
                if (converter == null)
                {
                    converter = Converters.GetGlobalConverter(type);
                }

                if (converter != null)
                {
                    // found converter, write configuration item into the xml document
                    string s = converter.ConvertObjectToString(value, CultureInfo.InvariantCulture);
                    return(SetItem(parent, itemName, s));
                }
                else
                {
                    // there is no way to make the configuration item persistent
                    // (should never happen, since such configuration item should never get into the configuration!)
                    throw new ConfigurationException(
                              "The configuration contains an item the configuration cannot make persistent (configuration: {0}, item: {1}, item type: {2}).",
                              configuration.Name, itemName, type.FullName);
                }
            }
        }
        /// <summary>
        /// Saves configuration data from the specified configuration into the backend storage.
        /// </summary>
        /// <param name="configuration">Configuration to save.</param>
        /// <param name="flags">Flags controlling the saving behavior.</param>
        /// <exception cref="ConfigurationException">
        /// The configuration is a child configuration (try to load the root configuration instead).
        /// </exception>
        public override void Save(CascadedConfiguration configuration, CascadedConfigurationSaveFlags flags)
        {
            if (mConfigurationFilePath == null)
            {
                throw new ConfigurationException("The configuration is a child configuration (try to save the root configuration instead).");
            }

            // load existing configuration file
            XmlDocument doc = new XmlDocument();

            try
            {
                if (File.Exists(mConfigurationFilePath))
                {
                    doc.Load(mConfigurationFilePath);
                }
            }
            catch (Exception ex)
            {
                throw new ConfigurationException(
                          string.Format("Loading existing configuration file ({0}) before saving failed.", mConfigurationFilePath),
                          ex);
            }

            // create root node, if necessary
            XmlElement root = doc.SelectSingleNode("//ConfigurationFile") as XmlElement;

            if (root == null)
            {
                root = doc.CreateElement("ConfigurationFile");
                doc.AppendChild(root);
            }

            // modify the xml document to reflect the settings in the configuration
            lock (configuration.Sync) {
                SaveInternal(configuration, root, flags);
            }

            // save the xml document
            string directoryPath = Path.GetDirectoryName(mConfigurationFilePath);

            if (!string.IsNullOrWhiteSpace(directoryPath) && !Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }
            string tempFile = mConfigurationFilePath + ".tmp";

            doc.Save(tempFile);
            try { File.Delete(mConfigurationFilePath); } catch { }
            File.Move(tempFile, mConfigurationFilePath);
        }
Example #4
0
        public void GetChildConfiguration_AddNewAndGet(string path)
        {
            // at the beginning, the configuration should not exist
            CascadedConfiguration child = mConfiguration.GetChildConfiguration(path, false);

            Assert.Null(child);

            // create configuration
            child = mConfiguration.GetChildConfiguration(path, true);
            Assert.NotNull(child);

            // get existing configuration
            CascadedConfiguration existingChild = mConfiguration.GetChildConfiguration(path, false);

            Assert.NotNull(existingChild);
            Assert.Same(child, existingChild);
        }
        /// <summary>
        /// Reads the specified XML element (a 'Configuration' element) and updates the corresponding items in the specified
        /// configuration.
        /// </summary>
        /// <param name="configuration">Configuration to update.</param>
        /// <param name="parent">Parent element in the XML tree.</param>
        private void LoadInternal(CascadedConfiguration configuration, XmlElement parent)
        {
            // read 'Configuration' element
            XmlElement configurationElement = parent.SelectSingleNode(string.Format("Configuration[@name='{0}']", configuration.Name)) as XmlElement;

            if (configurationElement != null)
            {
                foreach (ICascadedConfigurationItem item in configuration.Items)
                {
                    XmlElement itemElement = configurationElement.SelectSingleNode(string.Format("Item[@name='{0}']", item.Name)) as XmlElement;
                    if (itemElement != null)
                    {
                        object value = GetValueFromXmlElement(itemElement, item.Name, item.Type);
                        item.Value = value;
                    }
                    else
                    {
                        item.ResetValue();
                    }
                }

                // load child configurations
                foreach (CascadedConfiguration child in configuration.Children)
                {
                    LoadInternal(child, configurationElement);
                }

                // add configurations that do not exist, yet
                // (items are not mapped, since items are typed and the file does not contain any type information)
                foreach (XmlElement element in configurationElement.SelectNodes("Configuration[@name]"))
                {
                    string name = element.Attributes["name"].Value;
                    CascadedConfiguration child = configuration.Children.Where(x => x.Name == name).FirstOrDefault();
                    if (child == null)
                    {
                        child = configuration.GetChildConfiguration(CascadedConfigurationPathHelper.EscapeName(name), true);
                        LoadInternal(child, configurationElement);
                    }
                }
            }
            else
            {
                configuration.ResetItems(true);
            }
        }
        /// <summary>
        /// Loads the value of the specified configuration item from the persistent storage.
        /// </summary>
        /// <param name="item">Item to load.</param>
        public override void LoadItem(ICascadedConfigurationItem item)
        {
            CascadedConfiguration configuration = item.Configuration.RootConfiguration;

            lock (configuration.Sync)
            {
                if (mXmlDocument != null)
                {
                    XmlElement root = mXmlDocument.SelectSingleNode("//ConfigurationFile") as XmlElement;
                    if (root != null)
                    {
                        XmlElement rootConfigurationElement = root.SelectSingleNode(string.Format("Configuration[@name='{0}']", configuration.Name)) as XmlElement;
                        if (rootConfigurationElement != null)
                        {
                            LoadItemInternal(item, item.Path.TrimStart('/'), rootConfigurationElement);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Writes the specified configuration into the specified XML element
        /// </summary>
        /// <param name="configuration">Configuration to write.</param>
        /// <param name="parent">Parent element in the XML tree.</param>
        /// <param name="flags">Flags controlling the saving behavior.</param>
        private void SaveInternal(CascadedConfiguration configuration, XmlElement parent, CascadedConfigurationSaveFlags flags)
        {
            // create 'Configuration' element
            XmlElement configurationElement = parent.SelectSingleNode(string.Format("Configuration[@name='{0}']", configuration.Name)) as XmlElement;

            if (configurationElement == null)
            {
                configurationElement = parent.OwnerDocument.CreateElement("Configuration");
                XmlAttribute configurationNameAttribute = parent.OwnerDocument.CreateAttribute("name");
                configurationNameAttribute.InnerText = configuration.Name;
                configurationElement.Attributes.Append(configurationNameAttribute);
                parent.AppendChild(configurationElement);
            }

            foreach (ICascadedConfigurationItem item in configuration.Items)
            {
                if (item.HasValue || flags.HasFlag(CascadedConfigurationSaveFlags.SaveInheritedSettings))
                {
                    XmlElement itemElement = SetItem(configurationElement, configuration, item.Name, item.Type, item.Value);

                    // remove all comment nodes before the node
                    for (int i = 0; i < configurationElement.ChildNodes.Count; i++)
                    {
                        XmlNode node = configurationElement.ChildNodes[i];
                        if (node == itemElement)
                        {
                            for (int j = i; j > 0; j--)
                            {
                                node = configurationElement.ChildNodes[j - 1];
                                if (node.NodeType != XmlNodeType.Comment)
                                {
                                    break;
                                }
                                configurationElement.RemoveChild(node);
                            }
                            break;
                        }
                    }

                    // add comment nodes
                    if (item.Comment != null)
                    {
                        string[] commentLines = item.Comment.Split('\n');
                        foreach (string commentLine in commentLines)
                        {
                            string line = commentLine.Trim();
                            if (line.Length > 0)
                            {
                                XmlComment commentNode = configurationElement.OwnerDocument.CreateComment(line);
                                configurationElement.InsertBefore(commentNode, itemElement);
                            }
                        }
                    }
                }
                else
                {
                    RemoveItem(configurationElement, item.Name);
                }
            }

            // save child configurations
            foreach (CascadedConfiguration child in configuration.Children)
            {
                SaveInternal(child, configurationElement, flags);
            }
        }
Example #8
0
 /// <summary>
 /// Sets the configuration the current item is in.
 /// </summary>
 /// <param name="configuration">Configuration to set.</param>
 void ICascadedConfigurationItemInternal.SetConfiguration(CascadedConfiguration configuration)
 {
     Configuration = configuration;
 }
        public CascadedConfigurationTests_XmlFilePersistence()
        {
            ICascadedConfigurationPersistenceStrategy persistence = new XmlFilePersistenceStrategy(ConfigurationFilePath);

            mConfiguration = new CascadedConfiguration(RootConfigurationName, persistence);
        }
Example #10
0
        public CascadedConfigurationTests_RegistryPersistence()
        {
            ICascadedConfigurationPersistenceStrategy persistence = new RegistryPersistenceStrategy(RegistryKeyPath);

            mConfiguration = new CascadedConfiguration(RootConfigurationName, persistence);
        }
Example #11
0
        /// <summary>
        /// Saves the specified configuration in the registry.
        /// </summary>
        /// <param name="configuration">Configuration to save.</param>
        /// <param name="flags">Flags controlling the save behavior.</param>
        /// <exception cref="SecurityException">The user does not have the permissions required to read from the registry key.</exception>
        /// <exception cref="ConfigurationException">Saving the configuration failed due to a serialization error.</exception>
        public override void Save(CascadedConfiguration configuration, CascadedConfigurationSaveFlags flags)
        {
            lock (configuration.Sync)
            {
                string keyPath = mKeyBasePath + "\\" + configuration.Path.Trim('/').Replace('/', '\\');

                foreach (ICascadedConfigurationItem item in configuration.Items)
                {
                    if (item.HasValue || flags.HasFlag(CascadedConfigurationSaveFlags.SaveInheritedSettings))
                    {
                        if (item.Type.IsArray)
                        {
                            // array type => always mapped to a multi-string
                            // => use converter to perform the conversion (all basically supported types also have a converter)
                            Type       elementType = item.Type.GetElementType();
                            IConverter converter   = GetValueConverter(elementType);
                            if (converter == null)
                            {
                                converter = Converters.GetGlobalConverter(elementType);
                            }

                            List <string> value = new List <string>();
                            foreach (object obj in (IEnumerable)item.Value)
                            {
                                value.Add(converter.ConvertObjectToString(obj, CultureInfo.InvariantCulture));
                            }

                            Registry.SetValue(keyPath, item.Name, value.ToArray(), RegistryValueKind.MultiString);
                        }
                        else
                        {
                            if (item.Type == typeof(bool) || item.Type == typeof(byte) || item.Type == typeof(ushort) || item.Type == typeof(uint))
                            {
                                // booleans and unsigned integers (8/16/32-bit) best map to a registry DWORD (32-bit)
                                object value = item.Value;
                                if (value is bool)
                                {
                                    value = (bool)value ? 1 : 0;
                                }
                                Registry.SetValue(keyPath, item.Name, value, RegistryValueKind.DWord);
                            }
                            else if (item.Type == typeof(ulong))
                            {
                                // unsigned integer (64-bit) best maps to a registry DWORD (64-bit)
                                Registry.SetValue(keyPath, item.Name, item.Value, RegistryValueKind.QWord);
                            }
                            else if (item.Type == typeof(string[]))
                            {
                                // a string hash becomes a registry multi-string
                                Registry.SetValue(keyPath, item.Name, item.Value, RegistryValueKind.MultiString);
                            }
                            else if (item.Type.IsEnum)
                            {
                                // an enumeration value should be stored as a string
                                string value = item.Value.ToString();
                                Registry.SetValue(keyPath, item.Name, value, RegistryValueKind.String);
                            }
                            else
                            {
                                // the configuration item contains a value that is not covered by the standard types
                                // => try to find some other way to make it persistent...
                                if (SaveUsingConverter(item, keyPath))
                                {
                                    continue;
                                }

                                // there is no way to make the configuration item persistent
                                // (should never happen, since types that are not supported should never get into the configuration)
                                throw new ConfigurationException(
                                          "The configuration contains an item (path: {0}, type: {1}) that cannot be loaded/saved.",
                                          item.Path, item.Type.FullName);
                            }
                        }
                    }
                    else
                    {
                        // the configuration item does not have a value
                        // => remove corresponding registry value
                        DeleteRegistryValue(keyPath, item.Name);
                    }
                }
            }

            // save child configurations
            foreach (CascadedConfiguration child in configuration.Children)
            {
                child.Save(flags);
            }
        }
Example #12
0
        /// <summary>
        /// Updates the specified configuration loading settings from the registry.
        /// </summary>
        /// <exception cref="SecurityException">
        /// The user does not have the permissions required to read from the registry key.
        /// </exception>
        /// <exception cref="ConfigurationException">
        /// Loading the configuration failed due to a serialization error.
        /// </exception>
        public override void Load(CascadedConfiguration configuration)
        {
            lock (configuration.Sync)
            {
                string keyPath = mKeyBasePath + "\\" + configuration.Path.Trim('/').Replace('/', '\\');

                foreach (ICascadedConfigurationItem item in configuration.Items)
                {
                    object registryValue = Registry.GetValue(keyPath, item.Name, null);

                    if (registryValue != null)
                    {
                        if (item.Type.IsArray)
                        {
                            // array types are always mapped to a registry multi-string
                            if (registryValue.GetType() == typeof(string[]))
                            {
                                IConverter converter = GetValueConverter(item.Type);
                                if (converter == null)
                                {
                                    converter = Converters.GetGlobalConverter(item.Type);
                                }

                                if (converter != null)
                                {
                                    try
                                    {
                                        string[] regMultiStringValue = (string[])registryValue;
                                        Array    conversionResult    = Array.CreateInstance(item.Type.GetElementType(), regMultiStringValue.Length);
                                        for (int i = 0; i < regMultiStringValue.Length; i++)
                                        {
                                            object obj = converter.ConvertStringToObject(regMultiStringValue[i], CultureInfo.InvariantCulture);
                                            conversionResult.SetValue(obj, i);
                                        }
                                        item.Value = conversionResult;
                                        continue;
                                    }
                                    catch (Exception)
                                    {
                                        throw new ConfigurationException(
                                                  @"Loading configuration item (path: {0}, type: {1}) failed, because parsing the registry value (path: {2}\{3}, value: {4}) failed.",
                                                  item.Path, item.Type.FullName, keyPath, item.Name, registryValue);
                                    }
                                }
                            }
                            else
                            {
                                throw new ConfigurationException(
                                          @"Loading configuration item (path: {0}, type: {1}) failed, because the registry value (path: {2}\{3}) is expected to be a '{4}', but it isn't.",
                                          item.Path, item.Type.FullName, keyPath, item.Name, RegistryValueKind.MultiString);
                            }
                        }
                        else
                        {
                            if (item.Type == typeof(bool) || item.Type == typeof(byte) || item.Type == typeof(ushort) || item.Type == typeof(uint))
                            {
                                // booleans and 8/16/32 bit unsigned integers are mapped to a registry DWORD
                                if (registryValue.GetType() == typeof(int))
                                {
                                    if (item.Type == typeof(bool))
                                    {
                                        item.Value = (bool)((int)registryValue != 0);
                                    }
                                    else if (item.Type == typeof(byte))
                                    {
                                        item.Value = (byte)(int)registryValue;
                                    }
                                    else if (item.Type == typeof(ushort))
                                    {
                                        item.Value = (ushort)(int)registryValue;
                                    }
                                    else if (item.Type == typeof(uint))
                                    {
                                        item.Value = (uint)(int)registryValue;
                                    }
                                    continue;
                                }
                                else
                                {
                                    throw new ConfigurationException(
                                              @"Loading configuration item (path: {0}, type: {1}) failed, because the registry value (path: {2}\{3}) is expected to be a '{4}', but it isn't.",
                                              item.Path, item.Type.FullName, mKeyBasePath, item.Name, RegistryValueKind.DWord);
                                }
                            }
                            else if (item.Type == typeof(ulong))
                            {
                                // 64 bit unsigned integers are mapped to a registry QWORD
                                if (registryValue.GetType() == typeof(long))
                                {
                                    item.Value = (ulong)(long)registryValue;
                                    continue;
                                }
                                else
                                {
                                    throw new ConfigurationException(
                                              @"Loading configuration item (path: {0}, type: {1}) failed, because the registry value (path: {2}\{3}) is expected to be a '{4}', but it isn't.",
                                              item.Path, item.Type.FullName, keyPath, item.Name, RegistryValueKind.QWord);
                                }
                            }
                            else if (item.Type == typeof(string[]))
                            {
                                // string arrays are mapped to a registry multi-string
                                if (registryValue.GetType() == typeof(string[]))
                                {
                                    item.Value = registryValue;
                                    continue;
                                }
                                else
                                {
                                    throw new ConfigurationException(
                                              @"Loading configuration item (path: {0}, type: {1}) failed, because the registry value (path: {2}\{3}) is expected to be a '{4}', but it isn't.",
                                              item.Path, item.Type.FullName, keyPath, item.Name, RegistryValueKind.MultiString);
                                }
                            }
                            else if (item.Type.IsEnum)
                            {
                                // enumerations are mapped to a registry string
                                if (registryValue.GetType() == typeof(string))
                                {
                                    try
                                    {
                                        item.Value = Enum.Parse(item.Type, (string)registryValue);
                                        continue;
                                    }
                                    catch (Exception)
                                    {
                                        throw new ConfigurationException(
                                                  @"Loading configuration item (path: {0}, type: {1}) failed, because parsing the registry value (path: {2}\{3}, value: {4}) failed.",
                                                  item.Path, item.Type.FullName, keyPath, item.Name, registryValue);
                                    }
                                }
                                else
                                {
                                    throw new ConfigurationException(
                                              @"Loading configuration item (path: {0}, type: {1}) failed, because the registry value (path: {2}\{3}) is expected to be a '{4}', but it isn't.",
                                              item.Path, item.Type.FullName, keyPath, item.Name, RegistryValueKind.String);
                                }
                            }
                            else
                            {
                                // any other type is converted to a string using a converter
                                if (registryValue.GetType() == typeof(string))
                                {
                                    IConverter converter = GetValueConverter(item.Type);
                                    if (converter == null)
                                    {
                                        converter = Converters.GetGlobalConverter(item.Type);
                                    }

                                    try
                                    {
                                        if (converter != null)
                                        {
                                            object obj = converter.ConvertStringToObject((string)registryValue, CultureInfo.InvariantCulture);
                                            item.Value = obj;
                                            continue;
                                        }
                                    }
                                    catch (Exception)
                                    {
                                        throw new ConfigurationException(
                                                  @"Loading configuration item (path: {0}, type: {1}) failed, because parsing the registry value (path: {2}\{3}, value: {4}) failed.",
                                                  item.Path, item.Type.FullName, keyPath, item.Name, registryValue);
                                    }
                                }
                                else
                                {
                                    throw new ConfigurationException(
                                              @"Loading configuration item (path: {0}, type: {1}) failed, because the registry value (path: {2}\{3}) is expected to be a '{4}', but it isn't.",
                                              item.Path, item.Type.FullName, keyPath, item.Name, RegistryValueKind.String);
                                }
                            }

                            // there is no way to make the configuration item persistent
                            // (should never happen, since types that are not supported should never get into the configuration)
                            throw new ConfigurationException(
                                      "The configuration contains an item (path: {0}, type: {1}) that cannot be loaded/saved.",
                                      item.Path, item.Type.FullName);
                        }
                    }
                }

                // load child configurations
                foreach (CascadedConfiguration child in configuration.Children)
                {
                    child.Load();
                }
            }
        }
 /// <summary>
 /// Saves configuration data from the specified configuration into the backend storage.
 /// </summary>
 /// <param name="configuration">Configuration to save.</param>
 /// <param name="flags">Flags controlling the saving behavior.</param>
 public abstract void Save(CascadedConfiguration configuration, CascadedConfigurationSaveFlags flags);
 /// <summary>
 /// Loads configuration data from the backend storage into the specified configuration.
 /// </summary>
 /// <param name="configuration">Configuration to update.</param>
 public abstract void Load(CascadedConfiguration configuration);
Example #15
0
 public CascadedConfigurationTests_NoPersistence()
 {
     mConfiguration = new CascadedConfiguration("Test Configuration", null);
 }