/*
         * AddRange
         */

        /// <summary>
        /// Adds the specified <see cref="NuGenPropertyInfoCollection"/> to this <see cref="NuGenPropertyInfoCollection"/>.
        /// </summary>
        /// <param name="propertyInfoCollection">Specifies the <see cref="NuGenPropertyInfoCollection"/> to add.</param>
        /// <exception cref="ArgumentNullException"><paramref name="propertyInfoCollection"/> is <see langword="null"/>.</exception>
        public void AddRange(NuGenPropertyInfoCollection propertyInfoCollection)
        {
            if (propertyInfoCollection == null)
            {
                throw new ArgumentNullException("propertyInfoCollection");
            }

            foreach (NuGenPropertyInfo propertyInfo in propertyInfoCollection)
            {
                this.Add(propertyInfo);
            }
        }
		/// <summary>
		/// Deserializes the specified <see cref="Object"/> from the specified <see cref="NuGenPropertyInfoCollection"/>.
		/// </summary>
		/// <param name="obj">Specifies the <see cref="Object"/> to serialize.</param>
		/// <param name="properties">Specifies the list of properties to deserialize.</param>
		private void DeserializeObject(object obj, NuGenPropertyInfoCollection properties)
		{
			foreach (NuGenPropertyInfo property in properties)
			{
				this.OnDeserializing(EventArgs.Empty);

				PropertyInfo propertyInfo = null;

				if (obj != null)
				{
					propertyInfo = obj.GetType().GetProperty(property.Name);
				}

				if (!property.IsReference && (property.ReferenceCode != -1))
				{
					_graph.Add(property.Value, property.ReferenceCode);
				}

				if (property.IsKey)
				{
					NuGenSerializer.SetProperty(propertyInfo, obj, property.Value);

					if (propertyInfo != null)
					{
						this.DeserializeObject(propertyInfo.GetValue(obj, null), property.Properties);
						continue;
					}

					this.DeserializeObject(null, property.Properties);
				}
				else
				{
					if (property.IsList)
					{
						int count = property.Count;

						IList list = null;
						object propertyValue = null;

						if (propertyInfo != null)
						{
							list = propertyInfo.GetValue(obj, null) as IList;

							if (list != null)
							{
								Type listElementType = NuGenSerializer.GetTypeOfArrayElement(list);

								if (list is Array)
								{
									list = Array.CreateInstance(listElementType, count);
									NuGenSerializer.SetProperty(propertyInfo, obj, list);
								}
								else
								{
									list.Clear();
								}

								int listPropertyCount = 0;

								foreach (NuGenPropertyInfo listProperty in property.Properties)
								{
									this.OnDeserializing(EventArgs.Empty);

									if (listProperty.IsReference && (listProperty.ReferenceCode != -1))
									{
										listProperty.Value = _graph[listProperty.ReferenceCode];
										propertyValue = listProperty.Value;
									}
									else if ((listElementType == typeof(string)) || !(listProperty.Value is string))
									{
										propertyValue = listProperty.Value;
									}
									else
									{
										try
										{
											propertyValue = _converter.StringToObject(
												(string)listProperty.Value,
												listElementType
												);
										}
										catch
										{
											propertyValue = (string)listProperty.Value;
										}
									}

									if (!listProperty.IsReference && (listProperty.ReferenceCode != -1))
									{
										_graph.Add(listProperty.Value, listProperty.ReferenceCode);
									}

									if (list is Array)
									{
										object elementValue = _converter.StringToObject((string)propertyValue, listElementType);
										((Array)list).SetValue(elementValue, listPropertyCount++);
									}
									else
									{
										list.Add(propertyValue);

										if (!listProperty.IsReference && !listProperty.IsSerializable)
										{
											this.DeserializeObject(propertyValue, listProperty.Properties);
										}
									}
								}
							}
						}

						continue;
					}

					if (property.IsReference)
					{
						object propertyValue = _graph[property.ReferenceCode];

						NuGenSerializer.SetProperty(propertyInfo, obj, propertyValue);

						if (propertyValue == null)
						{
							_references.Add(
								property,
								propertyInfo,
								obj
							);
						}
					}
					else
					{
						if (property.Value == null)
						{
							NuGenSerializer.SetProperty(propertyInfo, obj, null);
							continue;
						}

						if (propertyInfo != null)
						{
							object propertyValue = property.Value;

							if ((property.Value is string) && (propertyInfo.PropertyType != typeof(object)))
							{
								propertyValue = _converter.StringToObject((string)property.Value, propertyInfo.PropertyType);
							}

							NuGenSerializer.SetProperty(propertyInfo, obj, propertyValue);
						}
					}
				}
			}
		}
		private NuGenPropertyInfoCollection DeserializeObject(XmlTextReader xmlTextReader)
		{
			Debug.Assert(xmlTextReader != null, "xmlTextReader != null");

			NuGenPropertyInfoCollection propertyInfoCollection = new NuGenPropertyInfoCollection();
			xmlTextReader.Read();

			while (!xmlTextReader.EOF)
			{
				if (xmlTextReader.IsStartElement())
				{
					this.OnDeserializing(EventArgs.Empty);

					string propertyString = xmlTextReader.Name;

					bool isKey = xmlTextReader.GetAttribute(Resources.XmlAttribute_IsKey) == Resources.XmlValue_True;
					bool isRef = xmlTextReader.MoveToAttribute(Resources.XmlAttribute_IsRef);
					bool isList = xmlTextReader.MoveToAttribute(Resources.XmlAttribute_IsList);

					NuGenPropertyInfo propertyInfo = new NuGenPropertyInfo(
						XmlConvert.DecodeName(NuGenSerializer.GetPropertyFromString(propertyString)),
						null,
						isKey,
						isRef,
						isList
						);

					if (xmlTextReader.GetAttribute(Resources.XmlAttribute_IsNull) == Resources.XmlValue_True)
					{
						propertyInfo.Value = null;
					}
					else if (xmlTextReader.GetAttribute(Resources.XmlAttribute_IsSer) != null)
					{
						string refAttrValue = xmlTextReader.GetAttribute(Resources.XmlAttribute_Ref);

						if (refAttrValue != null)
						{
							propertyInfo.ReferenceCode = int.Parse(refAttrValue, CultureInfo.InvariantCulture);
						}

						string typeAttrValue = xmlTextReader.GetAttribute(Resources.XmlAttribute_Type);

						Type typeFromAttr = NuGenSerializer.StringToType[typeAttrValue] as Type;

						if (typeFromAttr == null)
						{
							propertyInfo.Value = NuGenActivator.CreateObject(typeAttrValue);
						}
						else
						{
							propertyInfo.Value = NuGenActivator.CreateObject(typeFromAttr);
						}

						propertyInfo.IsSerializable = true;
						INuGenSerializable serializable = (INuGenSerializable)propertyInfo.Value;
						serializable.Deserialize(_converter, xmlTextReader);

						_graph.Add(serializable, propertyInfo.ReferenceCode);
					}
					else if (propertyInfo.IsReference)
					{
						propertyInfo.ReferenceCode = int.Parse(xmlTextReader.GetAttribute(Resources.XmlAttribute_IsRef), CultureInfo.InvariantCulture);
					}
					else if (propertyInfo.IsList)
					{
						propertyInfo.Count = int.Parse(xmlTextReader.GetAttribute(Resources.XmlAttribute_Count), CultureInfo.InvariantCulture);

						if ((propertyInfo.Count > 0) && !xmlTextReader.IsEmptyElement)
						{
							propertyInfo.Properties = this.DeserializeObject(xmlTextReader);
						}
					}
					else if (!propertyInfo.IsKey)
					{
						if (xmlTextReader.GetAttribute(Resources.XmlAttribute_IsImage) == Resources.XmlValue_True)
						{
							int bufferLength = int.Parse(xmlTextReader.GetAttribute(Resources.XmlAttribute_Length), CultureInfo.InvariantCulture);
							byte[] buffer = new byte[bufferLength];
							xmlTextReader.ReadBase64(buffer, 0, bufferLength);
							propertyInfo.Value = NuGenImageConverter.BytesToImage(buffer);
						}
						else if (xmlTextReader.GetAttribute(Resources.XmlAttribute_IsEmfImage) == Resources.XmlValue_True)
						{
							int bufferLength = int.Parse(xmlTextReader.GetAttribute(Resources.XmlAttribute_Length), CultureInfo.InvariantCulture);
							byte[] buffer = new byte[bufferLength];
							xmlTextReader.ReadBase64(buffer, 0, bufferLength);
							propertyInfo.Value = NuGenMetafileConverter.BytesToMetafile(buffer);
						}
						else
						{
							propertyInfo.Value = xmlTextReader.ReadString();
						}
					}
					else if (propertyInfo.IsKey)
					{
						string attrRef = xmlTextReader.GetAttribute(Resources.XmlAttribute_Ref);

						if (attrRef != null)
						{
							propertyInfo.ReferenceCode = int.Parse(attrRef, CultureInfo.InvariantCulture);
						}

						string typeAttr = xmlTextReader.GetAttribute(Resources.XmlAttribute_Type);
						Type typeFromAttr = NuGenSerializer.StringToType[typeAttr] as Type;

						if (typeFromAttr == null)
						{
							propertyInfo.Value = NuGenActivator.CreateObject(typeAttr);
						}
						else
						{
							propertyInfo.Value = NuGenActivator.CreateObject(typeFromAttr);
						}

						if (!xmlTextReader.IsEmptyElement)
						{
							propertyInfo.Properties = this.DeserializeObject(xmlTextReader);
						}
					}

					propertyInfoCollection.Add(propertyInfo);
				}
				else if (xmlTextReader.NodeType == XmlNodeType.EndElement)
				{
					return propertyInfoCollection;
				}

				xmlTextReader.Read();
			}

			return propertyInfoCollection;
		}
		private void SerializeObject(XmlTextWriter xmlTextWriter, NuGenPropertyInfoCollection properties)
		{
			if (properties != null)
			{
				foreach (NuGenPropertyInfo property in properties)
				{
					this.OnSerializing(EventArgs.Empty);

					if (
						(property.DefaultValue == null) ||
						!(object.Equals(property.DefaultValue, property.Value))
						)
					{
						this.SerializeProperty(xmlTextWriter, property);
					}
				}
			}
		}
		/// <summary>
		/// Serializes the specified <see cref="Object"/>. Can be <see langword="null"/>.
		/// </summary>
		/// <param name="obj">Specifies the <see cref="Object"/> to serialize.</param>
		/// <returns></returns>
		public NuGenPropertyInfoCollection SerializeObject(object obj)
		{
			if (obj == null)
			{
				return null;
			}

			if (_graph[obj] == -1)
			{
				_graph.Add(obj);
			}

			NuGenPropertyInfoCollection properties = new NuGenPropertyInfoCollection();
			PropertyDescriptorCollection descriptors = TypeDescriptor.GetProperties(obj);

			/* If no public properties found return null. */

			if ((descriptors == null) || (descriptors.Count == 0))
			{
				return null;
			}

			if (this.SortProperties)
			{
				descriptors = descriptors.Sort();
			}

			/*
			 * Serialize public properties.
			 */

			for (int i = 0; i < descriptors.Count; i++)
			{
				this.OnSerializing(EventArgs.Empty);

				PropertyDescriptor descriptor = descriptors[i];

				NuGenSerializationVisibilityAttribute attribute = (NuGenSerializationVisibilityAttribute)descriptor.Attributes[typeof(NuGenSerializationVisibilityAttribute)];

				/* This is the default value for the property that is not marked with
				 * NuGenSerializationVisibilityAttribute attribute. */
				NuGenSerializationVisibility visibility = NuGenSerializationVisibility.Content;

				object propertyDefaultValue = NuGenSerializer.GetDefaultValue(descriptor);
				string propertyName = descriptor.Name;
				object propertyValue = descriptor.GetValue(obj);

				/* If the property is marked with the NuGenSerializationVisibilityAttribute attribute... */
				if (attribute != null)
				{
					/* Skip properties that are not visible for the serializer. */
					if (attribute.Visibility == NuGenSerializationVisibility.Hidden)
					{
						continue;
					}

					visibility = attribute.Visibility;
				}
				/* If the property is not marked with the NuGenSerializationVisibilityAttribute attribute... */
				else
				{
					if (
						(descriptor.SerializationVisibility == DesignerSerializationVisibility.Content) &&
						!(propertyValue is IList) &&
						!(descriptor.PropertyType.IsEnum)
						)
					{
						visibility = NuGenSerializationVisibility.Reference;
					}
					else if (
						!(descriptor.ShouldSerializeValue(obj)) ||
						(descriptor.SerializationVisibility == DesignerSerializationVisibility.Hidden)
						)
					{
						continue;
					}
				}

				NuGenPropertyInfo propertyInfo = null;

				if (propertyValue == null)
				{
					propertyInfo = new NuGenPropertyInfo(
						propertyName,
						propertyValue,
						propertyDefaultValue,
						true,
						false,
						false
					);
				}
				else
				{
					switch (visibility)
					{
						case NuGenSerializationVisibility.Reference:
						{
							propertyInfo = new NuGenPropertyInfo(
								propertyName,
								propertyValue,
								propertyDefaultValue,
								true,
								true,
								false
							);
							_references.Add(propertyInfo);
							break;
						}
						case NuGenSerializationVisibility.Content:
						{

							/*
							 * Automatically determine if the marked property is a collection.
							 */

							if (propertyValue is IList)
							{
								propertyInfo = new NuGenPropertyInfo(
									propertyName,
									propertyValue,
									propertyDefaultValue,
									false,
									false,
									true
								);
								propertyInfo.Count = this.SerializeList(
									propertyInfo.Properties,
									propertyValue
								);
							}
							else
							{
								propertyInfo = new NuGenPropertyInfo(
									propertyName,
									propertyValue,
									propertyDefaultValue,
									false,
									false,
									false
								);
							}

							break;
						}
						case NuGenSerializationVisibility.Class:
						{
							if (!propertyValue.GetType().IsClass)
							{
								throw new InvalidCastException(
									string.Format(CultureInfo.InvariantCulture, Properties.Resources.InvalidCast_NotClass, propertyName)
								);
							}

							NuGenReferenceIgnoreAttribute[] ignoreAttributes = (NuGenReferenceIgnoreAttribute[])descriptor.PropertyType.GetCustomAttributes(typeof(NuGenReferenceIgnoreAttribute), false);

							int valueInitial = _graph[propertyValue];

							if ((valueInitial == -1) || (ignoreAttributes.Length > 0))
							{
								_graph.Add(propertyValue);

								int valueAfterAdd = _graph[propertyValue];

								if (
									(this.CheckSerializable)
									&& (propertyValue is INuGenSerializable)
									)
								{
									propertyInfo = new NuGenPropertyInfo(
										propertyName,
										propertyValue,
										propertyDefaultValue,
										true,
										false,
										false
									);

									propertyInfo.IsSerializable = true;
								}
								else
								{
									propertyInfo = new NuGenPropertyInfo(
										propertyName,
										propertyValue,
										propertyDefaultValue,
										true,
										false,
										false
									);

									NuGenPropertyInfoCollection innerProperties = this.SerializeObject(propertyValue);

									if (innerProperties != null)
									{
										propertyInfo.Properties.AddRange(innerProperties);
									}
								}

								propertyInfo.ReferenceCode = valueAfterAdd;
							}
							else
							{
								propertyInfo = new NuGenPropertyInfo(
									propertyName,
									propertyValue,
									propertyDefaultValue,
									true,
									true,
									false
								);

								propertyInfo.ReferenceCode = valueInitial;
							}

							break;
						}
					}
				}

				if (propertyInfo != null)
				{
					properties.Add(propertyInfo);
				}
			}

			return properties;
		}
		/// <summary>
		/// Serializes the specified list.
		/// </summary>
		/// <param name="properties">Specifies the <see cref="NuGenPropertyInfoCollection"/> to be filled
		/// with the elements of the specified list.</param>
		/// <param name="list">Specifies the list to serialize.</param>
		/// <returns>Returns the number of elements in the specified list.</returns>
		private int SerializeList(NuGenPropertyInfoCollection properties, object list)
		{
			if (list == null)
			{
				return 0;
			}

			Debug.Assert(properties != null, "properties != null");
			Debug.Assert(list is IList, "list is IList");

			int count = 0;

			foreach (object item in (IList)list)
			{
				this.OnSerializing(EventArgs.Empty);

				if (
					(item.GetType().IsPrimitive)
					|| (item is string)
					|| (this.IsContent(item))
					)
				{
					properties.Add(
						new NuGenPropertyInfo(Resources.XmlAttribute_Value, item, false, false, false)
					);
				}
				else
				{
					int index = _graph[item];

					if (index == -1)
					{
						_graph.Add(item);

						NuGenPropertyInfo propertyInfo = null;

						if (this.CheckSerializable && (item is INuGenSerializable))
						{
							propertyInfo = new NuGenPropertyInfo(
								string.Format(CultureInfo.InvariantCulture, "{0}{1}", Resources.XmlTag_Item, (count + 1).ToString(CultureInfo.InvariantCulture)),
								item,
								true,
								false,
								false
							);

							propertyInfo.IsSerializable = true;
						}
						else
						{
							string propertyName = Resources.XmlTag_Item;

							PropertyInfo namePropertyInfo = item.GetType().GetProperty("Name");

							if (namePropertyInfo != null)
							{
								string namePropertyValue = (string)namePropertyInfo.GetValue(item, null);

								if (!string.IsNullOrEmpty(namePropertyValue))
								{
									propertyName = namePropertyValue;
								}
							}

							propertyInfo = new NuGenPropertyInfo(
								string.Format(CultureInfo.InvariantCulture, "{0}{1}", propertyName, (count + 1).ToString(CultureInfo.InvariantCulture)),
								item,
								true,
								false,
								false
							);

							propertyInfo.Properties.AddRange(this.SerializeObject(item));
						}

						Debug.Assert(propertyInfo != null, "propertyInfo != null");

						propertyInfo.ReferenceCode = _graph[item];
						properties.Add(propertyInfo);
					}
					else
					{
						NuGenPropertyInfo propertyInfo = new NuGenPropertyInfo(
							Resources.XmlTag_Item,
							item,
							true,
							true,
							false
						);

						propertyInfo.ReferenceCode = index;
						properties.Add(propertyInfo);
					}
				}

				count++;
			}

			return count;
		}
		/*
		 * AddRange
		 */

		/// <summary>
		/// Adds the specified <see cref="NuGenPropertyInfoCollection"/> to this <see cref="NuGenPropertyInfoCollection"/>.
		/// </summary>
		/// <param name="propertyInfoCollection">Specifies the <see cref="NuGenPropertyInfoCollection"/> to add.</param>
		/// <exception cref="ArgumentNullException"><paramref name="propertyInfoCollection"/> is <see langword="null"/>.</exception>
		public void AddRange(NuGenPropertyInfoCollection propertyInfoCollection)
		{
			if (propertyInfoCollection == null)
			{
				throw new ArgumentNullException("propertyInfoCollection");
			}

			foreach (NuGenPropertyInfo propertyInfo in propertyInfoCollection)
			{
				this.Add(propertyInfo);
			}
		}