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