610 lines · 26.5 KB · C#
RawDownload
//******************************************************************************************************
//  ConfigurationFile.cs - Gbtc
//
//  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
//  not use this file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://www.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  04/12/2006 - Pinal C. Patel
//       Generated original version of source code.
//  11/14/2006 - Pinal C. Patel
//       Modified the ValidateConfigurationFile to save the config file only it was modified.
//  12/12/2006 - Pinal C. Patel
//       Wrote the TrimEnd function that is being used for initialized the Configuration object.
//  08/20/2007 - Darrell Zuercher
//       Edited code comments.
//  09/22/2008 - Pinal C. Patel
//       Converted code to C#.
//  09/29/2008 - Pinal C. Patel
//       Reviewed code comments.
//  09/14/2009 - Stephen C. Wills
//       Added new header and license agreement.
//  09/29/2008 - Pinal C. Patel
//       Added new CurrentWeb and CurrentWin static properties to get the configuration file for a 
//       specific type of application (i.e. Web or Windows).
//       Modified GetConfiguration() to remove dependency on HttpContext.Current to avoid exception in
//       async web application operations where HttpContext.Current is null.
//  04/20/2010 - Pinal C. Patel
//       Added static Open() method as the only way of retrieving a ConfigurationFile instance.
//       Added internal UserConfigurationFile class for accessing and updating user scope settings.
//  07/01/2010 - Pinal C. Patel
//       Removed FileName, AppSettings and ConnectionStrings proxy properties and replaced them with
//       new Configuration property to allow for full access to the application configuration.
//  11/26/2010 - Pinal C. Patel
//       Updated usage example to include user-scope setting sample.
//  12/05/2010 - Pinal C. Patel
//       Added Culture property that can be used for specifying a culture to use for value conversion.
//  01/04/2011 - J. Ritchie Carroll
//       Modified culture to default to InvariantCulture for English style parsing defaults.
//  04/07/2011 - J. Ritchie Carroll
//       Added a Reload() method to reload the configuration file settings and a
//       RestoreDefaultUserSettings() method to restore the default user settings.
//  12/12/2011 - J. Ritchie Carroll
//       Added synchronization to save methods such that saves from multiple threads will not
//       cause issues when lots of new settings are being added.
//  12/14/2012 - Starlynn Danyelle Gilliam
//       Modified Header.
//
//******************************************************************************************************

using System;
using System.Collections.Concurrent;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Xml;
using GSF.IO;
using GSF.Threading;
using GSF.Xml;

namespace GSF.Configuration
{
    /// <summary>
    /// Represents a configuration file of a Windows or Web application.
    /// </summary>
    /// <example>
    /// This example shows how to save and read settings from the config file:
    /// <code>
    /// using System;
    /// using System.Configuration;
    /// using GSF;
    /// using GSF.Configuration;
    /// 
    /// class Program
    /// {
    ///     static void Main(string[] args)
    ///     {
    ///         // Get the application config file.
    ///         ConfigurationFile config = ConfigurationFile.Current;
    /// 
    ///         // Get the sections of config file.
    ///         CategorizedSettingsElementCollection startup = config.Settings["Startup"];
    ///         CategorizedSettingsElementCollection passwords = config.Settings["Passwords"];
    ///         CategorizedSettingsElementCollection monitoring = config.Settings["Monitoring"];
    ///         KeyValueConfigurationCollection appSettings = config.Configuration.AppSettings.Settings;
    ///         ConnectionStringSettingsCollection connStrings = config.Configuration.ConnectionStrings.ConnectionStrings;
    /// 
    ///         // Add settings to the config file under the "appSettings" section.
    ///         appSettings.Add("SaveSettingOnExit", true.ToString());
    ///         // Add settings to the config file under the "connectionStrings" section.
    ///         connStrings.Add(new ConnectionStringSettings("DevSql", "Server=SqlServer;Database=Sandbox;Trusted_Connection=True"));
    ///         // Add settings to the config (if they don't exist) under a custom "monitoring" section.
    ///         monitoring.Add("RefreshInterval", 5, "Interval in seconds at which the Monitor screen is to be refreshed.");
    ///         monitoring.Add("MessagesSnapshot", 30000, "Maximum messages length to be displayed on the Monitor screen.");
    ///         // Add password to the config file encrypted (if it doesn't exist) under a custom "passwords" section.
    ///         passwords.Add("Admin", "Adm1nP4ss", "Password used for performing administrative tasks.", true);
    ///         // Add user-scope setting to the config (if it doesn't exist) under a custom "startup" section.
    ///         startup.Add("Theme", "Default", "Application theme to use for the session.", false, SettingScope.User);
    ///         config.Save();  // Save settings to the config file.
    /// 
    ///         // Read saved settings from the config file.
    ///         bool saveSettingsOnExit = appSettings["SaveSettingOnExit"].Value.ParseBoolean();
    ///         string devConnectionString = connStrings["DevSql"].ConnectionString;
    ///         string appTheme = startup["Theme"].Value;
    ///         string adminPassword = passwords["Admin"].Value;
    ///         int refreshInterval = monitoring["RefreshInterval"].ValueAsInt32();
    ///         int messagesSnapshot = monitoring["MessagesSnapshot"].ValueAsInt32();
    /// 
    ///         // Print the retrieved settings to the console.
    ///         Console.WriteLine("SaveSettingOnExit = {0}", saveSettingsOnExit);
    ///         Console.WriteLine("DevSql = {0}", devConnectionString);
    ///         Console.WriteLine("Theme = {0}", appTheme);
    ///         Console.WriteLine("Admin = {0}", adminPassword);
    ///         Console.WriteLine("RefreshInterval = {0}", refreshInterval);
    ///         Console.WriteLine("MessagesSnapshot = {0}", messagesSnapshot);
    /// 
    ///         Console.ReadLine();
    ///     }
    /// }
    /// </code>
    /// This example shows the content of the config file from the sample code above:
    /// <code>
    /// <![CDATA[
    /// <?xml version="1.0" encoding="utf-8"?>
    /// <configuration>
    ///   <configSections>
    ///     <section name="categorizedSettings" type="GSF.Configuration.CategorizedSettingsSection, GSF.Core" />
    ///   </configSections>
    ///   <appSettings>
    ///     <add key="SaveSettingOnExit" value="True" />
    ///   </appSettings>
    ///   <categorizedSettings>
    ///     <startup>
    ///       <add name="Theme" value="Default" description="Application theme to use for the session."
    ///         encrypted="false" scope="User" />
    ///     </startup>
    ///     <passwords>
    ///       <add name="Admin" value="C+0j6fE/N0Q9b5xaeDKgvRmSeY9zJkO1EQCr7cHoG3x24tztlbBB54PfWsuMGXc/"
    ///         description="Password used for performing administrative tasks."
    ///         encrypted="true" />
    ///     </passwords>
    ///     <monitoring>
    ///       <add name="RefreshInterval" value="5" description="Interval in seconds at which the Monitor screen is to be refreshed."
    ///         encrypted="false" />
    ///       <add name="MessagesSnapshot" value="30000" description="Maximum messages length to be displayed on the Monitor screen."
    ///         encrypted="false" />
    ///     </monitoring>
    ///   </categorizedSettings>
    ///   <connectionStrings>
    ///     <add name="DevSql" connectionString="Server=SqlServer;Database=Sandbox;Trusted_Connection=True" />
    ///   </connectionStrings>
    /// </configuration>
    /// ]]>
    /// </code>
    /// </example>
    /// <seealso cref="CategorizedSettingsSection"/>
    /// <seealso cref="CategorizedSettingsElement"/>
    /// <seealso cref="CategorizedSettingsElementCollection"/>
    // Since each instance of this class is statically cached for the process lifetime, IDisposable is not implemented so that
    // the instance will not be inadvertently disposed
    public class ConfigurationFile
    {
        #region [ Members ]

        // Nested Types
        internal class UserConfigurationFile
        {
            private readonly string m_fileName;
            private readonly XmlDocument m_settings;
            private bool m_settingsModified;

            private const string RootNode = "settings";
            private const string Filename = "Settings.xml";

            public UserConfigurationFile()
            {
                m_settings = new XmlDocument();
                m_fileName = Path.Combine(FilePath.GetApplicationDataFolder(), Filename);
                if (File.Exists(m_fileName))
                {
                    // Load existing settings.
                    m_settings.Load(m_fileName);
                }
                else
                {
                    // Create new settings file.
                    m_settings.AppendChild(m_settings.CreateNode(XmlNodeType.XmlDeclaration, null, null));
                    m_settings.AppendChild(m_settings.CreateElement(RootNode));
                }
            }

            public string FileName
            {
                get
                {
                    return m_fileName;
                }
            }

            public void Save(bool force)
            {
                if (force || m_settingsModified)
                {
                    // Create directory if missing.
                    string folder = FilePath.GetDirectoryName(m_fileName);
                    if (!Directory.Exists(folder))
                        Directory.CreateDirectory(folder);

                    // Save settings to the file.
                    m_settings.Save(m_fileName);
                }
            }

            public void Write(string category, string name, string value)
            {
                XmlNode node = GetSetting(category, name);

                if ((object)node != null)
                {
                    // Setting exists so update it.
                    node.SetAttributeValue("value", value);
                }
                else
                {
                    // Setting doesn't exist so add it.
                    XmlNode setting = m_settings.CreateElement("add");
                    setting.SetAttributeValue("name", name);
                    setting.SetAttributeValue("value", value);
                    m_settings.GetXmlNode(category).AppendChild(setting);
                }

                m_settingsModified = true;
            }

            public string Read(string category, string name, string defaultValue)
            {
                XmlNode node = GetSetting(category, name);

                // If setting exists, return its value
                if ((object)node != null && (object)node.Attributes != null)
                    return node.Attributes["value"].Value;

                // Setting doesn't exist so return the default
                return defaultValue;
            }

            public XmlNode GetSetting(string category, string name)
            {
                return m_settings.SelectSingleNode(string.Format("{0}/{1}/add[@name='{2}']", RootNode, category, name));
            }
        }

        // Constants
        private const string CustomSectionName = "categorizedSettings";
        private const string CustomSectionType = "GSF.Configuration.CategorizedSettingsSection, GSF.Core";

        // Fields
        private string m_cryptoKey;
        private CultureInfo m_culture;
        private System.Configuration.Configuration m_configuration;
        private UserConfigurationFile m_userConfiguration;
        private volatile ConfigurationSaveMode m_saveMode;
        private readonly LongSynchronizedOperation m_saveOperation;
        internal bool m_forceSave;

        #endregion

        #region [ Constructors ]

        /// <summary>
        /// Initializes a new instance of the <see cref="ConfigurationFile"/> class.
        /// </summary>        
        internal ConfigurationFile(string configFilePath)
        {
            m_culture = CultureInfo.InvariantCulture;
            m_saveOperation = new LongSynchronizedOperation(ExecuteConfigurationSave);

            m_configuration = GetConfiguration(configFilePath);

            if (m_configuration.HasFile && File.Exists(m_configuration.FilePath))
                ValidateConfigurationFile(m_configuration.FilePath);
            else
                CreateConfigurationFile(m_configuration.FilePath);

            m_configuration = GetConfiguration(configFilePath);
            m_userConfiguration = new UserConfigurationFile();
        }

        #endregion

        #region [ Properties ]

        /// <summary>
        /// Gets or sets the <see cref="CultureInfo"/> to use for the conversion of setting values to and from <see cref="string"/>.
        /// </summary>
        public CultureInfo Culture
        {
            get
            {
                return m_culture;
            }
            set
            {
                if ((object)value != null)
                    m_culture = value;
                else
                    m_culture = CultureInfo.InvariantCulture;
            }
        }

        /// <summary>
        /// Get the underlying <see cref="System.Configuration.Configuration"/> that can be accessed using this <see cref="ConfigurationFile"/> object.
        /// </summary>
        public System.Configuration.Configuration Configuration
        {
            get
            {
                return m_configuration;
            }
        }

        /// <summary>
        /// Gets the <see cref="CategorizedSettingsSection"/> object representing settings under the "categorizedSettings" section of the config file.
        /// </summary>
        public CategorizedSettingsSection Settings
        {
            get
            {
                CategorizedSettingsSection settings = (CategorizedSettingsSection)m_configuration.GetSection(CustomSectionName);
                settings.File = this;
                settings.SetCryptoKey(m_cryptoKey);
                return settings;
            }
        }

        /// <summary>
        /// Gets the <see cref="UserConfigurationFile"/> where user specific settings are saved.
        /// </summary>
        internal UserConfigurationFile UserSettings
        {
            get
            {
                return m_userConfiguration;
            }
        }

        #endregion

        #region [ Methods ]

        /// <summary>
        /// Writes the configuration settings contained within this <see cref="ConfigurationFile"/> object to the configuration file that it represents.
        /// </summary>
        public void Save()
        {
            Save(m_forceSave ? ConfigurationSaveMode.Full : ConfigurationSaveMode.Modified);
        }

        /// <summary>
        /// Writes the configuration settings contained within this <see cref="ConfigurationFile"/> object to the configuration file that it represents.
        /// </summary>
        /// <param name="saveMode">One of the <see cref="ConfigurationSaveMode"/> values.</param>
        public void Save(ConfigurationSaveMode saveMode)
        {
            m_saveMode = saveMode;

            // Since the static open method always returns the same instance for the same configuration files,
            // synchronizing the instance will sync all saves.
            m_saveOperation.RunOnceAsync();
        }

        private void ExecuteConfigurationSave()
        {
            // Perform configuration save
            ConfigurationSaveMode saveMode = m_saveMode;
            m_configuration.Save(saveMode, m_forceSave);
            m_userConfiguration.Save(saveMode == ConfigurationSaveMode.Full);
            m_saveMode = ConfigurationSaveMode.Minimal;
            m_forceSave = false;
        }

        /// <summary>
        /// Writes the configuration settings contained within this <see cref="ConfigurationFile"/> object to the specified configuration file.
        /// </summary>
        /// <param name="fileName">The path and file name to save the configuration file to.</param>
        public void SaveAs(string fileName)
        {
            m_configuration.SaveAs(fileName);
        }

        /// <summary>
        /// Reloads the current configuration settings from the configuration file that the <see cref="ConfigurationFile"/> represents.
        /// </summary>
        public void Reload()
        {
            m_configuration = GetConfiguration(m_configuration.FilePath);
            m_userConfiguration = new UserConfigurationFile();
        }

        /// <summary>
        /// Restores all the default settings for <see cref="SettingScope.User"/> scoped settings.
        /// </summary>
        public void RestoreDefaultUserSettings()
        {
            if ((object)m_userConfiguration != null)
            {
                string fileName = m_userConfiguration.FileName;

                if (File.Exists(fileName))
                    File.Delete(fileName);

                m_userConfiguration = new UserConfigurationFile();
            }
        }

        /// <summary>
        /// Sets the key to be used for encrypting and decrypting values of <see cref="Settings"/>.
        /// </summary>
        /// <param name="cryptoKey">New crypto key.</param>
        public void SetCryptoKey(string cryptoKey)
        {
            m_cryptoKey = cryptoKey;
        }

        private System.Configuration.Configuration GetConfiguration(string configFilePath)
        {
            ApplicationType appType = Common.GetApplicationType();
            System.Configuration.Configuration configuration;

            if ((object)configFilePath != null)
            {
                if (string.IsNullOrEmpty(configFilePath) || string.Compare(FilePath.GetExtension(configFilePath), ".config", true) == 0)
                {
                    // PCP - 12/12/2006: Using the TrimEnd function to get the correct value that needs to be passed
                    // to the method call for getting the Configuration object. The previous method (String.TrimEnd())
                    // yielded incorrect output resulting in the Configuration object not being initialized correctly.
                    switch (appType)
                    {
                        case ApplicationType.Web:
                            if (string.IsNullOrEmpty(configFilePath))
                                configFilePath = HostingEnvironment.ApplicationVirtualPath;
                            configuration = WebConfigurationManager.OpenWebConfiguration(TrimEnd(configFilePath, "web.config"));
                            break;
                        default:
                            configuration = ConfigurationManager.OpenExeConfiguration(TrimEnd(configFilePath, ".config").EnsureEnd(".exe"));
                            break;
                    }
                }
                else
                {
                    throw (new ArgumentException("Path of configuration file must be either empty or end in \'.config\'"));
                }
            }
            else
            {
                throw (new ArgumentNullException("configFilePath", "Path of configuration file path cannot be null"));
            }

            return configuration;
        }

        private void CreateConfigurationFile(string configFilePath)
        {
            if (!string.IsNullOrEmpty(configFilePath))
            {
                XmlTextWriter configFileWriter = new XmlTextWriter(configFilePath, Encoding.UTF8);
                configFileWriter.Indentation = 2;
                configFileWriter.Formatting = Formatting.Indented;

                // Populates the very basic information required in a config file.
                configFileWriter.WriteStartDocument();
                configFileWriter.WriteStartElement("configuration");
                configFileWriter.WriteEndElement();
                configFileWriter.WriteEndDocument();

                // Closes the config file.
                configFileWriter.Close();

                ValidateConfigurationFile(configFilePath);
            }
            else
            {
                throw (new ArgumentNullException("configFilePath", "Path of configuration file path cannot be null"));
            }
        }

        // Validate configuration file

Lines 1–500 of 610.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t