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