From: <mar...@us...> - 2010-02-06 23:11:13
|
Revision: 17 http://cronoscontrol.svn.sourceforge.net/cronoscontrol/?rev=17&view=rev Author: marioarce Date: 2010-02-06 23:11:07 +0000 (Sat, 06 Feb 2010) Log Message: ----------- Ticket #4 Setting Manager class Implemented a class that manages an application configuration XML file Added Paths: ----------- source/trunk/CronosControl/Libraries/BusinessLogic/Configuration/ source/trunk/CronosControl/Libraries/BusinessLogic/Configuration/Settings/ source/trunk/CronosControl/Libraries/BusinessLogic/Configuration/Settings/SettingManager.cs Added: source/trunk/CronosControl/Libraries/BusinessLogic/Configuration/Settings/SettingManager.cs =================================================================== --- source/trunk/CronosControl/Libraries/BusinessLogic/Configuration/Settings/SettingManager.cs (rev 0) +++ source/trunk/CronosControl/Libraries/BusinessLogic/Configuration/Settings/SettingManager.cs 2010-02-06 23:11:07 UTC (rev 17) @@ -0,0 +1,575 @@ +//------------------------------------------------------------------------------ +// The contents of this file are subject to the GNU General Public License Version 3.0 ("License"); you may not use this file except in compliance with the License. +// You may obtain a copy of the License at http://www.cronoscontrol.net/license.html. +// +// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +// See the License for the specific language governing rights and limitations under the License. +// +// The Original Code is CronosControl. +// The Initial Developer of the Original Code is WebImageConsulting http://www.wicnow.com/. +// All Rights Reserved. +// +// Contributor(s): Mario Alberto Arce, _______. +//------------------------------------------------------------------------------ + +using System; +using System.IO; +using System.Xml; +using System.Linq; +using System.Text; +using System.Threading; +using System.Configuration; +using System.Collections.Generic; + +namespace CronosControl.BusinessLogic.Configuration.Settings +{ + /// <summary> + /// Setting manager + /// Manages an application configuration XML file + /// </summary> + /// <example> + /// <strong>SettingManager.Current["mail/smtpPort"]</strong><br /> + /// <strong>SettingManager.Current["viewport/width"] = "800"</strong> + /// <strong>SettingManager.Current["viewport/background@red_element"] = "F6"</strong> + /// </example> + /// <remarks> + /// Setting values to non-existing nodes in xml configuration file is not supported + /// </remarks> + /// <seealso href="http://www.w3.org/TR/xpath"/> + public partial class SettingManager + { + #region Constants + /// <summary> + /// Name of the default configuration file to read if not specified in constructor + /// </summary> + private const string DEFAULT_CONFIGURATION_FILENAME = "Config.xml"; + + /// <summary> + /// Name of the default XML element which acts as the configuration root + /// </summary> + private const string DEFAULT_CONFIG_ROOT = "/configuration"; + + /// <summary> + /// Indicates a default value to determine if the configuration is cached or not + /// </summary> + private const bool DEFAULT_IS_CONFIG_CACHEABLE = true; + + /// <summary> + /// Indicates a default value to determine if setting values automatically flush the config to disk + /// </summary> + private const bool DEFAULT_AUTO_FLUSH_ON_SET = true; + + /// <summary> + /// Indicates a default value to use in case NULL replacement was set to 'true' + /// </summary> + private const string DEFAULT_VALUE_IF_NOT_EXISTS = null; + + /// <summary> + /// Indicates if non-existent nodes should return a custom value (e.g.: null, "") + /// </summary> + private const bool DEFAULT_USE_REPLACEMENT_IF_NULL = false; + #endregion + + #region Constructors + /// <summary> + /// Initializes a new instance of the <see cref="SettingManager"/> class. + /// </summary> + private SettingManager() + : this(null, null) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="SettingManager"/> class. + /// </summary> + /// <param name="configurationFilePath">The configuration file path.</param> + private SettingManager(string configurationFilePath) + : this(configurationFilePath, null) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="SettingManager"/> class. + /// </summary> + /// <param name="configurationFilePath">The configuration file path.</param> + /// <param name="rootElement">The root element.</param> + private SettingManager(string configurationFilePath, string rootElement) + { + if (configurationFilePath == null) + { + // Determines if to use configuration-specified SettingManager fileName, or use default + string fileName = DEFAULT_CONFIGURATION_FILENAME; + if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["CronosControl.Web.Configuration.FileName"])) + { + fileName = ConfigurationManager.AppSettings["CronosControl.Web.Configuration.FileName"]; + } + + // Determines the base dir to construct the full path + string baseDir = Thread.GetDomain().BaseDirectory; + if (System.Web.HttpContext.Current != null) + { + try + { + baseDir = System.Web.HttpContext.Current.Request.PhysicalApplicationPath; + } + catch + { + // sometimes during the executing HttpContext.Current.Request would not be initialized, + // not even going to be null, but trying to get this value + // the application raise an exception, this is reason of this try-catch + } + } + baseDir = baseDir.Replace("/", "\\"); + configurationFilePath = _ConfigurationFileName = Path.Combine(baseDir, fileName); + } + + this._ConfigurationFileName = configurationFilePath; + + if (rootElement == null) + { + // Determines if to use configuration-specified SettingManager rootElement, or use default + if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["CronosControl.Web.Configuration.RootElement"])) + { + rootElement = ConfigurationManager.AppSettings["CronosControl.Web.Configuration.RootElement"]; + } + else + { + rootElement = DEFAULT_CONFIG_ROOT; + } + } + this._ConfigurationRoot = rootElement; + + // Determines if configuration is going to be cached in ram, or load it from disk upon each access + if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["CronosControl.Web.Configuration.UseCache"])) + { + _UseCache = ConfigurationManager.AppSettings["CronosControl.Web.Configuration.RootElement"] == "true"; + } + else + { + _UseCache = DEFAULT_IS_CONFIG_CACHEABLE; + } + } + #endregion + + #region Properties + private static SettingManager _Current = null; + /// <summary> + /// Gets the singleton instance of configuration. + /// </summary> + /// <value>The current.</value> + public static SettingManager Current + { + get + { + if (_Current == null) + { + //NOTE: Not using 'lock' or 'Syncronized' since static variables in .NET now are thread-safe + if (_Current == null) + { + _Current = new SettingManager(); + } + } + return _Current; + } + } + + private bool _UseCache = DEFAULT_IS_CONFIG_CACHEABLE; + /// <summary> + /// Gets or sets a value indicating whether [use cache]. + /// </summary> + /// <value><c>true</c> if [use cache]; otherwise, <c>false</c>.</value> + private bool UseCache + { + get + { + return _UseCache; + } + set + { + _UseCache = value; + } + } + + private bool _FlushOnSet = DEFAULT_AUTO_FLUSH_ON_SET; + /// <summary> + /// Gets or sets a value indicating whether [to flush] when setting values. + /// </summary> + /// <value><c>true</c> if [flush on set]; otherwise, <c>false</c>.</value> + public bool FlushOnSet + { + get + { + return _FlushOnSet; + } + set + { + _FlushOnSet = value; + } + } + + private bool _UseValueIfNotExists = DEFAULT_USE_REPLACEMENT_IF_NULL; + /// <summary> + /// Gets or sets a value indicating whether [use value if not exists], instead of throwing an exception. + /// </summary> + /// <value> + /// <c>true</c> if [use value if not exists]; otherwise, <c>false</c>. + /// </value> + public bool UseValueIfNotExists + { + get + { + return _UseValueIfNotExists; + } + set + { + _UseValueIfNotExists = value; + } + } + + private string _ValueIfNotExists = DEFAULT_VALUE_IF_NOT_EXISTS; + /// <summary> + /// Gets or sets the value that is used if <strong>UseValueIfNotExists</strong> is [true] and a non-existent node was found + /// </summary> + /// <value>The value if not exists.</value> + public string ValueIfNotExists + { + get + { + return _ValueIfNotExists; + } + set + { + _ValueIfNotExists = value; + } + } + + private XmlDocument _ConfigurationDocument = null; + /// <summary> + /// Gets the configuration document. + /// </summary> + /// <value>The configuration document.</value> + private XmlDocument ConfigurationDocument + { + get + { + if (_ConfigurationDocument == null) + { + Reload(); + } + return _ConfigurationDocument; + } + } + + private string _ConfigurationRoot; + /// <summary> + /// Gets the configuration root. + /// </summary> + /// <value>The configuration root.</value> + private string ConfigurationRoot + { + get + { + if (String.IsNullOrEmpty(_ConfigurationRoot)) + { + _ConfigurationRoot = DEFAULT_CONFIG_ROOT; + } + return _ConfigurationRoot; + } + } + + private string _ConfigurationFileName = null; + /// <summary> + /// Gets the name of the configuration file. + /// </summary> + /// <value>The name of the configuration file.</value> + internal string ConfigurationFileName + { + get + { + return _ConfigurationFileName; + } + } + + /// <summary> + /// Gets or sets the <see cref="System.String"/> with the specified branch. + /// </summary> + /// <value></value> + public string this[string branch, string key] + { + get + { + return GetValue(branch + "/" + key); + } + set + { + SetValue(branch + "/" + key, value); + } + } + + /// <summary> + /// Gets or sets the <see cref="System.String"/> with the specified expression. + /// </summary> + /// <value></value> + public string this[string expression] + { + get + { + return GetValue(expression); + } + set + { + SetValue(expression, value); + } + } + #endregion + + #region Methods + /// <summary> + /// Gets the configuration for a specific filename + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns></returns> + public static SettingManager GetConfiguration(string filename) + { + return new SettingManager(filename); + } + + /// <summary> + /// Gets the configuration for a specific file and root name + /// </summary> + /// <param name="filename">The filename.</param> + /// <param name="rootElement">The root element.</param> + /// <returns></returns> + public static SettingManager GetConfiguration(string filename, string rootElement) + { + return new SettingManager(filename, rootElement); + } + + /// <summary> + /// Gets the configuration from the current file name, but for a particular node + /// </summary> + /// <param name="rootElement">The root element.</param> + /// <returns></returns> + public static SettingManager GetConfigurationFromCurrent(string rootElement) + { + return new SettingManager(Current.ConfigurationFileName, rootElement); + } + + /// <summary> + /// Reloads configuration + /// </summary> + public void Reload() + { + try + { + _ConfigurationDocument = new XmlDocument(); + _ConfigurationDocument.Load(ConfigurationFileName); + } + catch (Exception ex) + { + throw new SettingManagerException("Unable to load configuration", ex, this); + } + } + + /// <summary> + /// Gets the value. + /// </summary> + /// <param name="expression">The expression.</param> + /// <returns></returns> + public string GetValue(string expression) + { + if (!UseCache) + { + Reload(); + } + + XmlNode node = ConfigurationDocument.SelectSingleNode(ConfigurationRoot + "/" + expression); + if (node == null) + { + if (UseValueIfNotExists) + { + return ValueIfNotExists; + } + + throw new SettingManagerException( + "Configuration node '" + expression + "' was not found", + this + ); + } + else if (node is XmlElement) + { + return node.InnerText; + } + else if (node is XmlAttribute) + { + return node.Value; + } + else + { + return node.InnerXml; + } + } + + /// <summary> + /// Sets the value. + /// </summary> + /// <param name="expression">The expression.</param> + /// <param name="value">The value.</param> + public void SetValue(string expression, string value) + { + XmlNode selectedNode = ConfigurationDocument.SelectSingleNode(ConfigurationRoot + "/" + expression); + if (selectedNode == null) + { + throw new SettingManagerException("Selected node not found. Setting values on non-existent nodes is not supported yet", this); + } + else if (selectedNode is XmlElement) + { + (selectedNode as XmlElement).InnerText = value; + } + else if (selectedNode is XmlAttribute) + { + (selectedNode as XmlAttribute).Value = value; + } + else + { + throw new SettingManagerException("The type of configuration node specified is not supported", this); + } + + if (FlushOnSet) + { + try + { + Flush(); + } + catch (Exception ex) + { + throw new SettingManagerException( + "Unable to save changes to disk. Check the configuration file exists and write access is allowed", + ex, this + ); + } + } + } + + /// <summary> + /// Flushes current configuration to disk. + /// </summary> + public void Flush() + { + + try + { + ConfigurationDocument.Save(ConfigurationFileName); + } + catch (Exception ex) + { + throw new SettingManagerException( + "Unable to save changes to disk. Check the configuration file exists and write access is allowed", + ex, this + ); + } + } + + /// <summary> + /// Gets a value specifying a branch and key name, where the branch is the parent element of the key element + /// </summary> + /// <param name="branch">The branch.</param> + /// <param name="nodeName">Name of the node.</param> + /// <returns></returns> + [Obsolete( + "Use instead" + + " 'SettingManager.Current[\"xpathExpression\"]' or " + + " 'SettingManager.Current[\"branch\", \"key\"]'", false + )] + public string GetNodeValue(string branch, string nodeName) + { + return GetValue(branch + "/" + nodeName); + } + + /// <summary> + /// Gets the node value. + /// </summary> + /// <param name="branch">The branch.</param> + /// <param name="nodeName">Name of the node.</param> + /// <param name="handleException">if set to <c>true</c> [handle exception].</param> + /// <returns></returns> + [Obsolete( + "Use instead" + + " 'SettingManager.Current[\"xpathExpression\"]' or " + + " 'SettingManager.Current[\"branch\", \"key\"]'", false + )] + public string GetNodeValue(string branch, string nodeName, bool handleException) + { + try + { + return GetValue(branch + "/" + nodeName); + } + catch (Exception e) + { + if (handleException) + { + e.Data.Add("branch", branch); + } + else + { + throw new SettingManagerException( + "Unable to get configuration value for key '" + nodeName + "'", + e, this + ); + } + return null; + } + } + #endregion + } + + /// <summary> + /// Wraps configuration exceptions + /// </summary> + public class SettingManagerException : ApplicationException + { + private SettingManager targetConfiguration = null; + + /// <summary> + /// Initializes a new instance of the <see cref="SettingManagerException"/> class. + /// </summary> + /// <param name="message">The message.</param> + public SettingManagerException(string message) + : base(message) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="SettingManagerException"/> class. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="ex">The ex.</param> + public SettingManagerException(string message, Exception ex) + : base(message) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="SettingManagerException"/> class. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="cfg">The CFG.</param> + public SettingManagerException(string message, SettingManager cfg) + : base(message) + { + targetConfiguration = cfg; + } + + /// <summary> + /// Initializes a new instance of the <see cref="SettingManagerException"/> class. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="ex">The exception.</param> + /// <param name="configuration">The configuration.</param> + public SettingManagerException(string message, Exception ex, SettingManager configuration) + : base(message, ex) + { + targetConfiguration = configuration; + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |