/// <summary>
		/// Adds the option to this collection
		/// </summary>
		/// <param name="option"></param>
		/// <returns></returns>
		public int Add(XmlConfigurationOption option) 
		{				
			if (this.Contains(option))
				throw new ArgumentException("ElementName already exists. ElementName in collection: " + option.ElementName + " ElementName being added: " + option.ElementName);
			
			option.Parent = this;
			option.BeforeEdit += new XmlConfigurationElementCancelEventHandler(this.OnBeforeEdit);
			option.Changed += new XmlConfigurationElementEventHandler(this.OnChanged);
			option.AfterEdit += new XmlConfigurationElementEventHandler(this.OnAfterEdit);
			option.EditCancelled += new XmlConfigurationElementEventHandler(this.OnEditCancelled);
			int index = base.InnerList.Add(option);
			this.OnChanged(this, new XmlConfigurationOptionEventArgs(option, XmlConfigurationElementActions.Added));			
			return index;
		}
		/// <summary>
		/// Adds the array of options to this collection
		/// </summary>
		/// <param name="options"></param>
		public void Add(XmlConfigurationOption[] options) 
		{
			if (options == null)
				throw new ArgumentNullException("options");			

			foreach(XmlConfigurationOption opt in options) 
			{
				try 
				{
					this.Add(opt);
				}
				catch(System.Exception systemException) 
				{
					System.Diagnostics.Trace.WriteLine(systemException);
				}
			}
		}
		/// <summary>
		/// Initializes a new instance of the XmlConfigurationOption class
		/// </summary>
		/// <param name="option">The option to base this option on</param>
		public XmlConfigurationOption(XmlConfigurationOption option) : base((XmlConfigurationElement)option)
		{
			_valueAssemblyQualifiedName = _value.GetType().AssemblyQualifiedName;
			if (option != null)
			{
				_value = option.Value;
				_valueAssemblyQualifiedName = option.ValueAssemblyQualifiedName;
				_editorAssemblyQualifiedName = option.EditorAssemblyQualifiedName;
				_shouldSerializeValue = option.ShouldSerializeValue;						
			}
		}
		/// <summary>
		/// Initializes a new instance of the XmlConfigurationOptionEventArgs class
		/// </summary>
		/// <param name="option">The option being affected by this action</param>
		/// <param name="action">The action affecting this option</param>
		public XmlConfigurationOptionEventArgs(XmlConfigurationOption option, XmlConfigurationElementActions action) : base(option, action)
		{
			
		}
		/// <summary>
		/// Removes the option from this collection
		/// </summary>
		/// <param name="option"></param>
		public void Remove(XmlConfigurationOption option) 
		{
			if (this.Contains(option))
			{											
				option.BeforeEdit -= new XmlConfigurationElementCancelEventHandler(this.OnBeforeEdit);
				option.Changed -= new XmlConfigurationElementEventHandler(this.OnChanged);
				option.AfterEdit -= new XmlConfigurationElementEventHandler(this.OnAfterEdit);
				option.EditCancelled -= new XmlConfigurationElementEventHandler(this.OnEditCancelled);
				base.InnerList.Remove(option);
				this.OnChanged(this, new XmlConfigurationOptionEventArgs(option, XmlConfigurationElementActions.Removed));
			}
		}
		public static Type GetType(XmlConfigurationOption option)
		{		
			try
			{
				Type t = null;
				if (option.ValueAssemblyQualifiedName != null)
				{			
					t = Type.GetType(option.ValueAssemblyQualifiedName, false, true);
					if (t != null)
						return t;					
				}

				object value = option.Value;
				if ((value != null) && (((string)value) != string.Empty))
				{
					t = value.GetType();
					if (t != null)
						return t;
				}
			}
			catch(System.Exception)
			{
//				System.Diagnostics.Trace.WriteLine(systemException);
			}
			return Type.Missing as Type;
		}	
		/// <summary>
		/// Initializes a new instance of the XmlConfigurationReaderEventArgs class
		/// </summary>
		/// <param name="systemException"></param>
		/// <param name="option"></param>
		public XmlConfigurationReaderEventArgs(System.Exception systemException, XmlConfigurationOption option, string buffer)
		{
			_systemException = systemException;
			_option = option;
			_buffer = buffer;
		}
		private XmlConfigurationOption ReadOption(XPathNavigator navigator, XmlConfigurationOption option)
		{
			string value = null;

			try
			{
				if (string.Compare(navigator.Name, @"Option", true) == 0)
				{
					#region Attributes

					XPathNavigator optionNavigator = navigator.Clone();	
				
					value = optionNavigator.Value;

					if (optionNavigator.HasAttributes)
					{
						XPathNavigator attributesNavigator = optionNavigator.Clone();
						if (attributesNavigator.MoveToFirstAttribute())
						{
							option.ElementName = attributesNavigator.Value;

							while(attributesNavigator.MoveToNextAttribute())
							{
								switch(attributesNavigator.Name)
								{
									case @"HasChanges":
										option.HasChanges = XmlConvert.ToBoolean(attributesNavigator.Value);
										break;
									case @"Category":
										option.Category = attributesNavigator.Value;
										break;
									case @"Description":
										option.Description = attributesNavigator.Value;
										break;
									case @"DisplayName":
										option.DisplayName = attributesNavigator.Value;
										break;
									case @"Hidden":
										option.Hidden = XmlConvert.ToBoolean(attributesNavigator.Value);
										break;
									case @"Readonly":
										option.Readonly = XmlConvert.ToBoolean(attributesNavigator.Value);
										break;
									case @"ShouldSerializeValue":
										option.ShouldSerializeValue = XmlConvert.ToBoolean(attributesNavigator.Value);
										break;
									case @"ValueAssemblyQualifiedName":
										option.ValueAssemblyQualifiedName = attributesNavigator.Value;
										break;
									case @"EditorAssemblyQualifiedName":
										option.EditorAssemblyQualifiedName = attributesNavigator.Value;
										break;
								};						
							}
						}
					}
					
					#endregion

					#region Value

					// if the option is serialized
					if (option.ShouldSerializeValue)
					{
						// it should be encoded in base 64, so decode it
						option.Value = this.GetSerializedValue(option, value);
						return option;
					}
					
					// otherwise figure out why type of object it is
					Type t = TypeLoader.GetType(option);
					if (t != null)
					{
						if (t.IsEnum)
						{
							option.Value = Enum.Parse(t, value, true);	
							return option;
						}
						
						if (t == typeof(System.String))
							option.Value = (string)value;
						if (t == typeof(System.Boolean))
							option.Value = (object) XmlConvert.ToBoolean(value);
						if (t == typeof(System.Int32))
							option.Value = XmlConvert.ToInt32(value);
						if (t == typeof(System.Int64))
							option.Value = XmlConvert.ToInt64(value);
						if (t == typeof(System.Decimal))
							option.Value = XmlConvert.ToDecimal(value);
						if (t == typeof(System.Double))
							option.Value = XmlConvert.ToDouble(value);
						if (t == typeof(System.Byte))
							option.Value = XmlConvert.ToByte(value);
						if (t == typeof(System.Char))
							option.Value = XmlConvert.ToChar(value);
						if (t == typeof(System.DateTime))
							option.Value = XmlConvert.ToDateTime(value);
						if (t == typeof(System.Guid))
							option.Value = XmlConvert.ToGuid(value);
						if (t == typeof(System.Int16))
							option.Value = XmlConvert.ToInt16(value);
						if (t == typeof(System.SByte))
							option.Value = XmlConvert.ToSByte(value);
						if (t == typeof(System.Single))
							option.Value = XmlConvert.ToSingle(value);
						if (t == typeof(System.UInt16))
							option.Value = XmlConvert.ToUInt16(value);
						if (t == typeof(System.UInt32))
							option.Value = XmlConvert.ToUInt32(value);
						if (t == typeof(System.UInt64))
							option.Value = XmlConvert.ToUInt64(value);						
					}
										
					#endregion

					return option;
				}
			}
			catch(Exception ex)
			{								
				this.OnCannotReadValue(this, new XmlConfigurationReaderEventArgs(ex, option, value));
//				Debug.WriteLine(ex);				
			}
			return null;
		}
		/// <summary>
		/// Gets the value to write using the XmlConvert.ToString() method on the Option.Value property.
		/// </summary>
		/// <param name="option"></param>
		/// <returns></returns>
		private string GetConvertableValue(XmlConfigurationOption option)
		{
			try
			{
				if (option != null)
				{
					if (option.Value != null)
					{
						Type t = option.Value.GetType();
						if (t != null)
						{
							if (t == typeof(string))
								return (string)option.Value;

							if (t == typeof(bool))
								return XmlConvert.ToString((bool)option.Value);							

							if (t == typeof(int))
								return XmlConvert.ToString((int)option.Value);

							if (t == typeof(long))
								return XmlConvert.ToString((long)option.Value);
							
							if (t == typeof(decimal))
								return XmlConvert.ToString((decimal)option.Value);

							if (t == typeof(double))
								return XmlConvert.ToString((double)option.Value);

							if (t == typeof(byte))
								return XmlConvert.ToString((byte)option.Value);	

							if (t == typeof(char))
								return XmlConvert.ToString((char)option.Value);	

							if (t == typeof(System.DateTime))
								return XmlConvert.ToString((System.DateTime)option.Value);

							if (t == typeof(System.Guid))
								return XmlConvert.ToString((System.Guid)option.Value);

							if (t == typeof(short))
								return XmlConvert.ToString((short)option.Value);

							if (t == typeof(sbyte))
								return XmlConvert.ToString((sbyte)option.Value);

							if (t == typeof(float))
								return XmlConvert.ToString((float)option.Value);

							if (t == typeof(System.TimeSpan))
								return XmlConvert.ToString((System.TimeSpan)option.Value);

							if (t == typeof(ushort))
								return XmlConvert.ToString((ushort)option.Value);

							if (t == typeof(uint))
								return XmlConvert.ToString((uint)option.Value);

							if (t == typeof(ulong))
								return XmlConvert.ToString((ulong)option.Value);

							return option.Value.ToString();
						}
					}
				}
			}
			catch(System.Exception systemException)
			{
				System.Diagnostics.Trace.WriteLine(systemException);
				this.OnCannotWriteValue(this, new XmlConfigurationWriterEventArgs(systemException, option));
			}
			return string.Empty;
		}
		/// <summary>
		/// Gets the value to write using a BinaryFormatter to serialize the Object.Value property.
		/// </summary>
		/// <param name="option"></param>
		/// <returns></returns>
		private string GetSerializedValue(XmlConfigurationOption option)
		{
			string buffer = null;
			try
			{
				Type t = Type.GetType(option.ValueAssemblyQualifiedName);
				if (t != null)
				{
					if (EncodingEngine.Base64Encode(option.Value, t, out buffer))
						return buffer;
				}
			}
			catch(System.Exception systemException)
			{
				System.Diagnostics.Trace.WriteLine(systemException);
				this.OnCannotWriteValue(this, new XmlConfigurationWriterEventArgs(systemException, option));
			}
			return buffer;
		}
		/// <summary>
		/// Writes an option to the XmlDocument
		/// </summary>
		/// <param name="doc"></param>
		/// <param name="parent"></param>
		/// <param name="option"></param>
		/// <param name="alwaysPersist"></param>
		private void WriteOption(XmlDocument doc, XmlElement parent, XmlConfigurationOption option, bool alwaysPersist)
		{
			try
			{
				if (option.Persistent || alwaysPersist)
				{
					/// create an element for the option
					XmlElement child = doc.CreateElement(@"Option");

					/// write the properties of this option
					child.SetAttribute(@"ElementName", option.ElementName);
					child.SetAttribute(@"HasChanges", XmlConvert.ToString(option.HasChanges));
					child.SetAttribute(@"Category", option.Category);
					child.SetAttribute(@"Description", option.Description);
					child.SetAttribute(@"DisplayName", option.DisplayName);				
					child.SetAttribute(@"Hidden", XmlConvert.ToString(option.Hidden));	
					child.SetAttribute(@"Readonly", XmlConvert.ToString(option.Readonly));
					child.SetAttribute(@"ShouldSerializeValue", XmlConvert.ToString(option.ShouldSerializeValue));
					child.SetAttribute(@"ValueAssemblyQualifiedName", option.ValueAssemblyQualifiedName);
//					child.SetAttribute(@"ReferencedAssemblyName", option.ReferencedAssemblyName);
					child.SetAttribute(@"EditorAssemblyQualifiedName", option.EditorAssemblyQualifiedName);
					
					/// create a text node for the value, as we are most likely unsure of what is contained in the actual value
					XmlText text = doc.CreateTextNode("Value");			

					/// try and serialize the value if we can, otherwise use the XmlConvert class to convert the value to a string
					if (option.ShouldSerializeValue)
						text.Value = this.GetSerializedValue(option);
					else
						text.Value = this.GetConvertableValue(option);

					/// add the text for the value to the element
					child.AppendChild(text);

					/// add the child to the parent
					parent.AppendChild(child);
				}
			}
			catch(System.Exception systemException)
			{
				System.Diagnostics.Trace.WriteLine(systemException);
			}
		}
		public XmlConfigurationWriterEventArgs(System.Exception systemException, XmlConfigurationOption option)
		{
			_systemException = systemException;
			_option = option;
		}
		public ValuePropertyDescriptor(XmlConfigurationOption option) : base("Value", null)
		{			
			_option = option;
		}
		public XmlConfigurationOptionPropertyDescriptorMenuItem(string text, EventHandler onClick, XmlConfigurationOption option) : base(text, onClick)
		{
			_option = option;
		}
		/// <summary>
		/// Determines whether an option exists using the specified option's elementName
		/// </summary>
		/// <param name="option"></param>
		/// <returns></returns>
		public bool Contains(XmlConfigurationOption option) 
		{
			foreach(XmlConfigurationOption opt in base.InnerList)
				if (opt.ElementName == option.ElementName)
					return true;
			return false;
		}				
		/// <summary>
		/// Reads an XmlConfigurationCategory using the specified XPathNavigator
		/// </summary>
		/// <param name="navigator"></param>
		/// <returns></returns>
		private XmlConfigurationCategory ReadCategory(XPathNavigator navigator, XmlConfigurationCategory category)
		{
			// break off a clone so that the starting cursor doesn't lose it's place
			XPathNavigator categoryNavigator = navigator.Clone();

			// does the cateogry have attributes, it should!
			if (categoryNavigator.HasAttributes)
			{
				// break off yet another clone to navigate the attributes of this element
				XPathNavigator attributesNavigator = categoryNavigator.Clone();
				if (attributesNavigator.MoveToFirstAttribute())
				{
					category.ElementName = attributesNavigator.Value;

					while(attributesNavigator.MoveToNextAttribute())
					{
						switch(attributesNavigator.Name)
						{
						case @"HasChanges":
							category.HasChanges = XmlConvert.ToBoolean(attributesNavigator.Value);
							break;
						case @"Category":
							category.Category = attributesNavigator.Value;
							break;
						case @"Description":
							category.Description = attributesNavigator.Value;
							break;
						case @"DisplayName":
							category.DisplayName = attributesNavigator.Value;
							break;
						case @"Hidden":
							category.Hidden = XmlConvert.ToBoolean(attributesNavigator.Value);
							break;
						};						
					}
				}
			}

			XmlConfigurationOption option = null;
			XPathNavigator optionNavigator = navigator.Clone();
			if (optionNavigator.HasChildren)
			{
				if (optionNavigator.MoveToFirstChild())
				{										
					option = new XmlConfigurationOption();
					option.BeginInit();
					if (this.ReadOption(optionNavigator, option) != null)
						category.Options.Add(option);											
					option.EndInit();
					
					while (optionNavigator.MoveToNext())
					{
						option = new XmlConfigurationOption();
						option.BeginInit();
						if (this.ReadOption(optionNavigator, option) != null)
							category.Options.Add(option);						
						option.EndInit();
					}					
				}
			}

			if (navigator.HasChildren)
			{
				this.ReadCategories(categoryNavigator, category.Categories);	
			}
						
			return category;
		}	
		public XmlConfigurationOption this[string elementName, bool createIfNotFound, object defaultValue]
		{
			get
			{
				XmlConfigurationOption option = this[elementName];				
				
				if (option == null)
					if (createIfNotFound)
					{
						option = new XmlConfigurationOption(elementName, defaultValue);
						this.Add(option);
						option = this[elementName];
					}
				return option;			
			}
		}
		/// <summary>
		/// Deserializes an object assuming that the string contains the base64 encoded data for the object
		/// </summary>
		/// <param name="option"></param>
		/// <param name="buffer"></param>
		/// <returns></returns>
		private object GetSerializedValue(XmlConfigurationOption option, string buffer)
		{			
			object instance = null;
			try
			{
				// try and base 64 decode the string
				if (EncodingEngine.Base64Decode(buffer, out instance))
					return instance;
			}
			catch(Exception ex)
			{				
				this.OnCannotReadValue(this, new XmlConfigurationReaderEventArgs(ex, option, buffer));
//				Debug.WriteLine(ex);
			}
			return instance;
		}
		/// <summary>
		/// Initializes a new instance of the XmlConfigurationOptionPropertyDescriptor class
		/// </summary>
		/// <param name="option">The option to describe</param>
		public XmlConfigurationOptionPropertyDescriptor(XmlConfigurationOption option) : base(option.DisplayName, null)
		{			
			_option = option;
		}