diff --git a/ExtensionLoader/Config/AliasText.cs b/ExtensionLoader/Config/AliasText.cs new file mode 100644 index 00000000..d47901ad --- /dev/null +++ b/ExtensionLoader/Config/AliasText.cs @@ -0,0 +1,147 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; + +namespace ExtensionLoader.Config +{ + /// + public class AliasText + { + #region Private variables + Hashtable intAlias = null; + Hashtable booleanAlias = null; + #endregion + + #region Constructors + /// + public AliasText () + { + intAlias = InsensitiveHashtable (); + booleanAlias = InsensitiveHashtable (); + DefaultAliasLoad (); + } + #endregion + + #region Public methods + /// + public void AddAlias (string key, string alias, int value) + { + if (intAlias.Contains (key)) { + Hashtable keys = (Hashtable)intAlias[key]; + + keys[alias] = value; + } else { + Hashtable keys = InsensitiveHashtable (); + keys[alias] = value; + intAlias.Add (key, keys); + } + } + + /// + public void AddAlias (string alias, bool value) + { + booleanAlias[alias] = value; + } + +#if (NET_COMPACT_1_0) +#else + /// + public void AddAlias (string key, Enum enumAlias) + { + SetAliasTypes (key, enumAlias); + } +#endif + + /// + public bool ContainsBoolean (string key) + { + return booleanAlias.Contains (key); + } + + /// + public bool ContainsInt (string key, string alias) + { + bool result = false; + + if (intAlias.Contains (key)) { + Hashtable keys = (Hashtable)intAlias[key]; + result = (keys.Contains (alias)); + } + + return result; + } + + /// + public bool GetBoolean (string key) + { + if (!booleanAlias.Contains (key)) { + throw new ArgumentException ("Alias does not exist for text"); + } + + return (bool)booleanAlias[key]; + } + + /// + public int GetInt (string key, string alias) + { + if (!intAlias.Contains (key)) { + throw new ArgumentException ("Alias does not exist for key"); + } + + Hashtable keys = (Hashtable)intAlias[key]; + + if (!keys.Contains (alias)) { + throw new ArgumentException ("Config value does not match a " + + "supplied alias"); + } + + return (int)keys[alias]; + } + #endregion + + #region Private methods + /// + /// Loads the default alias values. + /// + private void DefaultAliasLoad () + { + AddAlias("true", true); + AddAlias("false", false); + } + +#if (NET_COMPACT_1_0) +#else + /// + /// Extracts and sets the alias types from an enumeration. + /// + private void SetAliasTypes (string key, Enum enumAlias) + { + string[] names = Enum.GetNames (enumAlias.GetType ()); + int[] values = (int[])Enum.GetValues (enumAlias.GetType ()); + + for (int i = 0; i < names.Length; i++) + { + AddAlias (key, names[i], values[i]); + } + } +#endif + + /// + /// Returns a case insensitive hashtable. + /// + private Hashtable InsensitiveHashtable () + { + return new Hashtable (StringComparer.OrdinalIgnoreCase); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Config/ConfigBase.cs b/ExtensionLoader/Config/ConfigBase.cs new file mode 100644 index 00000000..b4c83b89 --- /dev/null +++ b/ExtensionLoader/Config/ConfigBase.cs @@ -0,0 +1,422 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using System.Globalization; +using ExtensionLoader.Util; + +namespace ExtensionLoader.Config +{ + #region ConfigKeyEventArgs class + /// + public delegate void ConfigKeyEventHandler (object sender, ConfigKeyEventArgs e); + + /// + public class ConfigKeyEventArgs : EventArgs + { + string keyName = null; + string keyValue = null; + + /// + public ConfigKeyEventArgs (string keyName, string keyValue) + { + this.keyName = keyName; + this.keyValue = keyValue; + } + + /// + public string KeyName + { + get { return keyName; } + } + + /// + public string KeyValue + { + get { return keyValue; } + } + } + #endregion + + /// + public class ConfigBase : IConfig + { + #region Private variables + string configName = null; + IConfigSource configSource = null; + AliasText aliasText = null; + IFormatProvider format = NumberFormatInfo.CurrentInfo; + #endregion + + #region Protected variables + protected OrderedList keys = new OrderedList (); + #endregion + + #region Constructors + /// + public ConfigBase (string name, IConfigSource source) + { + configName = name; + configSource = source; + aliasText = new AliasText (); + } + #endregion + + #region Public properties + /// + public string Name + { + get { return configName; } + set { + if (configName != value) { + Rename (value); + } + } + } + + /// + public IConfigSource ConfigSource + { + get { return configSource; } + } + + /// + public AliasText Alias + { + get { return aliasText; } + } + #endregion + + #region Public methods + /// + public bool Contains (string key) + { + return (Get (key) != null); + } + + /// + public virtual string Get (string key) + { + string result = null; + + if (keys.Contains (key)) { + result = keys[key].ToString (); + } + + return result; + } + + /// + public string Get (string key, string defaultValue) + { + string result = Get (key); + + return (result == null) ? defaultValue : result; + } + + /// + public string GetExpanded (string key) + { + return this.ConfigSource.GetExpanded(this, key); + } + + /// + public string GetString (string key) + { + return Get (key); + } + + /// + public string GetString (string key, string defaultValue) + { + return Get (key, defaultValue); + } + + /// + public int GetInt (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToInt32 (text, format); + } + + /// + public int GetInt (string key, bool fromAlias) + { + if (!fromAlias) { + return GetInt (key); + } + + string result = Get (key); + + if (result == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return GetIntAlias (key, result); + } + + /// + public int GetInt (string key, int defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToInt32 (result, format); + } + + /// + public int GetInt (string key, int defaultValue, bool fromAlias) + { + if (!fromAlias) { + return GetInt (key, defaultValue); + } + + string result = Get (key); + + return (result == null) ? defaultValue : GetIntAlias (key, result); + } + + /// + public long GetLong (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToInt64 (text, format); + } + + /// + public long GetLong (string key, long defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToInt64 (result, format); + } + + /// + public bool GetBoolean (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return GetBooleanAlias (text); + } + + /// + public bool GetBoolean (string key, bool defaultValue) + { + string text = Get (key); + + return (text == null) ? defaultValue : GetBooleanAlias (text); + } + + /// + public float GetFloat (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToSingle (text, format); + } + + /// + public float GetFloat (string key, float defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToSingle (result, format); + } + + /// + public double GetDouble (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToDouble (text, format); + } + + /// + public double GetDouble (string key, double defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToDouble (result, format); + } + + /// + public string[] GetKeys () + { + string[] result = new string[keys.Keys.Count]; + + keys.Keys.CopyTo (result, 0); + + return result; + } + + /// + public string[] GetValues () + { + string[] result = new string[keys.Values.Count]; + + keys.Values.CopyTo (result, 0); + + return result; + } + + /// + public void Add (string key, string value) + { + keys.Add (key, value); + } + + /// + public virtual void Set (string key, object value) + { + if (value == null) { + throw new ArgumentNullException ("Value cannot be null"); + } + + if (Get (key) == null) { + this.Add (key, value.ToString ()); + } else { + keys[key] = value.ToString (); + } + + if (ConfigSource.AutoSave) { + ConfigSource.Save (); + } + + OnKeySet (new ConfigKeyEventArgs (key, value.ToString ())); + } + + /// + public virtual void Remove (string key) + { + if (key == null) { + throw new ArgumentNullException ("Key cannot be null"); + } + + if (Get (key) != null) { + string keyValue = null; + if (KeySet != null) { + keyValue = Get (key); + } + keys.Remove (key); + + OnKeyRemoved (new ConfigKeyEventArgs (key, keyValue)); + } + } + #endregion + + #region Public events + /// + public event ConfigKeyEventHandler KeySet; + + /// + public event ConfigKeyEventHandler KeyRemoved; + #endregion + + #region Protected methods + /// + protected void OnKeySet (ConfigKeyEventArgs e) + { + if (KeySet != null) { + KeySet (this, e); + } + } + + /// + protected void OnKeyRemoved (ConfigKeyEventArgs e) + { + if (KeyRemoved != null) { + KeyRemoved (this, e); + } + } + #endregion + + #region Private methods + /// + /// Renames the config to the new name. + /// + private void Rename (string name) + { + this.ConfigSource.Configs.Remove (this); + configName = name; + this.ConfigSource.Configs.Add (this); + } + + /// + /// Returns the integer alias first from this IConfig then + /// the parent if there is none. + /// + private int GetIntAlias (string key, string alias) + { + int result = -1; + + if (aliasText.ContainsInt (key, alias)) { + result = aliasText.GetInt (key, alias); + } else { + result = ConfigSource.Alias.GetInt (key, alias); + } + + return result; + } + + /// + /// Returns the boolean alias first from this IConfig then + /// the parent if there is none. + /// + private bool GetBooleanAlias (string key) + { + bool result = false; + + if (aliasText.ContainsBoolean (key)) { + result = aliasText.GetBoolean (key); + } else { + if (ConfigSource.Alias.ContainsBoolean (key)) { + result = ConfigSource.Alias.GetBoolean (key); + } else { + throw new ArgumentException + ("Alias value not found: " + key + + ". Add it to the Alias property."); + } + } + + return result; + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Config/ConfigCollection.cs b/ExtensionLoader/Config/ConfigCollection.cs new file mode 100644 index 00000000..725c1660 --- /dev/null +++ b/ExtensionLoader/Config/ConfigCollection.cs @@ -0,0 +1,264 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; + +namespace ExtensionLoader.Config +{ + #region ConfigEventHandler class + /// + public delegate void ConfigEventHandler (object sender, ConfigEventArgs e); + + /// + public class ConfigEventArgs : EventArgs + { + IConfig config = null; + + /// + public ConfigEventArgs (IConfig config) + { + this.config = config; + } + + /// + public IConfig Config + { + get { return config; } + } + } + #endregion + + /// + public class ConfigCollection : ICollection, IEnumerable, IList + { + #region Private variables + ArrayList configList = new ArrayList (); + ConfigSourceBase owner = null; + #endregion + + #region Constructors + /// + public ConfigCollection (ConfigSourceBase owner) + { + this.owner = owner; + } + #endregion + + #region Public properties + /// + public int Count + { + get { return configList.Count; } + } + + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + public object SyncRoot + { + get { return this; } + } + + /// + public IConfig this[int index] + { + get { return (IConfig)configList[index]; } + } + + /// + object IList.this[int index] + { + get { return configList[index]; } + set { } + } + + /// + public IConfig this[string configName] + { + get + { + IConfig result = null; + + foreach (IConfig config in configList) + { + if (config.Name == configName) { + result = config; + break; + } + } + + return result; + } + } + + /// + public bool IsFixedSize + { + get { return false; } + } + + /// + public bool IsReadOnly + { + get { return false; } + } + #endregion + + #region Public methods + /// + public void Add (IConfig config) + { + if (configList.Contains (config)) { + throw new ArgumentException ("IConfig already exists"); + } + IConfig existingConfig = this[config.Name]; + + if (existingConfig != null) { + // Set all new keys + string[] keys = config.GetKeys (); + for (int i = 0; i < keys.Length; i++) + { + existingConfig.Set (keys[i], config.Get (keys[i])); + } + } else { + configList.Add (config); + OnConfigAdded (new ConfigEventArgs (config)); + } + } + + /// + int IList.Add (object config) + { + IConfig newConfig = config as IConfig; + + if (newConfig == null) { + throw new Exception ("Must be an IConfig"); + } else { + this.Add (newConfig); + return IndexOf (newConfig); + } + } + + /// + public IConfig Add (string name) + { + ConfigBase result = null; + + if (this[name] == null) { + result = new ConfigBase (name, owner); + configList.Add (result); + OnConfigAdded (new ConfigEventArgs (result)); + } else { + throw new ArgumentException ("An IConfig of that name already exists"); + } + + return result; + } + + /// + public void Remove (IConfig config) + { + configList.Remove (config); + OnConfigRemoved (new ConfigEventArgs (config)); + } + + /// + public void Remove (object config) + { + configList.Remove (config); + OnConfigRemoved (new ConfigEventArgs ((IConfig)config)); + } + + /// + public void RemoveAt (int index) + { + IConfig config = (IConfig)configList[index]; + configList.RemoveAt (index); + OnConfigRemoved (new ConfigEventArgs (config)); + } + + /// + public void Clear () + { + configList.Clear (); + } + + /// + public IEnumerator GetEnumerator () + { + return configList.GetEnumerator (); + } + + /// + public void CopyTo (Array array, int index) + { + configList.CopyTo (array, index); + } + + /// + public void CopyTo (IConfig[] array, int index) + { + ((ICollection)configList).CopyTo (array, index); + } + + /// + public bool Contains (object config) + { + return configList.Contains (config); + } + + /// + public int IndexOf (object config) + { + return configList.IndexOf (config); + } + + /// + public void Insert (int index, object config) + { + configList.Insert (index, config); + } + #endregion + + #region Public events + /// + public event ConfigEventHandler ConfigAdded; + + /// + public event ConfigEventHandler ConfigRemoved; + #endregion + + #region Protected methods + /// + protected void OnConfigAdded (ConfigEventArgs e) + { + if (ConfigAdded != null) { + ConfigAdded (this, e); + } + } + + /// + protected void OnConfigRemoved (ConfigEventArgs e) + { + if (ConfigRemoved != null) { + ConfigRemoved (this, e); + } + } + #endregion + + #region Private methods + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Config/ConfigSourceBase.cs b/ExtensionLoader/Config/ConfigSourceBase.cs new file mode 100644 index 00000000..317c399e --- /dev/null +++ b/ExtensionLoader/Config/ConfigSourceBase.cs @@ -0,0 +1,219 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Text; +using System.Collections; + +namespace ExtensionLoader.Config +{ + /// + public abstract class ConfigSourceBase : IConfigSource + { + #region Private variables + ArrayList sourceList = new ArrayList (); + ConfigCollection configList = null; + bool autoSave = false; + AliasText alias = new AliasText (); + #endregion + + #region Constructors + /// + public ConfigSourceBase () + { + configList = new ConfigCollection (this); + } + #endregion + + #region Public properties + /// + public ConfigCollection Configs + { + get { return configList; } + } + + /// + public bool AutoSave + { + get { return autoSave; } + set { autoSave = value; } + } + + /// + public AliasText Alias + { + get { return alias; } + } + #endregion + + #region Public methods + /// + public void Merge (IConfigSource source) + { + if (!sourceList.Contains (source)) { + sourceList.Add (source); + } + + foreach (IConfig config in source.Configs) + { + this.Configs.Add (config); + } + } + + /// + public virtual IConfig AddConfig (string name) + { + return configList.Add (name); + } + + /// + public string GetExpanded (IConfig config, string key) + { + return Expand (config, key, false); + } + + /// + public virtual void Save () + { + OnSaved (new EventArgs ()); + } + + /// + public virtual void Reload () + { + OnReloaded (new EventArgs ()); + } + + /// + public void ExpandKeyValues () + { + string[] keys = null; + + foreach (IConfig config in configList) + { + keys = config.GetKeys (); + for (int i = 0; i < keys.Length; i++) + { + Expand (config, keys[i], true); + } + } + } + + /// + public void ReplaceKeyValues () + { + ExpandKeyValues (); + } + #endregion + + #region Public events + /// + public event EventHandler Reloaded; + + /// + public event EventHandler Saved; + #endregion + + #region Protected methods + /// + protected void OnReloaded (EventArgs e) + { + if (Reloaded != null) { + Reloaded (this, e); + } + } + + /// + protected void OnSaved (EventArgs e) + { + if (Saved != null) { + Saved (this, e); + } + } + #endregion + + #region Private methods + /// + /// Expands key values from the given IConfig. + /// + private string Expand (IConfig config, string key, bool setValue) + { + string result = config.Get (key); + if (result == null) { + throw new ArgumentException (String.Format ("[{0}] not found in [{1}]", + key, config.Name)); + } + + while (true) + { + int startIndex = result.IndexOf ("${", 0); + if (startIndex == -1) { + break; + } + + int endIndex = result.IndexOf ("}", startIndex + 2); + if (endIndex == -1) { + break; + } + + string search = result.Substring (startIndex + 2, + endIndex - (startIndex + 2)); + + if (search == key) { + // Prevent infinite recursion + throw new ArgumentException + ("Key cannot have a expand value of itself: " + key); + } + + string replace = ExpandValue (config, search); + + result = result.Replace("${" + search + "}", replace); + } + + if (setValue) { + config.Set(key, result); + } + + return result; + } + + /// + /// Returns the replacement value of a config. + /// + private string ExpandValue (IConfig config, string search) + { + string result = null; + + string[] replaces = search.Split ('|'); + + if (replaces.Length > 1) { + IConfig newConfig = this.Configs[replaces[0]]; + if (newConfig == null) { + throw new ArgumentException ("Expand config not found: " + + replaces[0]); + } + result = newConfig.Get (replaces[1]); + if (result == null) { + throw new ArgumentException ("Expand key not found: " + + replaces[1]); + } + } else { + result = config.Get (search); + + if (result == null) { + throw new ArgumentException ("Key not found: " + search); + } + } + + return result; + } + #endregion + } +} diff --git a/ExtensionLoader/Config/IConfig.cs b/ExtensionLoader/Config/IConfig.cs new file mode 100644 index 00000000..c550e477 --- /dev/null +++ b/ExtensionLoader/Config/IConfig.cs @@ -0,0 +1,99 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; + +namespace ExtensionLoader.Config +{ + /// + public interface IConfig + { + /// + IConfigSource ConfigSource { get; } + + /// + string Name { get; set; } + + /// + AliasText Alias { get; } + + /// + bool Contains (string key); + + /// + string Get (string key); + + /// + string Get (string key, string defaultValue); + + /// + string GetExpanded (string key); + + /// + string GetString (string key); + + /// + string GetString (string key, string defaultValue); + + /// + int GetInt (string key); + + /// + int GetInt (string key, bool fromAlias); + + /// + int GetInt (string key, int defaultValue); + + /// + int GetInt (string key, int defaultValue, bool fromAlias); + + /// + long GetLong (string key); + + /// + long GetLong (string key, long defaultValue); + + /// + bool GetBoolean (string key); + + /// + bool GetBoolean (string key, bool defaultValue); + + /// + float GetFloat (string key); + + /// + float GetFloat (string key, float defaultValue); + + /// + double GetDouble (string key); + + /// + double GetDouble (string key, double defaultValue); + + /// + string[] GetKeys (); + + /// + string[] GetValues (); + + /// + void Set (string key, object value); + + /// + void Remove (string key); + + /// + event ConfigKeyEventHandler KeySet; + + /// + event ConfigKeyEventHandler KeyRemoved; + } +} \ No newline at end of file diff --git a/ExtensionLoader/Config/IConfigSource.cs b/ExtensionLoader/Config/IConfigSource.cs new file mode 100644 index 00000000..b029a380 --- /dev/null +++ b/ExtensionLoader/Config/IConfigSource.cs @@ -0,0 +1,55 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; + +namespace ExtensionLoader.Config +{ + /// + public interface IConfigSource + { + /// + ConfigCollection Configs { get; } + + /// + bool AutoSave { get; set; } + + /// + AliasText Alias { get; } + + /// + void Merge (IConfigSource source); + + /// + void Save (); + + /// + void Reload (); + + /// + IConfig AddConfig (string name); + + /// + string GetExpanded (IConfig config, string key); + + /// + void ExpandKeyValues (); + + /// + void ReplaceKeyValues (); + + /// + event EventHandler Reloaded; + + /// + event EventHandler Saved; + } +} \ No newline at end of file diff --git a/ExtensionLoader/Config/IniConfig.cs b/ExtensionLoader/Config/IniConfig.cs new file mode 100644 index 00000000..a08d4aa3 --- /dev/null +++ b/ExtensionLoader/Config/IniConfig.cs @@ -0,0 +1,90 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using System.Globalization; +using ExtensionLoader.Util; + +namespace ExtensionLoader.Config +{ + /// + public class IniConfig : ConfigBase + { + #region Private variables + IniConfigSource parent = null; + #endregion + + #region Constructors + /// + public IniConfig (string name, IConfigSource source) + : base(name, source) + { + parent = (IniConfigSource)source; + } + #endregion + + #region Public properties + #endregion + + #region Public methods + /// + public override string Get (string key) + { + if (!parent.CaseSensitive) { + key = CaseInsensitiveKeyName (key); + } + + return base.Get (key); + } + + /// + public override void Set (string key, object value) + { + if (!parent.CaseSensitive) { + key = CaseInsensitiveKeyName (key); + } + + base.Set (key, value); + } + + /// + public override void Remove (string key) + { + if (!parent.CaseSensitive) { + key = CaseInsensitiveKeyName (key); + } + + base.Remove (key); + } + #endregion + + #region Private methods + /// + /// Returns the key name if the case insensitivity is turned on. + /// + private string CaseInsensitiveKeyName (string key) + { + string result = null; + + string lowerKey = key.ToLower (); + foreach (string currentKey in keys.Keys) + { + if (currentKey.ToLower () == lowerKey) { + result = currentKey; + break; + } + } + + return (result == null) ? key : result; + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Config/IniConfigSource.cs b/ExtensionLoader/Config/IniConfigSource.cs new file mode 100644 index 00000000..e1ee9881 --- /dev/null +++ b/ExtensionLoader/Config/IniConfigSource.cs @@ -0,0 +1,329 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Collections; +using ExtensionLoader.Ini; + +namespace ExtensionLoader.Config +{ + /// + public class IniConfigSource : ConfigSourceBase + { + #region Private variables + IniDocument iniDocument = null; + string savePath = null; + bool caseSensitive = true; + #endregion + + #region Public properties + #endregion + + #region Constructors + /// + public IniConfigSource () + { + iniDocument = new IniDocument (); + } + + /// + public IniConfigSource (string filePath) + { + Load (filePath); + } + + /// + public IniConfigSource (TextReader reader) + { + Load (reader); + } + + /// + public IniConfigSource (IniDocument document) + { + Load (document); + } + + /// + public IniConfigSource (Stream stream) + { + Load (stream); + } + #endregion + + #region Public properties + /// + public bool CaseSensitive + { + get { return caseSensitive; } + set { caseSensitive = value; } + } + + /// + public string SavePath + { + get { return savePath; } + } + #endregion + + #region Public methods + /// + public void Load (string filePath) + { + Load (new StreamReader (filePath)); + this.savePath = filePath; + } + + /// + public void Load (TextReader reader) + { + Load (new IniDocument (reader)); + } + + /// + public void Load (IniDocument document) + { + this.Configs.Clear (); + + this.Merge (this); // required for SaveAll + iniDocument = document; + Load (); + } + + /// + public void Load (Stream stream) + { + Load (new StreamReader (stream)); + } + + /// + public override void Save () + { + if (!IsSavable ()) { + throw new ArgumentException ("Source cannot be saved in this state"); + } + + MergeConfigsIntoDocument (); + + iniDocument.Save (this.savePath); + base.Save (); + } + + /// + public void Save (string path) + { + this.savePath = path; + this.Save (); + } + + /// + public void Save (TextWriter writer) + { + MergeConfigsIntoDocument (); + iniDocument.Save (writer); + savePath = null; + OnSaved (new EventArgs ()); + } + + /// + public void Save (Stream stream) + { + MergeConfigsIntoDocument (); + iniDocument.Save (stream); + savePath = null; + OnSaved (new EventArgs ()); + } + + /// + public override void Reload () + { + if (savePath == null) { + throw new ArgumentException ("Error reloading: You must have " + + "the loaded the source from a file"); + } + + iniDocument = new IniDocument (savePath); + MergeDocumentIntoConfigs (); + base.Reload (); + } + + /// + public override string ToString () + { + MergeConfigsIntoDocument (); + StringWriter writer = new StringWriter (); + iniDocument.Save (writer); + + return writer.ToString (); + } + #endregion + + #region Private methods + /// + /// Merges all of the configs from the config collection into the + /// IniDocument before it is saved. + /// + private void MergeConfigsIntoDocument () + { + RemoveSections (); + foreach (IConfig config in this.Configs) + { + string[] keys = config.GetKeys (); + + // Create a new section if one doesn't exist + if (iniDocument.Sections[config.Name] == null) { + IniSection section = new IniSection (config.Name); + iniDocument.Sections.Add (section); + } + RemoveKeys (config.Name); + + for (int i = 0; i < keys.Length; i++) + { + iniDocument.Sections[config.Name].Set (keys[i], config.Get (keys[i])); + } + } + } + + /// + /// Removes all INI sections that were removed as configs. + /// + private void RemoveSections () + { + IniSection section = null; + for (int i = 0; i < iniDocument.Sections.Count; i++) + { + section = iniDocument.Sections[i]; + if (this.Configs[section.Name] == null) { + iniDocument.Sections.Remove (section.Name); + } + } + } + + /// + /// Removes all INI keys that were removed as config keys. + /// + private void RemoveKeys (string sectionName) + { + IniSection section = iniDocument.Sections[sectionName]; + + if (section != null) { + foreach (string key in section.GetKeys ()) + { + if (this.Configs[sectionName].Get (key) == null) { + section.Remove (key); + } + } + } + } + + /// + /// Loads the configuration file. + /// + private void Load () + { + IniConfig config = null; + IniSection section = null; + IniItem item = null; + + for (int j = 0; j < iniDocument.Sections.Count; j++) + { + section = iniDocument.Sections[j]; + config = new IniConfig (section.Name, this); + + for (int i = 0; i < section.ItemCount; i++) + { + item = section.GetItem (i); + + if (item.Type == IniType.Key) { + config.Add (item.Name, item.Value); + } + } + + this.Configs.Add (config); + } + } + + /// + /// Merges the IniDocument into the Configs when the document is + /// reloaded. + /// + private void MergeDocumentIntoConfigs () + { + // Remove all missing configs first + RemoveConfigs (); + + IniSection section = null; + for (int i = 0; i < iniDocument.Sections.Count; i++) + { + section = iniDocument.Sections[i]; + + IConfig config = this.Configs[section.Name]; + if (config == null) { + // The section is new so add it + config = new ConfigBase (section.Name, this); + this.Configs.Add (config); + } + RemoveConfigKeys (config); + } + } + + /// + /// Removes all configs that are not in the newly loaded INI doc. + /// + private void RemoveConfigs () + { + IConfig config = null; + for (int i = this.Configs.Count - 1; i > -1; i--) + { + config = this.Configs[i]; + // If the section is not present in the INI doc + if (iniDocument.Sections[config.Name] == null) { + this.Configs.Remove (config); + } + } + } + + /// + /// Removes all INI keys that were removed as config keys. + /// + private void RemoveConfigKeys (IConfig config) + { + IniSection section = iniDocument.Sections[config.Name]; + + // Remove old keys + string[] configKeys = config.GetKeys (); + foreach (string configKey in configKeys) + { + if (!section.Contains (configKey)) { + // Key doesn't exist, remove + config.Remove (configKey); + } + } + + // Add or set all new keys + string[] keys = section.GetKeys (); + for (int i = 0; i < keys.Length; i++) + { + string key = keys[i]; + config.Set (key, section.GetItem (i).Value); + } + } + + /// + /// Returns true if this instance is savable. + /// + private bool IsSavable () + { + return (this.savePath != null); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Ini/IniDocument.cs b/ExtensionLoader/Ini/IniDocument.cs new file mode 100644 index 00000000..0a996e4d --- /dev/null +++ b/ExtensionLoader/Ini/IniDocument.cs @@ -0,0 +1,302 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Collections; +using ExtensionLoader.Util; + +namespace ExtensionLoader.Ini +{ + #region IniFileType enumeration + /// + public enum IniFileType + { + /// + Standard, + /// + PythonStyle, + /// + SambaStyle, + /// + MysqlStyle, + /// + WindowsStyle + } + #endregion + + /// + public class IniDocument + { + #region Private variables + IniSectionCollection sections = new IniSectionCollection (); + ArrayList initialComment = new ArrayList (); + IniFileType fileType = IniFileType.Standard; + #endregion + + #region Public properties + /// + public IniFileType FileType + { + get { return fileType; } + set { fileType = value; } + } + #endregion + + #region Constructors + /// + public IniDocument (string filePath) + { + fileType = IniFileType.Standard; + Load (filePath); + } + + /// + public IniDocument (string filePath, IniFileType type) + { + fileType = type; + Load (filePath); + } + + /// + public IniDocument (TextReader reader) + { + fileType = IniFileType.Standard; + Load (reader); + } + + /// + public IniDocument (TextReader reader, IniFileType type) + { + fileType = type; + Load (reader); + } + + /// + public IniDocument (Stream stream) + { + fileType = IniFileType.Standard; + Load (stream); + } + + /// + public IniDocument (Stream stream, IniFileType type) + { + fileType = type; + Load (stream); + } + + /// + public IniDocument (IniReader reader) + { + fileType = IniFileType.Standard; + Load (reader); + } + + /// + public IniDocument () + { + } + #endregion + + #region Public methods + /// + public void Load (string filePath) + { + Load (new StreamReader (filePath)); + } + + /// + public void Load (TextReader reader) + { + Load (GetIniReader (reader, fileType)); + } + + /// + public void Load (Stream stream) + { + Load (new StreamReader (stream)); + } + + /// + public void Load (IniReader reader) + { + LoadReader (reader); + } + + /// + public IniSectionCollection Sections + { + get { return sections; } + } + + /// + public void Save (TextWriter textWriter) + { + IniWriter writer = GetIniWriter (textWriter, fileType); + IniItem item = null; + IniSection section = null; + + foreach (string comment in initialComment) + { + writer.WriteEmpty (comment); + } + + for (int j = 0; j < sections.Count; j++) + { + section = sections[j]; + writer.WriteSection (section.Name, section.Comment); + for (int i = 0; i < section.ItemCount; i++) + { + item = section.GetItem (i); + switch (item.Type) + { + case IniType.Key: + writer.WriteKey (item.Name, item.Value, item.Comment); + break; + case IniType.Empty: + writer.WriteEmpty (item.Comment); + break; + } + } + } + + writer.Close (); + } + + /// + public void Save (string filePath) + { + StreamWriter writer = new StreamWriter (filePath); + Save (writer); + writer.Close (); + } + + /// + public void Save (Stream stream) + { + Save (new StreamWriter (stream)); + } + #endregion + + #region Private methods + /// + /// Loads the file not saving comments. + /// + private void LoadReader (IniReader reader) + { + reader.IgnoreComments = false; + bool sectionFound = false; + IniSection section = null; + + try { + while (reader.Read ()) + { + switch (reader.Type) + { + case IniType.Empty: + if (!sectionFound) { + initialComment.Add (reader.Comment); + } else { + section.Set (reader.Comment); + } + + break; + case IniType.Section: + sectionFound = true; + // If section already exists then overwrite it + if (sections[reader.Name] != null) { + sections.Remove (reader.Name); + } + section = new IniSection (reader.Name, reader.Comment); + sections.Add (section); + + break; + case IniType.Key: + if (section.GetValue (reader.Name) == null) { + section.Set (reader.Name, reader.Value, reader.Comment); + } + break; + } + } + } catch (Exception ex) { + throw ex; + } finally { + // Always close the file + reader.Close (); + } + } + + /// + /// Returns a proper INI reader depending upon the type parameter. + /// + private IniReader GetIniReader (TextReader reader, IniFileType type) + { + IniReader result = new IniReader (reader); + + switch (type) + { + case IniFileType.Standard: + // do nothing + break; + case IniFileType.PythonStyle: + result.AcceptCommentAfterKey = false; + result.SetCommentDelimiters (new char[] { ';', '#' }); + result.SetAssignDelimiters (new char[] { ':' }); + break; + case IniFileType.SambaStyle: + result.AcceptCommentAfterKey = false; + result.SetCommentDelimiters (new char[] { ';', '#' }); + result.LineContinuation = true; + break; + case IniFileType.MysqlStyle: + result.AcceptCommentAfterKey = false; + result.AcceptNoAssignmentOperator = true; + result.SetCommentDelimiters (new char[] { '#' }); + result.SetAssignDelimiters (new char[] { ':', '=' }); + break; + case IniFileType.WindowsStyle: + result.ConsumeAllKeyText = true; + break; + } + + return result; + } + + /// + /// Returns a proper IniWriter depending upon the type parameter. + /// + private IniWriter GetIniWriter (TextWriter reader, IniFileType type) + { + IniWriter result = new IniWriter (reader); + + switch (type) + { + case IniFileType.Standard: + case IniFileType.WindowsStyle: + // do nothing + break; + case IniFileType.PythonStyle: + result.AssignDelimiter = ':'; + result.CommentDelimiter = '#'; + break; + case IniFileType.SambaStyle: + case IniFileType.MysqlStyle: + result.AssignDelimiter = '='; + result.CommentDelimiter = '#'; + break; + } + + return result; + } + #endregion + } +} + diff --git a/ExtensionLoader/Ini/IniException.cs b/ExtensionLoader/Ini/IniException.cs new file mode 100644 index 00000000..e7c4a0b0 --- /dev/null +++ b/ExtensionLoader/Ini/IniException.cs @@ -0,0 +1,121 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Security; +using System.Globalization; +using System.Security.Permissions; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; + + +namespace ExtensionLoader.Ini +{ + /// +#if (NET_COMPACT_1_0) +#else + [Serializable] +#endif + public class IniException : SystemException /*, ISerializable */ + { + #region Private variables + IniReader iniReader = null; + string message = ""; + #endregion + + #region Public properties + /// + public int LinePosition + { + get { + return (iniReader == null) ? 0 : iniReader.LinePosition; + } + } + + /// + public int LineNumber + { + get { + return (iniReader == null) ? 0 : iniReader.LineNumber; + } + } + + /// + public override string Message + { + get { + if (iniReader == null) { + return base.Message; + } + + return String.Format (CultureInfo.InvariantCulture, "{0} - Line: {1}, Position: {2}.", + message, this.LineNumber, this.LinePosition); + } + } + #endregion + + #region Constructors + /// + public IniException () + : base () + { + this.message = "An error has occurred"; + } + + /// + public IniException (string message, Exception exception) + : base (message, exception) + { + } + + /// + public IniException (string message) + : base (message) + { + this.message = message; + } + + /// + internal IniException (IniReader reader, string message) + : this (message) + { + iniReader = reader; + this.message = message; + } + +#if (NET_COMPACT_1_0) +#else + /// + protected IniException (SerializationInfo info, StreamingContext context) + : base (info, context) + { + } +#endif + #endregion + + #region Public methods +#if (NET_COMPACT_1_0) +#else + /// + [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)] + public override void GetObjectData (SerializationInfo info, + StreamingContext context) + { + base.GetObjectData (info, context); + if (iniReader != null) { + info.AddValue ("lineNumber", iniReader.LineNumber); + + info.AddValue ("linePosition", iniReader.LinePosition); + } + } +#endif + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Ini/IniItem.cs b/ExtensionLoader/Ini/IniItem.cs new file mode 100644 index 00000000..f30fa2c9 --- /dev/null +++ b/ExtensionLoader/Ini/IniItem.cs @@ -0,0 +1,54 @@ +using System; + +namespace ExtensionLoader.Ini +{ + /// + public class IniItem + { + #region Private variables + IniType iniType = IniType.Empty; + string iniName = ""; + string iniValue = ""; + string iniComment = null; + #endregion + + #region Public properties + /// + public IniType Type + { + get { return iniType; } + set { iniType = value; } + } + + /// + public string Value + { + get { return iniValue; } + set { iniValue = value; } + } + + /// + public string Name + { + get { return iniName; } + } + + /// + public string Comment + { + get { return iniComment; } + set { iniComment = value; } + } + #endregion + + /// + internal protected IniItem (string name, string value, IniType type, string comment) + { + iniName = name; + iniValue = value; + iniType = type; + iniComment = comment; + } + } +} + diff --git a/ExtensionLoader/Ini/IniReader.cs b/ExtensionLoader/Ini/IniReader.cs new file mode 100644 index 00000000..c900d2e3 --- /dev/null +++ b/ExtensionLoader/Ini/IniReader.cs @@ -0,0 +1,652 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Text; +using System.Collections; + +namespace ExtensionLoader.Ini +{ + #region IniReadState enumeration + /// + public enum IniReadState : int + { + /// + Closed, + /// + EndOfFile, + /// + Error, + /// + Initial, + /// + Interactive + }; + #endregion + + #region IniType enumeration + /// + public enum IniType : int + { + /// + Section, + /// + Key, + /// + Empty + } + #endregion + + /// + public class IniReader : IDisposable + { + #region Private variables + int lineNumber = 1; + int column = 1; + IniType iniType = IniType.Empty; + TextReader textReader = null; + bool ignoreComments = false; + StringBuilder name = new StringBuilder (); + StringBuilder value = new StringBuilder (); + StringBuilder comment = new StringBuilder (); + IniReadState readState = IniReadState.Initial; + bool hasComment = false; + bool disposed = false; + bool lineContinuation = false; + bool acceptCommentAfterKey = true; + bool acceptNoAssignmentOperator = false; + bool consumeAllKeyText = false; + char[] commentDelimiters = new char[] { ';' }; + char[] assignDelimiters = new char[] { '=' }; + #endregion + + #region Public properties + /// + public string Name + { + get { return this.name.ToString (); } + } + + /// + public string Value + { + get { return this.value.ToString (); } + } + + /// + public IniType Type + { + get { return iniType; } + } + + /// + public string Comment + { + get { return (hasComment) ? this.comment.ToString () : null; } + } + + /// + public int LineNumber + { + get { return lineNumber; } + } + + /// + public int LinePosition + { + get { return column; } + } + + /// + public bool IgnoreComments + { + get { return ignoreComments; } + set { ignoreComments = value; } + } + + /// + public IniReadState ReadState + { + get { return readState; } + } + + /// + public bool LineContinuation + { + get { return lineContinuation; } + set { lineContinuation = value; } + } + + /// + public bool AcceptCommentAfterKey + { + get { return acceptCommentAfterKey; } + set { acceptCommentAfterKey = value; } + } + + /// + public bool AcceptNoAssignmentOperator + { + get { return acceptNoAssignmentOperator; } + set { acceptNoAssignmentOperator = value; } + } + + /// + public bool ConsumeAllKeyText + { + get { return consumeAllKeyText; } + set { consumeAllKeyText = value; } + } + #endregion + + #region Constructors + /// + public IniReader (string filePath) + { + textReader = new StreamReader (filePath); + } + + /// + public IniReader (TextReader reader) + { + textReader = reader; + } + + /// + public IniReader (Stream stream) + : this (new StreamReader (stream)) + { + } + #endregion + + #region Public methods + /// + public bool Read () + { + bool result = false; + + if (readState != IniReadState.EndOfFile + || readState != IniReadState.Closed) { + readState = IniReadState.Interactive; + result = ReadNext (); + } + + return result; + } + + /// + public bool MoveToNextSection () + { + bool result = false; + + while (true) + { + result = Read (); + + if (iniType == IniType.Section || !result) { + break; + } + } + + return result; + } + + /// + public bool MoveToNextKey () + { + bool result = false; + + while (true) + { + result = Read (); + + if (iniType == IniType.Section) { + result = false; + break; + } + if (iniType == IniType.Key || !result) { + break; + } + } + + return result; + } + + /// + public void Close () + { + Reset (); + readState = IniReadState.Closed; + + if (textReader != null) { + textReader.Close (); + } + } + + /// + public void Dispose () + { + Dispose (true); + } + + /// + public char[] GetCommentDelimiters () + { + char[] result = new char[commentDelimiters.Length]; + Array.Copy (commentDelimiters, 0, result, 0, commentDelimiters.Length); + + return result; + } + + /// + public void SetCommentDelimiters (char[] delimiters) + { + if (delimiters.Length < 1) { + throw new ArgumentException ("Must supply at least one delimiter"); + } + + commentDelimiters = delimiters; + } + + /// + public char[] GetAssignDelimiters () + { + char[] result = new char[assignDelimiters.Length]; + Array.Copy (assignDelimiters, 0, result, 0, assignDelimiters.Length); + + return result; + } + + /// + public void SetAssignDelimiters (char[] delimiters) + { + if (delimiters.Length < 1) { + throw new ArgumentException ("Must supply at least one delimiter"); + } + + assignDelimiters = delimiters; + } + #endregion + + #region Protected methods + /// + protected virtual void Dispose (bool disposing) + { + if (!disposed) { + textReader.Close (); + disposed = true; + + if (disposing) { + GC.SuppressFinalize (this); + } + } + } + #endregion + + #region Private methods + /// + /// Destructor. + /// + ~IniReader () + { + Dispose (false); + } + + /// + /// Resets all of the current INI line data. + /// + private void Reset () + { + this.name.Remove (0, this.name.Length); + this.value.Remove (0, this.value.Length); + this.comment.Remove (0, this.comment.Length); + iniType = IniType.Empty; + hasComment = false; + } + + /// + /// Reads the next INI line item. + /// + private bool ReadNext () + { + bool result = true; + int ch = PeekChar (); + Reset (); + + if (IsComment (ch)) { + iniType = IniType.Empty; + ReadChar (); // consume comment character + ReadComment (); + + return result; + } + + switch (ch) + { + case ' ': + case '\t': + case '\r': + SkipWhitespace (); + ReadNext (); + break; + case '\n': + ReadChar (); + break; + case '[': + ReadSection (); + break; + case -1: + readState = IniReadState.EndOfFile; + result = false; + break; + default: + ReadKey (); + break; + } + + return result; + } + + /// + /// Reads a comment. Must start after the comment delimiter. + /// + private void ReadComment () + { + int ch = -1; + SkipWhitespace (); + hasComment = true; + + do + { + ch = ReadChar (); + this.comment.Append ((char)ch); + } while (!EndOfLine (ch)); + + RemoveTrailingWhitespace (this.comment); + } + + /// + /// Removes trailing whitespace from a StringBuilder. + /// + private void RemoveTrailingWhitespace (StringBuilder builder) + { + string temp = builder.ToString (); + + builder.Remove (0, builder.Length); + builder.Append (temp.TrimEnd (null)); + } + + /// + /// Reads a key. + /// + private void ReadKey () + { + int ch = -1; + iniType = IniType.Key; + + while (true) + { + ch = PeekChar (); + + if (IsAssign (ch)) { + ReadChar (); + break; + } + + if (EndOfLine (ch)) { + if (acceptNoAssignmentOperator) { + break; + } + throw new IniException (this, + String.Format ("Expected assignment operator ({0})", + assignDelimiters[0])); + } + + this.name.Append ((char)ReadChar ()); + } + + ReadKeyValue (); + SearchForComment (); + RemoveTrailingWhitespace (this.name); + } + + /// + /// Reads the value of a key. + /// + private void ReadKeyValue () + { + int ch = -1; + bool foundQuote = false; + int characters = 0; + SkipWhitespace (); + + while (true) + { + ch = PeekChar (); + + if (!IsWhitespace (ch)) { + characters++; + } + + if (!this.ConsumeAllKeyText && ch == '"') { + ReadChar (); + + if (!foundQuote && characters == 1) { + foundQuote = true; + continue; + } else { + break; + } + } + + if (foundQuote && EndOfLine (ch)) { + throw new IniException (this, "Expected closing quote (\")"); + } + + // Handle line continuation + if (lineContinuation && ch == '\\') + { + StringBuilder buffer = new StringBuilder (); + buffer.Append ((char)ReadChar ()); // append '\' + + while (PeekChar () != '\n' && IsWhitespace (PeekChar ())) + { + if (PeekChar () != '\r') { + buffer.Append ((char)ReadChar ()); + } else { + ReadChar (); // consume '\r' + } + } + + if (PeekChar () == '\n') { + // continue reading key value on next line + ReadChar (); + continue; + } else { + // Replace consumed characters + this.value.Append (buffer.ToString ()); + } + } + + if (!this.ConsumeAllKeyText) { + // If accepting comments then don't consume as key value + if (acceptCommentAfterKey && IsComment (ch) && !foundQuote) { + break; + } + } + + // Always break at end of line + if (EndOfLine (ch)) { + break; + } + + this.value.Append ((char)ReadChar ()); + } + + if (!foundQuote) { + RemoveTrailingWhitespace (this.value); + } + } + + /// + /// Reads an INI section. + /// + private void ReadSection () + { + int ch = -1; + iniType = IniType.Section; + ch = ReadChar (); // consume "[" + + while (true) + { + ch = PeekChar (); + if (ch == ']') { + break; + } + if (EndOfLine (ch)) { + throw new IniException (this, "Expected section end (])"); + } + + this.name.Append ((char)ReadChar ()); + } + + ConsumeToEnd (); // all after '[' is garbage + RemoveTrailingWhitespace (this.name); + } + + /// + /// Looks for a comment. + /// + private void SearchForComment () + { + int ch = ReadChar (); + + while (!EndOfLine (ch)) + { + if (IsComment (ch)) { + if (ignoreComments) { + ConsumeToEnd (); + } else { + ReadComment (); + } + break; + } + ch = ReadChar (); + } + } + + /// + /// Consumes all data until the end of a line. + /// + private void ConsumeToEnd () + { + int ch = -1; + + do + { + ch = ReadChar (); + } while (!EndOfLine (ch)); + } + + /// + /// Returns and consumes the next character from the stream. + /// + private int ReadChar () + { + int result = textReader.Read (); + + if (result == '\n') { + lineNumber++; + column = 1; + } else { + column++; + } + + return result; + } + + /// + /// Returns the next upcoming character from the stream. + /// + private int PeekChar () + { + return textReader.Peek (); + } + + /// + /// Returns true if a comment character is found. + /// + private bool IsComment (int ch) + { + return HasCharacter (commentDelimiters, ch); + } + + /// + /// Returns true if character is an assign character. + /// + private bool IsAssign (int ch) + { + return HasCharacter (assignDelimiters, ch); + } + + /// + /// Returns true if the character is found in the given array. + /// + private bool HasCharacter (char[] characters, int ch) + { + bool result = false; + + for (int i = 0; i < characters.Length; i++) + { + if (ch == characters[i]) + { + result = true; + break; + } + } + + return result; + } + + /// + /// Returns true if a value is whitespace. + /// + private bool IsWhitespace (int ch) + { + return ch == 0x20 || ch == 0x9 || ch == 0xD || ch == 0xA; + } + + /// + /// Skips all whitespace. + /// + private void SkipWhitespace () + { + while (IsWhitespace (PeekChar ())) + { + if (EndOfLine (PeekChar ())) { + break; + } + + ReadChar (); + } + } + + /// + /// Returns true if an end of line is found. End of line + /// includes both an end of line or end of file. + /// + private bool EndOfLine (int ch) + { + return (ch == '\n' || ch == -1); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Ini/IniSection.cs b/ExtensionLoader/Ini/IniSection.cs new file mode 100644 index 00000000..bde6a968 --- /dev/null +++ b/ExtensionLoader/Ini/IniSection.cs @@ -0,0 +1,155 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using ExtensionLoader.Util; + +namespace ExtensionLoader.Ini +{ + /// + public class IniSection + { + #region Private variables + OrderedList configList = new OrderedList (); + string name = ""; + string comment = null; + int commentCount = 0; + #endregion + + #region Constructors + /// + public IniSection (string name, string comment) + { + this.name = name; + this.comment = comment; + } + + /// + public IniSection (string name) + : this (name, null) + { + } + #endregion + + #region Public properties + /// + public string Name + { + get { return name; } + } + + /// + public string Comment + { + get { return comment; } + } + + /// + public int ItemCount + { + get { return configList.Count; } + } + #endregion + + #region Public methods + + /// + public string GetValue (string key) + { + string result = null; + + if (Contains (key)) { + IniItem item = (IniItem)configList[key]; + result = item.Value; + } + + return result; + } + + /// + public IniItem GetItem (int index) + { + return (IniItem)configList[index]; + } + + /// + public string[] GetKeys () + { + ArrayList list = new ArrayList (); + IniItem item = null; + + for (int i = 0; i < configList.Count; i++) + { + item = (IniItem)configList[i]; + if (item.Type == IniType.Key) { + list.Add (item.Name); + } + } + string[] result = new string[list.Count]; + list.CopyTo (result, 0); + + return result; + } + + /// + public bool Contains (string key) + { + return (configList[key] != null); + } + + /// + public void Set (string key, string value, string comment) + { + IniItem item = null; + + if (Contains (key)) { + item = (IniItem)configList[key]; + item.Value = value; + item.Comment = comment; + } else { + item = new IniItem (key, value, IniType.Key, comment); + configList.Add (key, item); + } + } + + /// + public void Set (string key, string value) + { + Set (key, value, null); + } + + /// + public void Set (string comment) + { + string name = "#comment" + commentCount; + IniItem item = new IniItem (name, null, + IniType.Empty, comment); + configList.Add (name, item); + + commentCount++; + } + + /// + public void Set () + { + Set (null); + } + + /// + public void Remove (string key) + { + if (Contains (key)) { + configList.Remove (key); + } + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Ini/IniSectionCollection.cs b/ExtensionLoader/Ini/IniSectionCollection.cs new file mode 100644 index 00000000..52475d93 --- /dev/null +++ b/ExtensionLoader/Ini/IniSectionCollection.cs @@ -0,0 +1,95 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using ExtensionLoader.Util; + +namespace ExtensionLoader.Ini +{ + /// + public class IniSectionCollection : ICollection, IEnumerable + { + #region Private variables + OrderedList list = new OrderedList (); + #endregion + + #region Public properties + /// + public IniSection this[int index] + { + get { return (IniSection)list[index]; } + } + + /// + public IniSection this[string configName] + { + get { return (IniSection)list[configName]; } + } + + /// + public int Count + { + get { return list.Count; } + } + + /// + public object SyncRoot + { + get { return list.SyncRoot; } + } + + /// + public bool IsSynchronized + { + get { return list.IsSynchronized; } + } + #endregion + + #region Public methods + /// + public void Add (IniSection section) + { + if (list.Contains (section)) { + throw new ArgumentException ("IniSection already exists"); + } + + list.Add (section.Name, section); + } + + /// + public void Remove (string config) + { + list.Remove (config); + } + + /// + public void CopyTo (Array array, int index) + { + list.CopyTo (array, index); + } + + /// + public void CopyTo (IniSection[] array, int index) + { + ((ICollection)list).CopyTo (array, index); + } + + /// + public IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + #endregion + + #region Private methods + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Ini/IniWriter.cs b/ExtensionLoader/Ini/IniWriter.cs new file mode 100644 index 00000000..8cae6f51 --- /dev/null +++ b/ExtensionLoader/Ini/IniWriter.cs @@ -0,0 +1,308 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Text; + +namespace ExtensionLoader.Ini +{ + #region IniWriteState enumeration + /// + public enum IniWriteState : int + { + /// + Start, + /// + BeforeFirstSection, + /// + Section, + /// + Closed + }; + #endregion + + /// + public class IniWriter : IDisposable + { + #region Private variables + int indentation = 0; + bool useValueQuotes = false; + IniWriteState writeState = IniWriteState.Start; + char commentDelimiter = ';'; + char assignDelimiter = '='; + TextWriter textWriter = null; + string eol = "\r\n"; + StringBuilder indentationBuffer = new StringBuilder (); + Stream baseStream = null; + bool disposed = false; + #endregion + + #region Public properties + /// + public int Indentation + { + get { return indentation; } + set + { + if (value < 0) + throw new ArgumentException ("Negative values are illegal"); + + indentation = value; + indentationBuffer.Remove(0, indentationBuffer.Length); + for (int i = 0; i < value; i++) + indentationBuffer.Append (' '); + } + } + + /// + public bool UseValueQuotes + { + get { return useValueQuotes; } + set { useValueQuotes = value; } + } + + /// + public IniWriteState WriteState + { + get { return writeState; } + } + + /// + public char CommentDelimiter + { + get { return commentDelimiter; } + set { commentDelimiter = value; } + } + + /// + public char AssignDelimiter + { + get { return assignDelimiter; } + set { assignDelimiter = value; } + } + + /// + public Stream BaseStream + { + get { return baseStream; } + } + #endregion + + #region Constructors + /// + public IniWriter(string filePath) + : this (new FileStream (filePath, FileMode.Create, FileAccess.Write, FileShare.None)) + { + } + + /// + public IniWriter (TextWriter writer) + { + textWriter = writer; + StreamWriter streamWriter = writer as StreamWriter; + if (streamWriter != null) { + baseStream = streamWriter.BaseStream; + } + } + + /// + public IniWriter (Stream stream) + : this (new StreamWriter (stream)) + { + } + #endregion + + #region Public methods + /// + public void Close () + { + textWriter.Close (); + writeState = IniWriteState.Closed; + } + + /// + public void Flush () + { + textWriter.Flush (); + } + + /// + public override string ToString () + { + return textWriter.ToString (); + } + + /// + public void WriteSection (string section) + { + ValidateState (); + writeState = IniWriteState.Section; + WriteLine ("[" + section + "]"); + } + + /// + public void WriteSection (string section, string comment) + { + ValidateState (); + writeState = IniWriteState.Section; + WriteLine ("[" + section + "]" + Comment(comment)); + } + + /// + public void WriteKey (string key, string value) + { + ValidateStateKey (); + WriteLine (key + " " + assignDelimiter + " " + GetKeyValue (value)); + } + + /// + public void WriteKey (string key, string value, string comment) + { + ValidateStateKey (); + WriteLine (key + " " + assignDelimiter + " " + GetKeyValue (value) + Comment (comment)); + } + + /// + public void WriteEmpty () + { + ValidateState (); + if (writeState == IniWriteState.Start) { + writeState = IniWriteState.BeforeFirstSection; + } + WriteLine (""); + } + + /// + public void WriteEmpty (string comment) + { + ValidateState (); + if (writeState == IniWriteState.Start) { + writeState = IniWriteState.BeforeFirstSection; + } + if (comment == null) { + WriteLine (""); + } else { + WriteLine (commentDelimiter + " " + comment); + } + } + + /// + public void Dispose () + { + Dispose (true); + } + #endregion + + #region Protected methods + /// + protected virtual void Dispose (bool disposing) + { + if (!disposed) + { + textWriter.Close (); + baseStream.Close (); + disposed = true; + + if (disposing) + { + GC.SuppressFinalize (this); + } + } + } + #endregion + + #region Private methods + /// + /// Destructor. + /// + ~IniWriter () + { + Dispose (false); + } + + /// + /// Returns the value of a key. + /// + private string GetKeyValue (string text) + { + string result; + + if (useValueQuotes) { + result = MassageValue ('"' + text + '"'); + } else { + result = MassageValue (text); + } + + return result; + } + + /// + /// Validates whether a key can be written. + /// + private void ValidateStateKey () + { + ValidateState (); + + switch (writeState) + { + case IniWriteState.BeforeFirstSection: + case IniWriteState.Start: + throw new InvalidOperationException ("The WriteState is not Section"); + case IniWriteState.Closed: + throw new InvalidOperationException ("The writer is closed"); + } + } + + /// + /// Validates the state to determine if the item can be written. + /// + private void ValidateState () + { + if (writeState == IniWriteState.Closed) { + throw new InvalidOperationException ("The writer is closed"); + } + } + + /// + /// Returns a formatted comment. + /// + private string Comment (string text) + { + return (text == null) ? "" : (" " + commentDelimiter + " " + text); + } + + /// + /// Writes data to the writer. + /// + private void Write (string value) + { + textWriter.Write (indentationBuffer.ToString () + value); + } + + /// + /// Writes a full line to the writer. + /// + private void WriteLine (string value) + { + Write (value + eol); + } + + /// + /// Fixes the incoming value to prevent illegal characters from + /// hurting the integrity of the INI file. + /// + private string MassageValue (string text) + { + return text.Replace ("\n", ""); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/AliasText.cs b/ExtensionLoader/Nini/Config/AliasText.cs new file mode 100644 index 00000000..659536a3 --- /dev/null +++ b/ExtensionLoader/Nini/Config/AliasText.cs @@ -0,0 +1,147 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; + +namespace Nini.Config +{ + /// + public class AliasText + { + #region Private variables + Hashtable intAlias = null; + Hashtable booleanAlias = null; + #endregion + + #region Constructors + /// + public AliasText () + { + intAlias = InsensitiveHashtable (); + booleanAlias = InsensitiveHashtable (); + DefaultAliasLoad (); + } + #endregion + + #region Public methods + /// + public void AddAlias (string key, string alias, int value) + { + if (intAlias.Contains (key)) { + Hashtable keys = (Hashtable)intAlias[key]; + + keys[alias] = value; + } else { + Hashtable keys = InsensitiveHashtable (); + keys[alias] = value; + intAlias.Add (key, keys); + } + } + + /// + public void AddAlias (string alias, bool value) + { + booleanAlias[alias] = value; + } + +#if (NET_COMPACT_1_0) +#else + /// + public void AddAlias (string key, Enum enumAlias) + { + SetAliasTypes (key, enumAlias); + } +#endif + + /// + public bool ContainsBoolean (string key) + { + return booleanAlias.Contains (key); + } + + /// + public bool ContainsInt (string key, string alias) + { + bool result = false; + + if (intAlias.Contains (key)) { + Hashtable keys = (Hashtable)intAlias[key]; + result = (keys.Contains (alias)); + } + + return result; + } + + /// + public bool GetBoolean (string key) + { + if (!booleanAlias.Contains (key)) { + throw new ArgumentException ("Alias does not exist for text"); + } + + return (bool)booleanAlias[key]; + } + + /// + public int GetInt (string key, string alias) + { + if (!intAlias.Contains (key)) { + throw new ArgumentException ("Alias does not exist for key"); + } + + Hashtable keys = (Hashtable)intAlias[key]; + + if (!keys.Contains (alias)) { + throw new ArgumentException ("Config value does not match a " + + "supplied alias"); + } + + return (int)keys[alias]; + } + #endregion + + #region Private methods + /// + /// Loads the default alias values. + /// + private void DefaultAliasLoad () + { + AddAlias("true", true); + AddAlias("false", false); + } + +#if (NET_COMPACT_1_0) +#else + /// + /// Extracts and sets the alias types from an enumeration. + /// + private void SetAliasTypes (string key, Enum enumAlias) + { + string[] names = Enum.GetNames (enumAlias.GetType ()); + int[] values = (int[])Enum.GetValues (enumAlias.GetType ()); + + for (int i = 0; i < names.Length; i++) + { + AddAlias (key, names[i], values[i]); + } + } +#endif + + /// + /// Returns a case insensitive hashtable. + /// + private Hashtable InsensitiveHashtable () + { + return new Hashtable (StringComparer.OrdinalIgnoreCase); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/ConfigBase.cs b/ExtensionLoader/Nini/Config/ConfigBase.cs new file mode 100644 index 00000000..a9ce4a47 --- /dev/null +++ b/ExtensionLoader/Nini/Config/ConfigBase.cs @@ -0,0 +1,422 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using System.Globalization; +using Nini.Util; + +namespace Nini.Config +{ + #region ConfigKeyEventArgs class + /// + public delegate void ConfigKeyEventHandler (object sender, ConfigKeyEventArgs e); + + /// + public class ConfigKeyEventArgs : EventArgs + { + string keyName = null; + string keyValue = null; + + /// + public ConfigKeyEventArgs (string keyName, string keyValue) + { + this.keyName = keyName; + this.keyValue = keyValue; + } + + /// + public string KeyName + { + get { return keyName; } + } + + /// + public string KeyValue + { + get { return keyValue; } + } + } + #endregion + + /// + public class ConfigBase : IConfig + { + #region Private variables + string configName = null; + IConfigSource configSource = null; + AliasText aliasText = null; + IFormatProvider format = NumberFormatInfo.CurrentInfo; + #endregion + + #region Protected variables + protected OrderedList keys = new OrderedList (); + #endregion + + #region Constructors + /// + public ConfigBase (string name, IConfigSource source) + { + configName = name; + configSource = source; + aliasText = new AliasText (); + } + #endregion + + #region Public properties + /// + public string Name + { + get { return configName; } + set { + if (configName != value) { + Rename (value); + } + } + } + + /// + public IConfigSource ConfigSource + { + get { return configSource; } + } + + /// + public AliasText Alias + { + get { return aliasText; } + } + #endregion + + #region Public methods + /// + public bool Contains (string key) + { + return (Get (key) != null); + } + + /// + public virtual string Get (string key) + { + string result = null; + + if (keys.Contains (key)) { + result = keys[key].ToString (); + } + + return result; + } + + /// + public string Get (string key, string defaultValue) + { + string result = Get (key); + + return (result == null) ? defaultValue : result; + } + + /// + public string GetExpanded (string key) + { + return this.ConfigSource.GetExpanded(this, key); + } + + /// + public string GetString (string key) + { + return Get (key); + } + + /// + public string GetString (string key, string defaultValue) + { + return Get (key, defaultValue); + } + + /// + public int GetInt (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToInt32 (text, format); + } + + /// + public int GetInt (string key, bool fromAlias) + { + if (!fromAlias) { + return GetInt (key); + } + + string result = Get (key); + + if (result == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return GetIntAlias (key, result); + } + + /// + public int GetInt (string key, int defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToInt32 (result, format); + } + + /// + public int GetInt (string key, int defaultValue, bool fromAlias) + { + if (!fromAlias) { + return GetInt (key, defaultValue); + } + + string result = Get (key); + + return (result == null) ? defaultValue : GetIntAlias (key, result); + } + + /// + public long GetLong (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToInt64 (text, format); + } + + /// + public long GetLong (string key, long defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToInt64 (result, format); + } + + /// + public bool GetBoolean (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return GetBooleanAlias (text); + } + + /// + public bool GetBoolean (string key, bool defaultValue) + { + string text = Get (key); + + return (text == null) ? defaultValue : GetBooleanAlias (text); + } + + /// + public float GetFloat (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToSingle (text, format); + } + + /// + public float GetFloat (string key, float defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToSingle (result, format); + } + + /// + public double GetDouble (string key) + { + string text = Get (key); + + if (text == null) { + throw new ArgumentException ("Value not found: " + key); + } + + return Convert.ToDouble (text, format); + } + + /// + public double GetDouble (string key, double defaultValue) + { + string result = Get (key); + + return (result == null) + ? defaultValue + : Convert.ToDouble (result, format); + } + + /// + public string[] GetKeys () + { + string[] result = new string[keys.Keys.Count]; + + keys.Keys.CopyTo (result, 0); + + return result; + } + + /// + public string[] GetValues () + { + string[] result = new string[keys.Values.Count]; + + keys.Values.CopyTo (result, 0); + + return result; + } + + /// + public void Add (string key, string value) + { + keys.Add (key, value); + } + + /// + public virtual void Set (string key, object value) + { + if (value == null) { + throw new ArgumentNullException ("Value cannot be null"); + } + + if (Get (key) == null) { + this.Add (key, value.ToString ()); + } else { + keys[key] = value.ToString (); + } + + if (ConfigSource.AutoSave) { + ConfigSource.Save (); + } + + OnKeySet (new ConfigKeyEventArgs (key, value.ToString ())); + } + + /// + public virtual void Remove (string key) + { + if (key == null) { + throw new ArgumentNullException ("Key cannot be null"); + } + + if (Get (key) != null) { + string keyValue = null; + if (KeySet != null) { + keyValue = Get (key); + } + keys.Remove (key); + + OnKeyRemoved (new ConfigKeyEventArgs (key, keyValue)); + } + } + #endregion + + #region Public events + /// + public event ConfigKeyEventHandler KeySet; + + /// + public event ConfigKeyEventHandler KeyRemoved; + #endregion + + #region Protected methods + /// + protected void OnKeySet (ConfigKeyEventArgs e) + { + if (KeySet != null) { + KeySet (this, e); + } + } + + /// + protected void OnKeyRemoved (ConfigKeyEventArgs e) + { + if (KeyRemoved != null) { + KeyRemoved (this, e); + } + } + #endregion + + #region Private methods + /// + /// Renames the config to the new name. + /// + private void Rename (string name) + { + this.ConfigSource.Configs.Remove (this); + configName = name; + this.ConfigSource.Configs.Add (this); + } + + /// + /// Returns the integer alias first from this IConfig then + /// the parent if there is none. + /// + private int GetIntAlias (string key, string alias) + { + int result = -1; + + if (aliasText.ContainsInt (key, alias)) { + result = aliasText.GetInt (key, alias); + } else { + result = ConfigSource.Alias.GetInt (key, alias); + } + + return result; + } + + /// + /// Returns the boolean alias first from this IConfig then + /// the parent if there is none. + /// + private bool GetBooleanAlias (string key) + { + bool result = false; + + if (aliasText.ContainsBoolean (key)) { + result = aliasText.GetBoolean (key); + } else { + if (ConfigSource.Alias.ContainsBoolean (key)) { + result = ConfigSource.Alias.GetBoolean (key); + } else { + throw new ArgumentException + ("Alias value not found: " + key + + ". Add it to the Alias property."); + } + } + + return result; + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/ConfigCollection.cs b/ExtensionLoader/Nini/Config/ConfigCollection.cs new file mode 100644 index 00000000..6f00ffe4 --- /dev/null +++ b/ExtensionLoader/Nini/Config/ConfigCollection.cs @@ -0,0 +1,264 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; + +namespace Nini.Config +{ + #region ConfigEventHandler class + /// + public delegate void ConfigEventHandler (object sender, ConfigEventArgs e); + + /// + public class ConfigEventArgs : EventArgs + { + IConfig config = null; + + /// + public ConfigEventArgs (IConfig config) + { + this.config = config; + } + + /// + public IConfig Config + { + get { return config; } + } + } + #endregion + + /// + public class ConfigCollection : ICollection, IEnumerable, IList + { + #region Private variables + ArrayList configList = new ArrayList (); + ConfigSourceBase owner = null; + #endregion + + #region Constructors + /// + public ConfigCollection (ConfigSourceBase owner) + { + this.owner = owner; + } + #endregion + + #region Public properties + /// + public int Count + { + get { return configList.Count; } + } + + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + public object SyncRoot + { + get { return this; } + } + + /// + public IConfig this[int index] + { + get { return (IConfig)configList[index]; } + } + + /// + object IList.this[int index] + { + get { return configList[index]; } + set { } + } + + /// + public IConfig this[string configName] + { + get + { + IConfig result = null; + + foreach (IConfig config in configList) + { + if (config.Name == configName) { + result = config; + break; + } + } + + return result; + } + } + + /// + public bool IsFixedSize + { + get { return false; } + } + + /// + public bool IsReadOnly + { + get { return false; } + } + #endregion + + #region Public methods + /// + public void Add (IConfig config) + { + if (configList.Contains (config)) { + throw new ArgumentException ("IConfig already exists"); + } + IConfig existingConfig = this[config.Name]; + + if (existingConfig != null) { + // Set all new keys + string[] keys = config.GetKeys (); + for (int i = 0; i < keys.Length; i++) + { + existingConfig.Set (keys[i], config.Get (keys[i])); + } + } else { + configList.Add (config); + OnConfigAdded (new ConfigEventArgs (config)); + } + } + + /// + int IList.Add (object config) + { + IConfig newConfig = config as IConfig; + + if (newConfig == null) { + throw new Exception ("Must be an IConfig"); + } else { + this.Add (newConfig); + return IndexOf (newConfig); + } + } + + /// + public IConfig Add (string name) + { + ConfigBase result = null; + + if (this[name] == null) { + result = new ConfigBase (name, owner); + configList.Add (result); + OnConfigAdded (new ConfigEventArgs (result)); + } else { + throw new ArgumentException ("An IConfig of that name already exists"); + } + + return result; + } + + /// + public void Remove (IConfig config) + { + configList.Remove (config); + OnConfigRemoved (new ConfigEventArgs (config)); + } + + /// + public void Remove (object config) + { + configList.Remove (config); + OnConfigRemoved (new ConfigEventArgs ((IConfig)config)); + } + + /// + public void RemoveAt (int index) + { + IConfig config = (IConfig)configList[index]; + configList.RemoveAt (index); + OnConfigRemoved (new ConfigEventArgs (config)); + } + + /// + public void Clear () + { + configList.Clear (); + } + + /// + public IEnumerator GetEnumerator () + { + return configList.GetEnumerator (); + } + + /// + public void CopyTo (Array array, int index) + { + configList.CopyTo (array, index); + } + + /// + public void CopyTo (IConfig[] array, int index) + { + ((ICollection)configList).CopyTo (array, index); + } + + /// + public bool Contains (object config) + { + return configList.Contains (config); + } + + /// + public int IndexOf (object config) + { + return configList.IndexOf (config); + } + + /// + public void Insert (int index, object config) + { + configList.Insert (index, config); + } + #endregion + + #region Public events + /// + public event ConfigEventHandler ConfigAdded; + + /// + public event ConfigEventHandler ConfigRemoved; + #endregion + + #region Protected methods + /// + protected void OnConfigAdded (ConfigEventArgs e) + { + if (ConfigAdded != null) { + ConfigAdded (this, e); + } + } + + /// + protected void OnConfigRemoved (ConfigEventArgs e) + { + if (ConfigRemoved != null) { + ConfigRemoved (this, e); + } + } + #endregion + + #region Private methods + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/ConfigSourceBase.cs b/ExtensionLoader/Nini/Config/ConfigSourceBase.cs new file mode 100644 index 00000000..516739f8 --- /dev/null +++ b/ExtensionLoader/Nini/Config/ConfigSourceBase.cs @@ -0,0 +1,219 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Text; +using System.Collections; + +namespace Nini.Config +{ + /// + public abstract class ConfigSourceBase : IConfigSource + { + #region Private variables + ArrayList sourceList = new ArrayList (); + ConfigCollection configList = null; + bool autoSave = false; + AliasText alias = new AliasText (); + #endregion + + #region Constructors + /// + public ConfigSourceBase () + { + configList = new ConfigCollection (this); + } + #endregion + + #region Public properties + /// + public ConfigCollection Configs + { + get { return configList; } + } + + /// + public bool AutoSave + { + get { return autoSave; } + set { autoSave = value; } + } + + /// + public AliasText Alias + { + get { return alias; } + } + #endregion + + #region Public methods + /// + public void Merge (IConfigSource source) + { + if (!sourceList.Contains (source)) { + sourceList.Add (source); + } + + foreach (IConfig config in source.Configs) + { + this.Configs.Add (config); + } + } + + /// + public virtual IConfig AddConfig (string name) + { + return configList.Add (name); + } + + /// + public string GetExpanded (IConfig config, string key) + { + return Expand (config, key, false); + } + + /// + public virtual void Save () + { + OnSaved (new EventArgs ()); + } + + /// + public virtual void Reload () + { + OnReloaded (new EventArgs ()); + } + + /// + public void ExpandKeyValues () + { + string[] keys = null; + + foreach (IConfig config in configList) + { + keys = config.GetKeys (); + for (int i = 0; i < keys.Length; i++) + { + Expand (config, keys[i], true); + } + } + } + + /// + public void ReplaceKeyValues () + { + ExpandKeyValues (); + } + #endregion + + #region Public events + /// + public event EventHandler Reloaded; + + /// + public event EventHandler Saved; + #endregion + + #region Protected methods + /// + protected void OnReloaded (EventArgs e) + { + if (Reloaded != null) { + Reloaded (this, e); + } + } + + /// + protected void OnSaved (EventArgs e) + { + if (Saved != null) { + Saved (this, e); + } + } + #endregion + + #region Private methods + /// + /// Expands key values from the given IConfig. + /// + private string Expand (IConfig config, string key, bool setValue) + { + string result = config.Get (key); + if (result == null) { + throw new ArgumentException (String.Format ("[{0}] not found in [{1}]", + key, config.Name)); + } + + while (true) + { + int startIndex = result.IndexOf ("${", 0); + if (startIndex == -1) { + break; + } + + int endIndex = result.IndexOf ("}", startIndex + 2); + if (endIndex == -1) { + break; + } + + string search = result.Substring (startIndex + 2, + endIndex - (startIndex + 2)); + + if (search == key) { + // Prevent infinite recursion + throw new ArgumentException + ("Key cannot have a expand value of itself: " + key); + } + + string replace = ExpandValue (config, search); + + result = result.Replace("${" + search + "}", replace); + } + + if (setValue) { + config.Set(key, result); + } + + return result; + } + + /// + /// Returns the replacement value of a config. + /// + private string ExpandValue (IConfig config, string search) + { + string result = null; + + string[] replaces = search.Split ('|'); + + if (replaces.Length > 1) { + IConfig newConfig = this.Configs[replaces[0]]; + if (newConfig == null) { + throw new ArgumentException ("Expand config not found: " + + replaces[0]); + } + result = newConfig.Get (replaces[1]); + if (result == null) { + throw new ArgumentException ("Expand key not found: " + + replaces[1]); + } + } else { + result = config.Get (search); + + if (result == null) { + throw new ArgumentException ("Key not found: " + search); + } + } + + return result; + } + #endregion + } +} diff --git a/ExtensionLoader/Nini/Config/IConfig.cs b/ExtensionLoader/Nini/Config/IConfig.cs new file mode 100644 index 00000000..8f916223 --- /dev/null +++ b/ExtensionLoader/Nini/Config/IConfig.cs @@ -0,0 +1,99 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; + +namespace Nini.Config +{ + /// + public interface IConfig + { + /// + IConfigSource ConfigSource { get; } + + /// + string Name { get; set; } + + /// + AliasText Alias { get; } + + /// + bool Contains (string key); + + /// + string Get (string key); + + /// + string Get (string key, string defaultValue); + + /// + string GetExpanded (string key); + + /// + string GetString (string key); + + /// + string GetString (string key, string defaultValue); + + /// + int GetInt (string key); + + /// + int GetInt (string key, bool fromAlias); + + /// + int GetInt (string key, int defaultValue); + + /// + int GetInt (string key, int defaultValue, bool fromAlias); + + /// + long GetLong (string key); + + /// + long GetLong (string key, long defaultValue); + + /// + bool GetBoolean (string key); + + /// + bool GetBoolean (string key, bool defaultValue); + + /// + float GetFloat (string key); + + /// + float GetFloat (string key, float defaultValue); + + /// + double GetDouble (string key); + + /// + double GetDouble (string key, double defaultValue); + + /// + string[] GetKeys (); + + /// + string[] GetValues (); + + /// + void Set (string key, object value); + + /// + void Remove (string key); + + /// + event ConfigKeyEventHandler KeySet; + + /// + event ConfigKeyEventHandler KeyRemoved; + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/IConfigSource.cs b/ExtensionLoader/Nini/Config/IConfigSource.cs new file mode 100644 index 00000000..eb0be2fc --- /dev/null +++ b/ExtensionLoader/Nini/Config/IConfigSource.cs @@ -0,0 +1,55 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; + +namespace Nini.Config +{ + /// + public interface IConfigSource + { + /// + ConfigCollection Configs { get; } + + /// + bool AutoSave { get; set; } + + /// + AliasText Alias { get; } + + /// + void Merge (IConfigSource source); + + /// + void Save (); + + /// + void Reload (); + + /// + IConfig AddConfig (string name); + + /// + string GetExpanded (IConfig config, string key); + + /// + void ExpandKeyValues (); + + /// + void ReplaceKeyValues (); + + /// + event EventHandler Reloaded; + + /// + event EventHandler Saved; + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/IniConfig.cs b/ExtensionLoader/Nini/Config/IniConfig.cs new file mode 100644 index 00000000..0d4f50d3 --- /dev/null +++ b/ExtensionLoader/Nini/Config/IniConfig.cs @@ -0,0 +1,90 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using System.Globalization; +using Nini.Util; + +namespace Nini.Config +{ + /// + public class IniConfig : ConfigBase + { + #region Private variables + IniConfigSource parent = null; + #endregion + + #region Constructors + /// + public IniConfig (string name, IConfigSource source) + : base(name, source) + { + parent = (IniConfigSource)source; + } + #endregion + + #region Public properties + #endregion + + #region Public methods + /// + public override string Get (string key) + { + if (!parent.CaseSensitive) { + key = CaseInsensitiveKeyName (key); + } + + return base.Get (key); + } + + /// + public override void Set (string key, object value) + { + if (!parent.CaseSensitive) { + key = CaseInsensitiveKeyName (key); + } + + base.Set (key, value); + } + + /// + public override void Remove (string key) + { + if (!parent.CaseSensitive) { + key = CaseInsensitiveKeyName (key); + } + + base.Remove (key); + } + #endregion + + #region Private methods + /// + /// Returns the key name if the case insensitivity is turned on. + /// + private string CaseInsensitiveKeyName (string key) + { + string result = null; + + string lowerKey = key.ToLower (); + foreach (string currentKey in keys.Keys) + { + if (currentKey.ToLower () == lowerKey) { + result = currentKey; + break; + } + } + + return (result == null) ? key : result; + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Config/IniConfigSource.cs b/ExtensionLoader/Nini/Config/IniConfigSource.cs new file mode 100644 index 00000000..dfff2ac5 --- /dev/null +++ b/ExtensionLoader/Nini/Config/IniConfigSource.cs @@ -0,0 +1,329 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Collections; +using Nini.Ini; + +namespace Nini.Config +{ + /// + public class IniConfigSource : ConfigSourceBase + { + #region Private variables + IniDocument iniDocument = null; + string savePath = null; + bool caseSensitive = true; + #endregion + + #region Public properties + #endregion + + #region Constructors + /// + public IniConfigSource () + { + iniDocument = new IniDocument (); + } + + /// + public IniConfigSource (string filePath) + { + Load (filePath); + } + + /// + public IniConfigSource (TextReader reader) + { + Load (reader); + } + + /// + public IniConfigSource (IniDocument document) + { + Load (document); + } + + /// + public IniConfigSource (Stream stream) + { + Load (stream); + } + #endregion + + #region Public properties + /// + public bool CaseSensitive + { + get { return caseSensitive; } + set { caseSensitive = value; } + } + + /// + public string SavePath + { + get { return savePath; } + } + #endregion + + #region Public methods + /// + public void Load (string filePath) + { + Load (new StreamReader (filePath)); + this.savePath = filePath; + } + + /// + public void Load (TextReader reader) + { + Load (new IniDocument (reader)); + } + + /// + public void Load (IniDocument document) + { + this.Configs.Clear (); + + this.Merge (this); // required for SaveAll + iniDocument = document; + Load (); + } + + /// + public void Load (Stream stream) + { + Load (new StreamReader (stream)); + } + + /// + public override void Save () + { + if (!IsSavable ()) { + throw new ArgumentException ("Source cannot be saved in this state"); + } + + MergeConfigsIntoDocument (); + + iniDocument.Save (this.savePath); + base.Save (); + } + + /// + public void Save (string path) + { + this.savePath = path; + this.Save (); + } + + /// + public void Save (TextWriter writer) + { + MergeConfigsIntoDocument (); + iniDocument.Save (writer); + savePath = null; + OnSaved (new EventArgs ()); + } + + /// + public void Save (Stream stream) + { + MergeConfigsIntoDocument (); + iniDocument.Save (stream); + savePath = null; + OnSaved (new EventArgs ()); + } + + /// + public override void Reload () + { + if (savePath == null) { + throw new ArgumentException ("Error reloading: You must have " + + "the loaded the source from a file"); + } + + iniDocument = new IniDocument (savePath); + MergeDocumentIntoConfigs (); + base.Reload (); + } + + /// + public override string ToString () + { + MergeConfigsIntoDocument (); + StringWriter writer = new StringWriter (); + iniDocument.Save (writer); + + return writer.ToString (); + } + #endregion + + #region Private methods + /// + /// Merges all of the configs from the config collection into the + /// IniDocument before it is saved. + /// + private void MergeConfigsIntoDocument () + { + RemoveSections (); + foreach (IConfig config in this.Configs) + { + string[] keys = config.GetKeys (); + + // Create a new section if one doesn't exist + if (iniDocument.Sections[config.Name] == null) { + IniSection section = new IniSection (config.Name); + iniDocument.Sections.Add (section); + } + RemoveKeys (config.Name); + + for (int i = 0; i < keys.Length; i++) + { + iniDocument.Sections[config.Name].Set (keys[i], config.Get (keys[i])); + } + } + } + + /// + /// Removes all INI sections that were removed as configs. + /// + private void RemoveSections () + { + IniSection section = null; + for (int i = 0; i < iniDocument.Sections.Count; i++) + { + section = iniDocument.Sections[i]; + if (this.Configs[section.Name] == null) { + iniDocument.Sections.Remove (section.Name); + } + } + } + + /// + /// Removes all INI keys that were removed as config keys. + /// + private void RemoveKeys (string sectionName) + { + IniSection section = iniDocument.Sections[sectionName]; + + if (section != null) { + foreach (string key in section.GetKeys ()) + { + if (this.Configs[sectionName].Get (key) == null) { + section.Remove (key); + } + } + } + } + + /// + /// Loads the configuration file. + /// + private void Load () + { + IniConfig config = null; + IniSection section = null; + IniItem item = null; + + for (int j = 0; j < iniDocument.Sections.Count; j++) + { + section = iniDocument.Sections[j]; + config = new IniConfig (section.Name, this); + + for (int i = 0; i < section.ItemCount; i++) + { + item = section.GetItem (i); + + if (item.Type == IniType.Key) { + config.Add (item.Name, item.Value); + } + } + + this.Configs.Add (config); + } + } + + /// + /// Merges the IniDocument into the Configs when the document is + /// reloaded. + /// + private void MergeDocumentIntoConfigs () + { + // Remove all missing configs first + RemoveConfigs (); + + IniSection section = null; + for (int i = 0; i < iniDocument.Sections.Count; i++) + { + section = iniDocument.Sections[i]; + + IConfig config = this.Configs[section.Name]; + if (config == null) { + // The section is new so add it + config = new ConfigBase (section.Name, this); + this.Configs.Add (config); + } + RemoveConfigKeys (config); + } + } + + /// + /// Removes all configs that are not in the newly loaded INI doc. + /// + private void RemoveConfigs () + { + IConfig config = null; + for (int i = this.Configs.Count - 1; i > -1; i--) + { + config = this.Configs[i]; + // If the section is not present in the INI doc + if (iniDocument.Sections[config.Name] == null) { + this.Configs.Remove (config); + } + } + } + + /// + /// Removes all INI keys that were removed as config keys. + /// + private void RemoveConfigKeys (IConfig config) + { + IniSection section = iniDocument.Sections[config.Name]; + + // Remove old keys + string[] configKeys = config.GetKeys (); + foreach (string configKey in configKeys) + { + if (!section.Contains (configKey)) { + // Key doesn't exist, remove + config.Remove (configKey); + } + } + + // Add or set all new keys + string[] keys = section.GetKeys (); + for (int i = 0; i < keys.Length; i++) + { + string key = keys[i]; + config.Set (key, section.GetItem (i).Value); + } + } + + /// + /// Returns true if this instance is savable. + /// + private bool IsSavable () + { + return (this.savePath != null); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Ini/IniDocument.cs b/ExtensionLoader/Nini/Ini/IniDocument.cs new file mode 100644 index 00000000..653876c5 --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniDocument.cs @@ -0,0 +1,302 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Collections; +using Nini.Util; + +namespace Nini.Ini +{ + #region IniFileType enumeration + /// + public enum IniFileType + { + /// + Standard, + /// + PythonStyle, + /// + SambaStyle, + /// + MysqlStyle, + /// + WindowsStyle + } + #endregion + + /// + public class IniDocument + { + #region Private variables + IniSectionCollection sections = new IniSectionCollection (); + ArrayList initialComment = new ArrayList (); + IniFileType fileType = IniFileType.Standard; + #endregion + + #region Public properties + /// + public IniFileType FileType + { + get { return fileType; } + set { fileType = value; } + } + #endregion + + #region Constructors + /// + public IniDocument (string filePath) + { + fileType = IniFileType.Standard; + Load (filePath); + } + + /// + public IniDocument (string filePath, IniFileType type) + { + fileType = type; + Load (filePath); + } + + /// + public IniDocument (TextReader reader) + { + fileType = IniFileType.Standard; + Load (reader); + } + + /// + public IniDocument (TextReader reader, IniFileType type) + { + fileType = type; + Load (reader); + } + + /// + public IniDocument (Stream stream) + { + fileType = IniFileType.Standard; + Load (stream); + } + + /// + public IniDocument (Stream stream, IniFileType type) + { + fileType = type; + Load (stream); + } + + /// + public IniDocument (IniReader reader) + { + fileType = IniFileType.Standard; + Load (reader); + } + + /// + public IniDocument () + { + } + #endregion + + #region Public methods + /// + public void Load (string filePath) + { + Load (new StreamReader (filePath)); + } + + /// + public void Load (TextReader reader) + { + Load (GetIniReader (reader, fileType)); + } + + /// + public void Load (Stream stream) + { + Load (new StreamReader (stream)); + } + + /// + public void Load (IniReader reader) + { + LoadReader (reader); + } + + /// + public IniSectionCollection Sections + { + get { return sections; } + } + + /// + public void Save (TextWriter textWriter) + { + IniWriter writer = GetIniWriter (textWriter, fileType); + IniItem item = null; + IniSection section = null; + + foreach (string comment in initialComment) + { + writer.WriteEmpty (comment); + } + + for (int j = 0; j < sections.Count; j++) + { + section = sections[j]; + writer.WriteSection (section.Name, section.Comment); + for (int i = 0; i < section.ItemCount; i++) + { + item = section.GetItem (i); + switch (item.Type) + { + case IniType.Key: + writer.WriteKey (item.Name, item.Value, item.Comment); + break; + case IniType.Empty: + writer.WriteEmpty (item.Comment); + break; + } + } + } + + writer.Close (); + } + + /// + public void Save (string filePath) + { + StreamWriter writer = new StreamWriter (filePath); + Save (writer); + writer.Close (); + } + + /// + public void Save (Stream stream) + { + Save (new StreamWriter (stream)); + } + #endregion + + #region Private methods + /// + /// Loads the file not saving comments. + /// + private void LoadReader (IniReader reader) + { + reader.IgnoreComments = false; + bool sectionFound = false; + IniSection section = null; + + try { + while (reader.Read ()) + { + switch (reader.Type) + { + case IniType.Empty: + if (!sectionFound) { + initialComment.Add (reader.Comment); + } else { + section.Set (reader.Comment); + } + + break; + case IniType.Section: + sectionFound = true; + // If section already exists then overwrite it + if (sections[reader.Name] != null) { + sections.Remove (reader.Name); + } + section = new IniSection (reader.Name, reader.Comment); + sections.Add (section); + + break; + case IniType.Key: + if (section.GetValue (reader.Name) == null) { + section.Set (reader.Name, reader.Value, reader.Comment); + } + break; + } + } + } catch (Exception ex) { + throw ex; + } finally { + // Always close the file + reader.Close (); + } + } + + /// + /// Returns a proper INI reader depending upon the type parameter. + /// + private IniReader GetIniReader (TextReader reader, IniFileType type) + { + IniReader result = new IniReader (reader); + + switch (type) + { + case IniFileType.Standard: + // do nothing + break; + case IniFileType.PythonStyle: + result.AcceptCommentAfterKey = false; + result.SetCommentDelimiters (new char[] { ';', '#' }); + result.SetAssignDelimiters (new char[] { ':' }); + break; + case IniFileType.SambaStyle: + result.AcceptCommentAfterKey = false; + result.SetCommentDelimiters (new char[] { ';', '#' }); + result.LineContinuation = true; + break; + case IniFileType.MysqlStyle: + result.AcceptCommentAfterKey = false; + result.AcceptNoAssignmentOperator = true; + result.SetCommentDelimiters (new char[] { '#' }); + result.SetAssignDelimiters (new char[] { ':', '=' }); + break; + case IniFileType.WindowsStyle: + result.ConsumeAllKeyText = true; + break; + } + + return result; + } + + /// + /// Returns a proper IniWriter depending upon the type parameter. + /// + private IniWriter GetIniWriter (TextWriter reader, IniFileType type) + { + IniWriter result = new IniWriter (reader); + + switch (type) + { + case IniFileType.Standard: + case IniFileType.WindowsStyle: + // do nothing + break; + case IniFileType.PythonStyle: + result.AssignDelimiter = ':'; + result.CommentDelimiter = '#'; + break; + case IniFileType.SambaStyle: + case IniFileType.MysqlStyle: + result.AssignDelimiter = '='; + result.CommentDelimiter = '#'; + break; + } + + return result; + } + #endregion + } +} + diff --git a/ExtensionLoader/Nini/Ini/IniException.cs b/ExtensionLoader/Nini/Ini/IniException.cs new file mode 100644 index 00000000..c627b055 --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniException.cs @@ -0,0 +1,121 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Security; +using System.Globalization; +using System.Security.Permissions; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; + + +namespace Nini.Ini +{ + /// +#if (NET_COMPACT_1_0) +#else + [Serializable] +#endif + public class IniException : SystemException /*, ISerializable */ + { + #region Private variables + IniReader iniReader = null; + string message = ""; + #endregion + + #region Public properties + /// + public int LinePosition + { + get { + return (iniReader == null) ? 0 : iniReader.LinePosition; + } + } + + /// + public int LineNumber + { + get { + return (iniReader == null) ? 0 : iniReader.LineNumber; + } + } + + /// + public override string Message + { + get { + if (iniReader == null) { + return base.Message; + } + + return String.Format (CultureInfo.InvariantCulture, "{0} - Line: {1}, Position: {2}.", + message, this.LineNumber, this.LinePosition); + } + } + #endregion + + #region Constructors + /// + public IniException () + : base () + { + this.message = "An error has occurred"; + } + + /// + public IniException (string message, Exception exception) + : base (message, exception) + { + } + + /// + public IniException (string message) + : base (message) + { + this.message = message; + } + + /// + internal IniException (IniReader reader, string message) + : this (message) + { + iniReader = reader; + this.message = message; + } + +#if (NET_COMPACT_1_0) +#else + /// + protected IniException (SerializationInfo info, StreamingContext context) + : base (info, context) + { + } +#endif + #endregion + + #region Public methods +#if (NET_COMPACT_1_0) +#else + /// + [SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)] + public override void GetObjectData (SerializationInfo info, + StreamingContext context) + { + base.GetObjectData (info, context); + if (iniReader != null) { + info.AddValue ("lineNumber", iniReader.LineNumber); + + info.AddValue ("linePosition", iniReader.LinePosition); + } + } +#endif + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Ini/IniItem.cs b/ExtensionLoader/Nini/Ini/IniItem.cs new file mode 100644 index 00000000..f11a6910 --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniItem.cs @@ -0,0 +1,54 @@ +using System; + +namespace Nini.Ini +{ + /// + public class IniItem + { + #region Private variables + IniType iniType = IniType.Empty; + string iniName = ""; + string iniValue = ""; + string iniComment = null; + #endregion + + #region Public properties + /// + public IniType Type + { + get { return iniType; } + set { iniType = value; } + } + + /// + public string Value + { + get { return iniValue; } + set { iniValue = value; } + } + + /// + public string Name + { + get { return iniName; } + } + + /// + public string Comment + { + get { return iniComment; } + set { iniComment = value; } + } + #endregion + + /// + internal protected IniItem (string name, string value, IniType type, string comment) + { + iniName = name; + iniValue = value; + iniType = type; + iniComment = comment; + } + } +} + diff --git a/ExtensionLoader/Nini/Ini/IniReader.cs b/ExtensionLoader/Nini/Ini/IniReader.cs new file mode 100644 index 00000000..14b441a5 --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniReader.cs @@ -0,0 +1,652 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Text; +using System.Collections; + +namespace Nini.Ini +{ + #region IniReadState enumeration + /// + public enum IniReadState : int + { + /// + Closed, + /// + EndOfFile, + /// + Error, + /// + Initial, + /// + Interactive + }; + #endregion + + #region IniType enumeration + /// + public enum IniType : int + { + /// + Section, + /// + Key, + /// + Empty + } + #endregion + + /// + public class IniReader : IDisposable + { + #region Private variables + int lineNumber = 1; + int column = 1; + IniType iniType = IniType.Empty; + TextReader textReader = null; + bool ignoreComments = false; + StringBuilder name = new StringBuilder (); + StringBuilder value = new StringBuilder (); + StringBuilder comment = new StringBuilder (); + IniReadState readState = IniReadState.Initial; + bool hasComment = false; + bool disposed = false; + bool lineContinuation = false; + bool acceptCommentAfterKey = true; + bool acceptNoAssignmentOperator = false; + bool consumeAllKeyText = false; + char[] commentDelimiters = new char[] { ';' }; + char[] assignDelimiters = new char[] { '=' }; + #endregion + + #region Public properties + /// + public string Name + { + get { return this.name.ToString (); } + } + + /// + public string Value + { + get { return this.value.ToString (); } + } + + /// + public IniType Type + { + get { return iniType; } + } + + /// + public string Comment + { + get { return (hasComment) ? this.comment.ToString () : null; } + } + + /// + public int LineNumber + { + get { return lineNumber; } + } + + /// + public int LinePosition + { + get { return column; } + } + + /// + public bool IgnoreComments + { + get { return ignoreComments; } + set { ignoreComments = value; } + } + + /// + public IniReadState ReadState + { + get { return readState; } + } + + /// + public bool LineContinuation + { + get { return lineContinuation; } + set { lineContinuation = value; } + } + + /// + public bool AcceptCommentAfterKey + { + get { return acceptCommentAfterKey; } + set { acceptCommentAfterKey = value; } + } + + /// + public bool AcceptNoAssignmentOperator + { + get { return acceptNoAssignmentOperator; } + set { acceptNoAssignmentOperator = value; } + } + + /// + public bool ConsumeAllKeyText + { + get { return consumeAllKeyText; } + set { consumeAllKeyText = value; } + } + #endregion + + #region Constructors + /// + public IniReader (string filePath) + { + textReader = new StreamReader (filePath); + } + + /// + public IniReader (TextReader reader) + { + textReader = reader; + } + + /// + public IniReader (Stream stream) + : this (new StreamReader (stream)) + { + } + #endregion + + #region Public methods + /// + public bool Read () + { + bool result = false; + + if (readState != IniReadState.EndOfFile + || readState != IniReadState.Closed) { + readState = IniReadState.Interactive; + result = ReadNext (); + } + + return result; + } + + /// + public bool MoveToNextSection () + { + bool result = false; + + while (true) + { + result = Read (); + + if (iniType == IniType.Section || !result) { + break; + } + } + + return result; + } + + /// + public bool MoveToNextKey () + { + bool result = false; + + while (true) + { + result = Read (); + + if (iniType == IniType.Section) { + result = false; + break; + } + if (iniType == IniType.Key || !result) { + break; + } + } + + return result; + } + + /// + public void Close () + { + Reset (); + readState = IniReadState.Closed; + + if (textReader != null) { + textReader.Close (); + } + } + + /// + public void Dispose () + { + Dispose (true); + } + + /// + public char[] GetCommentDelimiters () + { + char[] result = new char[commentDelimiters.Length]; + Array.Copy (commentDelimiters, 0, result, 0, commentDelimiters.Length); + + return result; + } + + /// + public void SetCommentDelimiters (char[] delimiters) + { + if (delimiters.Length < 1) { + throw new ArgumentException ("Must supply at least one delimiter"); + } + + commentDelimiters = delimiters; + } + + /// + public char[] GetAssignDelimiters () + { + char[] result = new char[assignDelimiters.Length]; + Array.Copy (assignDelimiters, 0, result, 0, assignDelimiters.Length); + + return result; + } + + /// + public void SetAssignDelimiters (char[] delimiters) + { + if (delimiters.Length < 1) { + throw new ArgumentException ("Must supply at least one delimiter"); + } + + assignDelimiters = delimiters; + } + #endregion + + #region Protected methods + /// + protected virtual void Dispose (bool disposing) + { + if (!disposed) { + textReader.Close (); + disposed = true; + + if (disposing) { + GC.SuppressFinalize (this); + } + } + } + #endregion + + #region Private methods + /// + /// Destructor. + /// + ~IniReader () + { + Dispose (false); + } + + /// + /// Resets all of the current INI line data. + /// + private void Reset () + { + this.name.Remove (0, this.name.Length); + this.value.Remove (0, this.value.Length); + this.comment.Remove (0, this.comment.Length); + iniType = IniType.Empty; + hasComment = false; + } + + /// + /// Reads the next INI line item. + /// + private bool ReadNext () + { + bool result = true; + int ch = PeekChar (); + Reset (); + + if (IsComment (ch)) { + iniType = IniType.Empty; + ReadChar (); // consume comment character + ReadComment (); + + return result; + } + + switch (ch) + { + case ' ': + case '\t': + case '\r': + SkipWhitespace (); + ReadNext (); + break; + case '\n': + ReadChar (); + break; + case '[': + ReadSection (); + break; + case -1: + readState = IniReadState.EndOfFile; + result = false; + break; + default: + ReadKey (); + break; + } + + return result; + } + + /// + /// Reads a comment. Must start after the comment delimiter. + /// + private void ReadComment () + { + int ch = -1; + SkipWhitespace (); + hasComment = true; + + do + { + ch = ReadChar (); + this.comment.Append ((char)ch); + } while (!EndOfLine (ch)); + + RemoveTrailingWhitespace (this.comment); + } + + /// + /// Removes trailing whitespace from a StringBuilder. + /// + private void RemoveTrailingWhitespace (StringBuilder builder) + { + string temp = builder.ToString (); + + builder.Remove (0, builder.Length); + builder.Append (temp.TrimEnd (null)); + } + + /// + /// Reads a key. + /// + private void ReadKey () + { + int ch = -1; + iniType = IniType.Key; + + while (true) + { + ch = PeekChar (); + + if (IsAssign (ch)) { + ReadChar (); + break; + } + + if (EndOfLine (ch)) { + if (acceptNoAssignmentOperator) { + break; + } + throw new IniException (this, + String.Format ("Expected assignment operator ({0})", + assignDelimiters[0])); + } + + this.name.Append ((char)ReadChar ()); + } + + ReadKeyValue (); + SearchForComment (); + RemoveTrailingWhitespace (this.name); + } + + /// + /// Reads the value of a key. + /// + private void ReadKeyValue () + { + int ch = -1; + bool foundQuote = false; + int characters = 0; + SkipWhitespace (); + + while (true) + { + ch = PeekChar (); + + if (!IsWhitespace (ch)) { + characters++; + } + + if (!this.ConsumeAllKeyText && ch == '"') { + ReadChar (); + + if (!foundQuote && characters == 1) { + foundQuote = true; + continue; + } else { + break; + } + } + + if (foundQuote && EndOfLine (ch)) { + throw new IniException (this, "Expected closing quote (\")"); + } + + // Handle line continuation + if (lineContinuation && ch == '\\') + { + StringBuilder buffer = new StringBuilder (); + buffer.Append ((char)ReadChar ()); // append '\' + + while (PeekChar () != '\n' && IsWhitespace (PeekChar ())) + { + if (PeekChar () != '\r') { + buffer.Append ((char)ReadChar ()); + } else { + ReadChar (); // consume '\r' + } + } + + if (PeekChar () == '\n') { + // continue reading key value on next line + ReadChar (); + continue; + } else { + // Replace consumed characters + this.value.Append (buffer.ToString ()); + } + } + + if (!this.ConsumeAllKeyText) { + // If accepting comments then don't consume as key value + if (acceptCommentAfterKey && IsComment (ch) && !foundQuote) { + break; + } + } + + // Always break at end of line + if (EndOfLine (ch)) { + break; + } + + this.value.Append ((char)ReadChar ()); + } + + if (!foundQuote) { + RemoveTrailingWhitespace (this.value); + } + } + + /// + /// Reads an INI section. + /// + private void ReadSection () + { + int ch = -1; + iniType = IniType.Section; + ch = ReadChar (); // consume "[" + + while (true) + { + ch = PeekChar (); + if (ch == ']') { + break; + } + if (EndOfLine (ch)) { + throw new IniException (this, "Expected section end (])"); + } + + this.name.Append ((char)ReadChar ()); + } + + ConsumeToEnd (); // all after '[' is garbage + RemoveTrailingWhitespace (this.name); + } + + /// + /// Looks for a comment. + /// + private void SearchForComment () + { + int ch = ReadChar (); + + while (!EndOfLine (ch)) + { + if (IsComment (ch)) { + if (ignoreComments) { + ConsumeToEnd (); + } else { + ReadComment (); + } + break; + } + ch = ReadChar (); + } + } + + /// + /// Consumes all data until the end of a line. + /// + private void ConsumeToEnd () + { + int ch = -1; + + do + { + ch = ReadChar (); + } while (!EndOfLine (ch)); + } + + /// + /// Returns and consumes the next character from the stream. + /// + private int ReadChar () + { + int result = textReader.Read (); + + if (result == '\n') { + lineNumber++; + column = 1; + } else { + column++; + } + + return result; + } + + /// + /// Returns the next upcoming character from the stream. + /// + private int PeekChar () + { + return textReader.Peek (); + } + + /// + /// Returns true if a comment character is found. + /// + private bool IsComment (int ch) + { + return HasCharacter (commentDelimiters, ch); + } + + /// + /// Returns true if character is an assign character. + /// + private bool IsAssign (int ch) + { + return HasCharacter (assignDelimiters, ch); + } + + /// + /// Returns true if the character is found in the given array. + /// + private bool HasCharacter (char[] characters, int ch) + { + bool result = false; + + for (int i = 0; i < characters.Length; i++) + { + if (ch == characters[i]) + { + result = true; + break; + } + } + + return result; + } + + /// + /// Returns true if a value is whitespace. + /// + private bool IsWhitespace (int ch) + { + return ch == 0x20 || ch == 0x9 || ch == 0xD || ch == 0xA; + } + + /// + /// Skips all whitespace. + /// + private void SkipWhitespace () + { + while (IsWhitespace (PeekChar ())) + { + if (EndOfLine (PeekChar ())) { + break; + } + + ReadChar (); + } + } + + /// + /// Returns true if an end of line is found. End of line + /// includes both an end of line or end of file. + /// + private bool EndOfLine (int ch) + { + return (ch == '\n' || ch == -1); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Ini/IniSection.cs b/ExtensionLoader/Nini/Ini/IniSection.cs new file mode 100644 index 00000000..9f86e71b --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniSection.cs @@ -0,0 +1,155 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using Nini.Util; + +namespace Nini.Ini +{ + /// + public class IniSection + { + #region Private variables + OrderedList configList = new OrderedList (); + string name = ""; + string comment = null; + int commentCount = 0; + #endregion + + #region Constructors + /// + public IniSection (string name, string comment) + { + this.name = name; + this.comment = comment; + } + + /// + public IniSection (string name) + : this (name, null) + { + } + #endregion + + #region Public properties + /// + public string Name + { + get { return name; } + } + + /// + public string Comment + { + get { return comment; } + } + + /// + public int ItemCount + { + get { return configList.Count; } + } + #endregion + + #region Public methods + + /// + public string GetValue (string key) + { + string result = null; + + if (Contains (key)) { + IniItem item = (IniItem)configList[key]; + result = item.Value; + } + + return result; + } + + /// + public IniItem GetItem (int index) + { + return (IniItem)configList[index]; + } + + /// + public string[] GetKeys () + { + ArrayList list = new ArrayList (); + IniItem item = null; + + for (int i = 0; i < configList.Count; i++) + { + item = (IniItem)configList[i]; + if (item.Type == IniType.Key) { + list.Add (item.Name); + } + } + string[] result = new string[list.Count]; + list.CopyTo (result, 0); + + return result; + } + + /// + public bool Contains (string key) + { + return (configList[key] != null); + } + + /// + public void Set (string key, string value, string comment) + { + IniItem item = null; + + if (Contains (key)) { + item = (IniItem)configList[key]; + item.Value = value; + item.Comment = comment; + } else { + item = new IniItem (key, value, IniType.Key, comment); + configList.Add (key, item); + } + } + + /// + public void Set (string key, string value) + { + Set (key, value, null); + } + + /// + public void Set (string comment) + { + string name = "#comment" + commentCount; + IniItem item = new IniItem (name, null, + IniType.Empty, comment); + configList.Add (name, item); + + commentCount++; + } + + /// + public void Set () + { + Set (null); + } + + /// + public void Remove (string key) + { + if (Contains (key)) { + configList.Remove (key); + } + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Ini/IniSectionCollection.cs b/ExtensionLoader/Nini/Ini/IniSectionCollection.cs new file mode 100644 index 00000000..e303d0ff --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniSectionCollection.cs @@ -0,0 +1,95 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.Collections; +using Nini.Util; + +namespace Nini.Ini +{ + /// + public class IniSectionCollection : ICollection, IEnumerable + { + #region Private variables + OrderedList list = new OrderedList (); + #endregion + + #region Public properties + /// + public IniSection this[int index] + { + get { return (IniSection)list[index]; } + } + + /// + public IniSection this[string configName] + { + get { return (IniSection)list[configName]; } + } + + /// + public int Count + { + get { return list.Count; } + } + + /// + public object SyncRoot + { + get { return list.SyncRoot; } + } + + /// + public bool IsSynchronized + { + get { return list.IsSynchronized; } + } + #endregion + + #region Public methods + /// + public void Add (IniSection section) + { + if (list.Contains (section)) { + throw new ArgumentException ("IniSection already exists"); + } + + list.Add (section.Name, section); + } + + /// + public void Remove (string config) + { + list.Remove (config); + } + + /// + public void CopyTo (Array array, int index) + { + list.CopyTo (array, index); + } + + /// + public void CopyTo (IniSection[] array, int index) + { + ((ICollection)list).CopyTo (array, index); + } + + /// + public IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + #endregion + + #region Private methods + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Ini/IniWriter.cs b/ExtensionLoader/Nini/Ini/IniWriter.cs new file mode 100644 index 00000000..cdeeb0a1 --- /dev/null +++ b/ExtensionLoader/Nini/Ini/IniWriter.cs @@ -0,0 +1,308 @@ +#region Copyright +// +// Nini Configuration Project. +// Copyright (C) 2006 Brent R. Matzelle. All rights reserved. +// +// This software is published under the terms of the MIT X11 license, a copy of +// which has been included with this distribution in the LICENSE.txt file. +// +#endregion + +using System; +using System.IO; +using System.Text; + +namespace Nini.Ini +{ + #region IniWriteState enumeration + /// + public enum IniWriteState : int + { + /// + Start, + /// + BeforeFirstSection, + /// + Section, + /// + Closed + }; + #endregion + + /// + public class IniWriter : IDisposable + { + #region Private variables + int indentation = 0; + bool useValueQuotes = false; + IniWriteState writeState = IniWriteState.Start; + char commentDelimiter = ';'; + char assignDelimiter = '='; + TextWriter textWriter = null; + string eol = "\r\n"; + StringBuilder indentationBuffer = new StringBuilder (); + Stream baseStream = null; + bool disposed = false; + #endregion + + #region Public properties + /// + public int Indentation + { + get { return indentation; } + set + { + if (value < 0) + throw new ArgumentException ("Negative values are illegal"); + + indentation = value; + indentationBuffer.Remove(0, indentationBuffer.Length); + for (int i = 0; i < value; i++) + indentationBuffer.Append (' '); + } + } + + /// + public bool UseValueQuotes + { + get { return useValueQuotes; } + set { useValueQuotes = value; } + } + + /// + public IniWriteState WriteState + { + get { return writeState; } + } + + /// + public char CommentDelimiter + { + get { return commentDelimiter; } + set { commentDelimiter = value; } + } + + /// + public char AssignDelimiter + { + get { return assignDelimiter; } + set { assignDelimiter = value; } + } + + /// + public Stream BaseStream + { + get { return baseStream; } + } + #endregion + + #region Constructors + /// + public IniWriter(string filePath) + : this (new FileStream (filePath, FileMode.Create, FileAccess.Write, FileShare.None)) + { + } + + /// + public IniWriter (TextWriter writer) + { + textWriter = writer; + StreamWriter streamWriter = writer as StreamWriter; + if (streamWriter != null) { + baseStream = streamWriter.BaseStream; + } + } + + /// + public IniWriter (Stream stream) + : this (new StreamWriter (stream)) + { + } + #endregion + + #region Public methods + /// + public void Close () + { + textWriter.Close (); + writeState = IniWriteState.Closed; + } + + /// + public void Flush () + { + textWriter.Flush (); + } + + /// + public override string ToString () + { + return textWriter.ToString (); + } + + /// + public void WriteSection (string section) + { + ValidateState (); + writeState = IniWriteState.Section; + WriteLine ("[" + section + "]"); + } + + /// + public void WriteSection (string section, string comment) + { + ValidateState (); + writeState = IniWriteState.Section; + WriteLine ("[" + section + "]" + Comment(comment)); + } + + /// + public void WriteKey (string key, string value) + { + ValidateStateKey (); + WriteLine (key + " " + assignDelimiter + " " + GetKeyValue (value)); + } + + /// + public void WriteKey (string key, string value, string comment) + { + ValidateStateKey (); + WriteLine (key + " " + assignDelimiter + " " + GetKeyValue (value) + Comment (comment)); + } + + /// + public void WriteEmpty () + { + ValidateState (); + if (writeState == IniWriteState.Start) { + writeState = IniWriteState.BeforeFirstSection; + } + WriteLine (""); + } + + /// + public void WriteEmpty (string comment) + { + ValidateState (); + if (writeState == IniWriteState.Start) { + writeState = IniWriteState.BeforeFirstSection; + } + if (comment == null) { + WriteLine (""); + } else { + WriteLine (commentDelimiter + " " + comment); + } + } + + /// + public void Dispose () + { + Dispose (true); + } + #endregion + + #region Protected methods + /// + protected virtual void Dispose (bool disposing) + { + if (!disposed) + { + textWriter.Close (); + baseStream.Close (); + disposed = true; + + if (disposing) + { + GC.SuppressFinalize (this); + } + } + } + #endregion + + #region Private methods + /// + /// Destructor. + /// + ~IniWriter () + { + Dispose (false); + } + + /// + /// Returns the value of a key. + /// + private string GetKeyValue (string text) + { + string result; + + if (useValueQuotes) { + result = MassageValue ('"' + text + '"'); + } else { + result = MassageValue (text); + } + + return result; + } + + /// + /// Validates whether a key can be written. + /// + private void ValidateStateKey () + { + ValidateState (); + + switch (writeState) + { + case IniWriteState.BeforeFirstSection: + case IniWriteState.Start: + throw new InvalidOperationException ("The WriteState is not Section"); + case IniWriteState.Closed: + throw new InvalidOperationException ("The writer is closed"); + } + } + + /// + /// Validates the state to determine if the item can be written. + /// + private void ValidateState () + { + if (writeState == IniWriteState.Closed) { + throw new InvalidOperationException ("The writer is closed"); + } + } + + /// + /// Returns a formatted comment. + /// + private string Comment (string text) + { + return (text == null) ? "" : (" " + commentDelimiter + " " + text); + } + + /// + /// Writes data to the writer. + /// + private void Write (string value) + { + textWriter.Write (indentationBuffer.ToString () + value); + } + + /// + /// Writes a full line to the writer. + /// + private void WriteLine (string value) + { + Write (value + eol); + } + + /// + /// Fixes the incoming value to prevent illegal characters from + /// hurting the integrity of the INI file. + /// + private string MassageValue (string text) + { + return text.Replace ("\n", ""); + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Nini/Util/OrderedList.cs b/ExtensionLoader/Nini/Util/OrderedList.cs new file mode 100644 index 00000000..b4569fff --- /dev/null +++ b/ExtensionLoader/Nini/Util/OrderedList.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections; + +namespace Nini.Util +{ + //[Serializable] + /// + public class OrderedList : ICollection, IDictionary, IEnumerable + { + #region Private variables + Hashtable table = new Hashtable (); + ArrayList list = new ArrayList (); + #endregion + + #region Public properties + /// + public int Count + { + get { return list.Count; } + } + + /// + public bool IsFixedSize + { + get { return false; } + } + + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + public object this[int index] + { + get { return ((DictionaryEntry) list[index]).Value; } + set + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException ("index"); + + object key = ((DictionaryEntry) list[index]).Key; + list[index] = new DictionaryEntry (key, value); + table[key] = value; + } + } + + /// + public object this[object key] + { + get { return table[key]; } + set + { + if (table.Contains (key)) + { + table[key] = value; + table[IndexOf (key)] = new DictionaryEntry (key, value); + return; + } + Add (key, value); + } + } + + /// + public ICollection Keys + { + get + { + ArrayList retList = new ArrayList (); + for (int i = 0; i < list.Count; i++) + { + retList.Add ( ((DictionaryEntry)list[i]).Key ); + } + return retList; + } + } + + /// + public ICollection Values + { + get + { + ArrayList retList = new ArrayList (); + for (int i = 0; i < list.Count; i++) + { + retList.Add ( ((DictionaryEntry)list[i]).Value ); + } + return retList; + } + } + + /// + public object SyncRoot + { + get { return this; } + } + #endregion + + #region Public methods + /// + public void Add (object key, object value) + { + table.Add (key, value); + list.Add (new DictionaryEntry (key, value)); + } + + /// + public void Clear () + { + table.Clear (); + list.Clear (); + } + + /// + public bool Contains (object key) + { + return table.Contains (key); + } + + /// + public void CopyTo (Array array, int index) + { + table.CopyTo (array, index); + } + + /// + public void CopyTo (DictionaryEntry[] array, int index) + { + table.CopyTo (array, index); + } + + /// + public void Insert (int index, object key, object value) + { + if (index > Count) + throw new ArgumentOutOfRangeException ("index"); + + table.Add (key, value); + list.Insert (index, new DictionaryEntry (key, value)); + } + + /// + public void Remove (object key) + { + table.Remove (key); + list.RemoveAt (IndexOf (key)); + } + + /// + public void RemoveAt (int index) + { + if (index >= Count) + throw new ArgumentOutOfRangeException ("index"); + + table.Remove ( ((DictionaryEntry)list[index]).Key ); + list.RemoveAt (index); + } + + /// + public IEnumerator GetEnumerator () + { + return new OrderedListEnumerator (list); + } + + /// + IDictionaryEnumerator IDictionary.GetEnumerator () + { + return new OrderedListEnumerator (list); + } + + /// + IEnumerator IEnumerable.GetEnumerator () + { + return new OrderedListEnumerator (list); + } + #endregion + + #region Private variables + private int IndexOf (object key) + { + for (int i = 0; i < list.Count; i++) + { + if (((DictionaryEntry) list[i]).Key.Equals (key)) + { + return i; + } + } + return -1; + } + #endregion + } +} diff --git a/ExtensionLoader/Nini/Util/OrderedListEnumerator.cs b/ExtensionLoader/Nini/Util/OrderedListEnumerator.cs new file mode 100644 index 00000000..0bfa2927 --- /dev/null +++ b/ExtensionLoader/Nini/Util/OrderedListEnumerator.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections; + +namespace Nini.Util +{ + /// + public class OrderedListEnumerator : IDictionaryEnumerator + { + #region Private variables + int index = -1; + ArrayList list; + #endregion + + #region Constructors + /// + /// Instantiates an ordered list enumerator with an ArrayList. + /// + internal OrderedListEnumerator (ArrayList arrayList) + { + list = arrayList; + } + #endregion + + #region Public properties + /// + object IEnumerator.Current + { + get + { + if (index < 0 || index >= list.Count) + throw new InvalidOperationException (); + + return list[index]; + } + } + + /// + public DictionaryEntry Current + { + get + { + if (index < 0 || index >= list.Count) + throw new InvalidOperationException (); + + return (DictionaryEntry)list[index]; + } + } + + /// + public DictionaryEntry Entry + { + get { return (DictionaryEntry) Current; } + } + + /// + public object Key + { + get { return Entry.Key; } + } + + /// + public object Value + { + get { return Entry.Value; } + } + #endregion + + #region Public methods + /// + public bool MoveNext () + { + index++; + if (index >= list.Count) + return false; + + return true; + } + + /// + public void Reset () + { + index = -1; + } + #endregion + } +} \ No newline at end of file diff --git a/ExtensionLoader/Util/OrderedList.cs b/ExtensionLoader/Util/OrderedList.cs new file mode 100644 index 00000000..cfad2cd6 --- /dev/null +++ b/ExtensionLoader/Util/OrderedList.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections; + +namespace ExtensionLoader.Util +{ + //[Serializable] + /// + public class OrderedList : ICollection, IDictionary, IEnumerable + { + #region Private variables + Hashtable table = new Hashtable (); + ArrayList list = new ArrayList (); + #endregion + + #region Public properties + /// + public int Count + { + get { return list.Count; } + } + + /// + public bool IsFixedSize + { + get { return false; } + } + + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + public object this[int index] + { + get { return ((DictionaryEntry) list[index]).Value; } + set + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException ("index"); + + object key = ((DictionaryEntry) list[index]).Key; + list[index] = new DictionaryEntry (key, value); + table[key] = value; + } + } + + /// + public object this[object key] + { + get { return table[key]; } + set + { + if (table.Contains (key)) + { + table[key] = value; + table[IndexOf (key)] = new DictionaryEntry (key, value); + return; + } + Add (key, value); + } + } + + /// + public ICollection Keys + { + get + { + ArrayList retList = new ArrayList (); + for (int i = 0; i < list.Count; i++) + { + retList.Add ( ((DictionaryEntry)list[i]).Key ); + } + return retList; + } + } + + /// + public ICollection Values + { + get + { + ArrayList retList = new ArrayList (); + for (int i = 0; i < list.Count; i++) + { + retList.Add ( ((DictionaryEntry)list[i]).Value ); + } + return retList; + } + } + + /// + public object SyncRoot + { + get { return this; } + } + #endregion + + #region Public methods + /// + public void Add (object key, object value) + { + table.Add (key, value); + list.Add (new DictionaryEntry (key, value)); + } + + /// + public void Clear () + { + table.Clear (); + list.Clear (); + } + + /// + public bool Contains (object key) + { + return table.Contains (key); + } + + /// + public void CopyTo (Array array, int index) + { + table.CopyTo (array, index); + } + + /// + public void CopyTo (DictionaryEntry[] array, int index) + { + table.CopyTo (array, index); + } + + /// + public void Insert (int index, object key, object value) + { + if (index > Count) + throw new ArgumentOutOfRangeException ("index"); + + table.Add (key, value); + list.Insert (index, new DictionaryEntry (key, value)); + } + + /// + public void Remove (object key) + { + table.Remove (key); + list.RemoveAt (IndexOf (key)); + } + + /// + public void RemoveAt (int index) + { + if (index >= Count) + throw new ArgumentOutOfRangeException ("index"); + + table.Remove ( ((DictionaryEntry)list[index]).Key ); + list.RemoveAt (index); + } + + /// + public IEnumerator GetEnumerator () + { + return new OrderedListEnumerator (list); + } + + /// + IDictionaryEnumerator IDictionary.GetEnumerator () + { + return new OrderedListEnumerator (list); + } + + /// + IEnumerator IEnumerable.GetEnumerator () + { + return new OrderedListEnumerator (list); + } + #endregion + + #region Private variables + private int IndexOf (object key) + { + for (int i = 0; i < list.Count; i++) + { + if (((DictionaryEntry) list[i]).Key.Equals (key)) + { + return i; + } + } + return -1; + } + #endregion + } +} diff --git a/ExtensionLoader/Util/OrderedListEnumerator.cs b/ExtensionLoader/Util/OrderedListEnumerator.cs new file mode 100644 index 00000000..509e3405 --- /dev/null +++ b/ExtensionLoader/Util/OrderedListEnumerator.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections; + +namespace ExtensionLoader.Util +{ + /// + public class OrderedListEnumerator : IDictionaryEnumerator + { + #region Private variables + int index = -1; + ArrayList list; + #endregion + + #region Constructors + /// + /// Instantiates an ordered list enumerator with an ArrayList. + /// + internal OrderedListEnumerator (ArrayList arrayList) + { + list = arrayList; + } + #endregion + + #region Public properties + /// + object IEnumerator.Current + { + get + { + if (index < 0 || index >= list.Count) + throw new InvalidOperationException (); + + return list[index]; + } + } + + /// + public DictionaryEntry Current + { + get + { + if (index < 0 || index >= list.Count) + throw new InvalidOperationException (); + + return (DictionaryEntry)list[index]; + } + } + + /// + public DictionaryEntry Entry + { + get { return (DictionaryEntry) Current; } + } + + /// + public object Key + { + get { return Entry.Key; } + } + + /// + public object Value + { + get { return Entry.Value; } + } + #endregion + + #region Public methods + /// + public bool MoveNext () + { + index++; + if (index >= list.Count) + return false; + + return true; + } + + /// + public void Reset () + { + index = -1; + } + #endregion + } +} \ No newline at end of file