|
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
// Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
// File: XmlSerializer.cs
//
// Namespace: System.Web.UI.WebControls[Windows.Forms].Charting.Utilities
//
// Classes: XmlFormatSerializer, BinaryFormatSerializer
// SerializerBase, SerializationVisibilityAttribute
//
// Purpose:
//
// Chart serializer allows persisting of all chart data and
// settings into the stream or file using XML or binary format.
// This data can be later loaded back into the chart completely
// restoring its state. Serialize can also be used to reset chart
// control state to its default values.
//
// Both XML and Binary serialization methods use reflection to
// discover class properties which need to be serialized. Only
// properties with non-default values are persisted. Full Trust
// is required to use chartserialization.
//
// SerializeBase class implements all the chart serializer
// properties and methods to reset chart content. XmlFormatSerializer
// and BinaryFormatSerializer classes derive from the SerializeBase
// class and provide saving and loading functionality for XML and
// binary format.
//
// By default, all chart content is Saved, Loaded or Reset, but
// this can be changed using serializer Content, SerializableContent
// and NonSerializableContent properties. Content property allows a
// simple way to serialize everything, appearance or just chart data.
//
// SerializableContent and NonSerializableContent properties provide
// more control over what is beign persisted and they override the
// Content property settings. Each of the properties is a string
// which is a comma-separated listing of all chart properties to be
// serialized. The syntax of this property is "Class.Property[,Class.Property]",
// and wildcards may be used (represented by an asterisk). For example,
// to serialize all chart BackColor properties set this property to
// "*.BackColor".
//
// Reviewed: AG - August 7, 2002
// AG - Microsoft 6, 2007
//
//===================================================================
#region Used Namespaces
using System;
using System.Xml;
using System.Reflection;
using System.Collections;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Specialized;
using System.Security;
#if Microsoft_CONTROL
using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
#else
using System.Web.UI.WebControls;
using System.Web.UI.DataVisualization.Charting.ChartTypes;
#endif
#endregion
#if Microsoft_CONTROL
namespace System.Windows.Forms.DataVisualization.Charting.Utilities
#else
namespace System.Web.UI.DataVisualization.Charting.Utilities
#endif
{
#region Serialization enumerations
/// <summary>
/// Enumeration which describes how to persist property during the serialization
/// </summary>
internal enum SerializationVisibility
{
/// <summary>
/// Do not serialize
/// </summary>
Hidden,
/// <summary>
/// Serialize as XML attribute
/// </summary>
Attribute,
/// <summary>
/// Serialize as XML element
/// </summary>
Element
}
/// <summary>
/// Determines chart current serialization status.
/// </summary>
internal enum SerializationStatus
{
/// <summary>
/// Chart is not serializing
/// </summary>
None,
/// <summary>
/// Chart is loading
/// </summary>
Loading,
/// <summary>
/// Chart is saving
/// </summary>
Saving,
/// <summary>
/// Chart is resetting
/// </summary>
Resetting
}
#endregion
/// <summary>
/// Attribute which describes how to persist property during the serialization.
/// </summary>
[AttributeUsage(AttributeTargets.All)]
internal sealed class SerializationVisibilityAttribute : System.Attribute
{
#region Fields
// Visibility style
private SerializationVisibility _visibility = SerializationVisibility.Attribute;
#endregion
#region Constructor
/// <summary>
/// Public constructor
/// </summary>
/// <param name="visibility">Serialization visibility.</param>
internal SerializationVisibilityAttribute(SerializationVisibility visibility)
{
this._visibility = visibility;
}
#endregion
#region Properties
/// <summary>
/// Serialization visibility property
/// </summary>
public SerializationVisibility Visibility
{
get
{
return _visibility;
}
//set
//{
// _visibility = value;
//}
}
#endregion
}
/// <summary>
/// Base class of the serializers. Common properties and methods for all serializers.
/// </summary>
internal abstract class SerializerBase
{
#region Fields
/// <summary>
/// Indicates that unknown properties and elements are ignored
/// </summary>
private bool _isUnknownAttributeIgnored = false;
/// <summary>
/// Indicates that serializer works in template creation mode
/// </summary>
private bool _isTemplateMode = false;
/// <summary>
/// Indicates that object properties are reset before loading
/// </summary>
private bool _isResetWhenLoading = true;
/// <summary>
/// Comma separated list of serializable (Save/Load/Reset) properties. "ClassName.PropertyName"
/// </summary>
private string _serializableContent = "";
/// <summary>
/// Comma separated list of NON serializable (Save/Load/Reset) properties. "ClassName.PropertyName"
/// </summary>
private string _nonSerializableContent = "";
/// <summary>
/// Font converters used while serializing/deserializing
/// </summary>
internal static FontConverter fontConverter = new FontConverter();
/// <summary>
/// Color converters used while serializing/deserializing
/// </summary>
internal static ColorConverter colorConverter = new ColorConverter();
/// <summary>
/// Hash code provider.
/// </summary>
protected static StringComparer hashCodeProvider = StringComparer.OrdinalIgnoreCase;
/// <summary>
/// Contains chart specific converters
/// </summary>
HybridDictionary _converterDict = new HybridDictionary();
#endregion
#region Public properties
/// <summary>
/// Indicates that unknown properties and elements will be
/// ignored without throwing an exception.
/// </summary>
internal bool IsUnknownAttributeIgnored
{
get
{
return _isUnknownAttributeIgnored;
}
set
{
_isUnknownAttributeIgnored = value;
}
}
/// <summary>
/// Indicates that serializer works in template creation mode
/// </summary>
internal bool IsTemplateMode
{
get
{
return _isTemplateMode;
}
set
{
_isTemplateMode = value;
}
}
/// <summary>
/// Indicates that object properties are reset to default
/// values before loading.
/// </summary>
internal bool IsResetWhenLoading
{
get
{
return _isResetWhenLoading;
}
set
{
_isResetWhenLoading = value;
}
}
/// <summary>
/// Comma separated list of serializable (Save/Load/Reset) properties.
/// "ClassName.PropertyName,[ClassName.PropertyName]".
/// </summary>
internal string SerializableContent
{
get
{
return _serializableContent;
}
set
{
_serializableContent = value;
// Reset list
serializableContentList = null;
}
}
/// <summary>
/// Comma separated list of serializable (Save/Load/Reset) properties.
/// "ClassName.PropertyName,[ClassName.PropertyName]".
/// </summary>
internal string NonSerializableContent
{
get
{
return _nonSerializableContent;
}
set
{
_nonSerializableContent = value;
// Reset list
nonSerializableContentList = null;
}
}
#endregion
#region Resetting methods
/// <summary>
/// Reset properties of the object to default values.
/// </summary>
/// <param name="objectToReset">Object to be reset.</param>
virtual internal void ResetObjectProperties(object objectToReset)
{
// Reset object properties
ResetObjectProperties(objectToReset, null, GetObjectName(objectToReset));
}
/// <summary>
/// Reset properties of the object to default values.
/// Method is called recursively to reset child objects properties.
/// </summary>
/// <param name="objectToReset">Object to be reset.</param>
/// <param name="parent">Parent of the reset object.</param>
/// <param name="elementName">Object element name.</param>
virtual internal void ResetObjectProperties(object objectToReset, object parent, string elementName)
{
// Check input parameters
if(objectToReset == null)
{
return;
}
IList list = objectToReset as IList;
// Check if object is a list
if(list != null && IsSerializableContent(elementName, parent))
{
// Reset list by clearing all the items
list.Clear();
return;
}
// Retrive properties list of the object
PropertyInfo[] properties = objectToReset.GetType().GetProperties();
if(properties != null)
{
// Loop through all properties and reset public properties
foreach(PropertyInfo pi in properties)
{
// Get property descriptor
PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToReset)[pi.Name];
// Check XmlFormatSerializerStyle attribute
if(pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
// Hidden property
if(styleAttribute.Visibility == SerializationVisibility.Hidden)
{
continue;
}
}
}
// Check if this property should be reset
bool resetProperty = IsSerializableContent(pi.Name, objectToReset);
// Skip inherited properties from the root object
if(IsChartBaseProperty(objectToReset, parent, pi))
{
continue;
}
// Reset list
if(pi.CanRead && pi.PropertyType.GetInterface("IList", true) != null)
{
if(resetProperty)
{
// Check if collection has "Reset" method
bool resetComplete = false;
MethodInfo mi = objectToReset.GetType().GetMethod("Reset" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(mi != null)
{
mi.Invoke(objectToReset, null);
resetComplete = true;
}
// Reset list by clearing all the items
if(!resetComplete)
{
((IList)pi.GetValue(objectToReset, null)).Clear();
}
}
else
{
// Reset objects of the list
foreach(object listObject in ((IList)pi.GetValue(objectToReset, null)))
{
ResetObjectProperties(listObject, objectToReset, this.GetObjectName(listObject));
}
}
}
// Reset public properties with Get and Set methods
else if(pi.CanRead && pi.CanWrite)
{
// Skip indexes
if(pi.Name == "Item")
{
continue;
}
// Skip Names
if (pi.Name == "Name")
{
continue;
}
// Reset inner properies
if(ShouldSerializeAsAttribute(pi, objectToReset))
{
if(resetProperty)
{
// Reset the property using property descriptor
if(pd != null)
{
// Get property object
object objectProperty = pi.GetValue(objectToReset, null);
// Get default value of the property
DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
if(defValueAttribute != null)
{
if(objectProperty == null)
{
if(defValueAttribute.Value != null)
{
pd.SetValue(objectToReset, defValueAttribute.Value);
}
}
else if(! objectProperty.Equals(defValueAttribute.Value))
{
pd.SetValue(objectToReset, defValueAttribute.Value);
}
}
else
{
// Check if property has "Reset" method
MethodInfo mi = objectToReset.GetType().GetMethod("Reset" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(mi != null)
{
mi.Invoke(objectToReset, null);
}
}
}
}
}
else
{
// Reset inner object
ResetObjectProperties(pi.GetValue(objectToReset, null), objectToReset, pi.Name);
}
}
}
}
return;
}
#endregion
#region Abstract Serialization/Deserialization methods
/// <summary>
/// Serialize specified object into the destination object.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="destination">Destination of the serialization.</param>
internal abstract void Serialize(object objectToSerialize, object destination);
/// <summary>
/// Deserialize specified object from the source object.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="source">Source of the deserialization.</param>
internal abstract void Deserialize(object objectToDeserialize, object source);
#endregion
#region Protected helper methods
/// <summary>
/// Converts specified font object into a string.
/// </summary>
/// <param name="font">Font object to convert.</param>
/// <returns>String that contains font data.</returns>
internal static string FontToString(Font font)
{
// Save basic properties persisted by font converter
string fontData = (string)SerializerBase.fontConverter.ConvertToInvariantString(font);
// Persist properties not serialiazed by the converter
if(font.GdiCharSet != 1)
{
fontData += ", GdiCharSet=" + font.GdiCharSet.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
if(font.GdiVerticalFont)
{
fontData += ", GdiVerticalFont";
}
return fontData;
}
/// <summary>
/// Converts string data into a font object.
/// </summary>
/// <param name="fontString">String with font data.</param>
/// <returns>Newly created font object.</returns>
internal static Font FontFromString(string fontString)
{
// Check if string contains non-standard values "GdiCharSet" or "GdiVerticalFont"
string standardData = fontString;
byte gdiCharSet = 1;
bool gdiVerticalFont = false;
int charIndex = fontString.IndexOf(", GdiCharSet=", StringComparison.Ordinal);
if(charIndex >= 0)
{
// Read value
string val = fontString.Substring(charIndex + 13);
int commaIndex = val.IndexOf(",", StringComparison.Ordinal);
if(commaIndex >= 0)
{
val = val.Substring(0, commaIndex);
}
gdiCharSet = (byte)Int32.Parse(val, System.Globalization.CultureInfo.InvariantCulture);
// Truncate standard data string
if(standardData.Length > charIndex)
{
standardData = standardData.Substring(0, charIndex);
}
}
charIndex = fontString.IndexOf(", GdiVerticalFont", StringComparison.Ordinal);
if(charIndex >= 0)
{
gdiVerticalFont = true;
// Truncate standard data string
if(standardData.Length > charIndex)
{
standardData = standardData.Substring(0, charIndex);
}
}
// Create Font object from standard parameters
Font font = (Font)SerializerBase.fontConverter.ConvertFromInvariantString(standardData);
// check if non-standard parameters provided
if(gdiVerticalFont || gdiCharSet != 1)
{
Font newFont = new Font(
font.Name,
font.SizeInPoints,
font.Style,
GraphicsUnit.Point,
gdiCharSet,
gdiVerticalFont);
font.Dispose();
return newFont;
}
return font;
}
/// <summary>
/// Returns a hash code of a specified string.
/// </summary>
/// <param name="str">String to get the hash code for.</param>
/// <returns>String hash code.</returns>
internal static short GetStringHashCode(string str)
{
return (short)(hashCodeProvider.GetHashCode(str) + str.Length * 2);
}
/// <summary>
/// Reads hash ID from the specified binary reader.
/// </summary>
/// <param name="reader">Binary reader to get the data from.</param>
/// <returns>Property name or collection member type ID.</returns>
internal Int16 ReadHashID(BinaryReader reader)
{
// For later versions return ID without transformations
return reader.ReadInt16();
}
/// <summary>
/// Checks if property belongs to the base class of the chart "Control".
/// </summary>
/// <param name="objectToSerialize">Serializable object.</param>
/// <param name="parent">Object parent.</param>
/// <param name="pi">Serializable property information.</param>
/// <returns>True if property belongs to the base class.</returns>
internal bool IsChartBaseProperty(object objectToSerialize, object parent, PropertyInfo pi)
{
bool result = false;
// Check only for the root object
if(parent == null)
{
Type currentType = objectToSerialize.GetType();
while(currentType != null)
{
if(pi.DeclaringType == currentType)
{
result = false;
break;
}
// Check if it's a chart class
if( currentType == typeof(Chart))
{
result = true;
break;
}
// Get base class type
currentType = currentType.BaseType;
}
}
return result;
}
/// <summary>
/// Converts Image object into the BASE64 encoded string
/// </summary>
/// <param name="image">Image to convert.</param>
/// <returns>BASE64 encoded image data.</returns>
internal static string ImageToString(System.Drawing.Image image)
{
// Save image into the stream using BASE64 encoding
MemoryStream imageStream = new MemoryStream();
image.Save(imageStream, ImageFormat.Png);
imageStream.Seek(0, SeekOrigin.Begin);
// Create XmlTextWriter and save image in BASE64
StringBuilder stringBuilder = new StringBuilder();
XmlTextWriter textWriter = new XmlTextWriter(new StringWriter(stringBuilder, CultureInfo.InvariantCulture));
byte[] imageByteData = imageStream.ToArray();
textWriter.WriteBase64(imageByteData, 0, imageByteData.Length);
// Close image stream
textWriter.Close();
imageStream.Close();
return stringBuilder.ToString();
}
/// <summary>
/// Converts BASE64 encoded string to image.
/// </summary>
/// <param name="data">BASE64 encoded data.</param>
/// <returns>Image.</returns>
internal static System.Drawing.Image ImageFromString(string data)
{
// Create XML text reader
byte[] buffer = new byte[1000];
MemoryStream imageStream = new MemoryStream();
XmlTextReader textReader = new XmlTextReader(new StringReader("<base64>" + data + "</base64>"))
{
DtdProcessing = DtdProcessing.Ignore
};
// Read tags and BASE64 encoded data
textReader.Read();
int bytesRead = 0;
while((bytesRead = textReader.ReadBase64(buffer, 0, 1000)) > 0)
{
imageStream.Write(buffer, 0, bytesRead);
}
textReader.Read();
// Create image from stream
imageStream.Seek(0, SeekOrigin.Begin);
System.Drawing.Image tempImage = System.Drawing.Image.FromStream(imageStream);
System.Drawing.Bitmap image = new Bitmap(tempImage); // !!! .Net bug when image source stream is closed - can create brush using the image
image.SetResolution(tempImage.HorizontalResolution, tempImage.VerticalResolution); //The bitmap created using the constructor does not copy the resolution of the image
// Close image stream
textReader.Close();
imageStream.Close();
return image;
}
/// <summary>
/// Get the name of the object class
/// </summary>
/// <param name="obj">Object to get the name of.</param>
/// <returns>Name of the object class (without namespace).</returns>
internal string GetObjectName(object obj)
{
string name = obj.GetType().ToString();
return name.Substring(name.LastIndexOf('.') + 1);
}
/// <summary>
/// Create new empty item for the list.
/// AxisName of the objects is determined by the return type of the indexer.
/// </summary>
/// <param name="list">List used to detect type of the item objects.</param>
/// <param name="itemTypeName">Name of collection type.</param>
/// <param name="itemName">Optional item name to return.</param>
/// <param name="reusedObject">Indicates that object with specified name was already in the collection and it being reused.</param>
/// <returns>New list item object.</returns>
internal object GetListNewItem(IList list, string itemTypeName, ref string itemName, ref bool reusedObject)
{
// Get type of item in collection
Type itemType = null;
if(itemTypeName.Length > 0)
{
itemType = Type.GetType(typeof(Chart).Namespace + "." + itemTypeName, false, true);
}
reusedObject = false;
PropertyInfo pi = list.GetType().GetProperty("Item", itemType, new Type[] {typeof(string)} );
MethodInfo mi = list.GetType().GetMethod("IndexOf", new Type[] { typeof(String) });
ConstructorInfo ci = null;
if(pi != null)
{
// Try to get object by name using the indexer
if(itemName != null && itemName.Length > 0)
{
bool itemChecked = false;
if (mi != null)
{
try
{
int index = -1;
object oindex = mi.Invoke(list, new object[] { itemName });
if (oindex is int)
{
index = (int)oindex;
itemChecked = true;
}
if (index != -1)
{
object objByName = list[index];
if (objByName != null)
{
// Remove found object from the list
list.Remove(objByName);
// Return found object
reusedObject = true;
return objByName;
}
}
}
catch (ArgumentException)
{
}
catch (TargetException)
{
}
catch (TargetInvocationException)
{
}
}
if (!itemChecked)
{
object objByName = null;
try
{
objByName = pi.GetValue(list, new object[] { itemName });
}
catch (ArgumentException)
{
objByName = null;
}
catch (TargetException)
{
objByName = null;
}
catch (TargetInvocationException)
{
objByName = null;
}
if (objByName != null)
{
try
{
// Remove found object from the list
list.Remove(objByName);
}
catch (NotSupportedException)
{
}
// Return found object
reusedObject = true;
return objByName;
}
}
itemName = null;
}
}
// Get the constructor of the type returned by indexer
if (itemType != null)
{
ci = itemType.GetConstructor(Type.EmptyTypes);
}
else
{
ci = pi.PropertyType.GetConstructor(Type.EmptyTypes);
}
if (ci == null)
{
throw (new InvalidOperationException(SR.ExceptionChartSerializerDefaultConstructorUndefined(pi.PropertyType.ToString())));
}
return ci.Invoke(null);
}
/// <summary>
/// Returns true if the object property should be serialized as
/// parent element attribute. Otherwise as a child element.
/// </summary>
/// <param name="pi">Property information.</param>
/// <param name="parent">Object that the property belongs to.</param>
/// <returns>True if property should be serialized as attribute.</returns>
internal bool ShouldSerializeAsAttribute(PropertyInfo pi, object parent)
{
// Check if SerializationVisibilityAttribute is set
if(parent != null)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[pi.Name];
if(pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
if(styleAttribute.Visibility == SerializationVisibility.Attribute)
{
return true;
}
else if(styleAttribute.Visibility == SerializationVisibility.Element)
{
return false;
}
}
}
}
// If a simple type - serialize as property
if(!pi.PropertyType.IsClass)
{
return true;
}
// Some classes are serialized as properties
if(pi.PropertyType == typeof(string) ||
pi.PropertyType == typeof(Font) ||
pi.PropertyType == typeof(Color) ||
pi.PropertyType == typeof(System.Drawing.Image))
{
return true;
}
return false;
}
/// <summary>
/// Determines if this property should be serialized as attribute
/// </summary>
/// <param name="pi">Property information.</param>
/// <param name="objectToSerialize">Object that the property belongs to.</param>
/// <returns>True if should be serialized as attribute</returns>
internal bool SerializeICollAsAtribute(PropertyInfo pi, object objectToSerialize)
{
if (objectToSerialize != null)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
if (pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if (styleAttribute != null)
{
if (styleAttribute.Visibility == SerializationVisibility.Attribute)
{
return true;
}
}
}
}
return false;
}
/// <summary>
/// Returns true if the object property is serializable.
/// </summary>
/// <param name="propertyName">Property name.</param>
/// <param name="parent">Object that the property belongs to.</param>
/// <returns>True if property is serializable.</returns>
internal bool IsSerializableContent(string propertyName, object parent)
{
bool serializable = true;
if(_serializableContent.Length > 0 || _nonSerializableContent.Length > 0)
{
int serialzableClassFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
int serialzablePropertyFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
string ownerClassName = GetObjectName(parent);
// Check if property in this class is part of the serializable content
serializable = IsPropertyInList(GetSerializableContentList(), ownerClassName, propertyName, out serialzableClassFitType, out serialzablePropertyFitType);
// Check if property in this class is part of the NON serializable content
if(serializable)
{
int nonSerialzableClassFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
int nonSerialzablePropertyFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
bool nonSerializable = IsPropertyInList(GetNonSerializableContentList(), ownerClassName, propertyName, out nonSerialzableClassFitType, out nonSerialzablePropertyFitType);
// If property was found in non serializable content list - check the type priority
// Priority order: Exact match, 'Back*' mask match, '*' all mask match
if(nonSerializable)
{
// Check priority
if((nonSerialzableClassFitType + nonSerialzablePropertyFitType) >
(serialzableClassFitType + serialzablePropertyFitType))
{
serializable = false;
}
}
}
}
return serializable;
}
/// <summary>
/// Checks if property belongs is defined in the mask list.
/// </summary>
/// <param name="contentList">Array list of class/property items.</param>
/// <param name="className">Class name.</param>
/// <param name="propertyName">Property name.</param>
/// <param name="classFitType">Return class mask fit type.</param>
/// <param name="propertyFitType">Return property mask fit type.</param>
/// <returns>True if property was found in the list.</returns>
private bool IsPropertyInList(ArrayList contentList, string className, string propertyName, out int classFitType, out int propertyFitType)
{
// Initialize result values
classFitType = 0;
propertyFitType = 0;
if(contentList != null)
{
// Loop through all items in the list using step 2
for(int itemIndex = 0; itemIndex < contentList.Count; itemIndex += 2)
{
// Initialize result values
classFitType = 0;
propertyFitType = 0;
// Check if object class and property name match the mask
if(NameMatchMask((ItemInfo)contentList[itemIndex], className, out classFitType))
{
if(NameMatchMask((ItemInfo)contentList[itemIndex + 1], propertyName, out propertyFitType))
{
return true;
}
}
}
}
return false;
}
/// <summary>
/// Compares class/property name with the specified mask
/// </summary>
/// <param name="itemInfo">Class/Property item information.</param>
/// <param name="objectName">Class/Property name.</param>
/// <param name="type">AxisName of matching. 0-No Match; 1-'*' any wild card; 2-'Back*' contain wild card; 3-exact match</param>
/// <returns>True if name match the mask.</returns>
private bool NameMatchMask(ItemInfo itemInfo, string objectName, out int type)
{
// Initialize type
type = 0;
// Any class mask
if(itemInfo.any)
{
type = 1;
return true;
}
// Ends with class mask
if(itemInfo.endsWith)
{
if(itemInfo.name.Length <= objectName.Length)
{
if(objectName.Substring(0, itemInfo.name.Length) == itemInfo.name)
{
type = 2;
return true;
}
}
}
// Starts with class mask
if(itemInfo.startsWith)
{
if(itemInfo.name.Length <= objectName.Length)
{
if(objectName.Substring(objectName.Length - itemInfo.name.Length, itemInfo.name.Length) == itemInfo.name)
{
type = 2;
return true;
}
}
}
// Exact name is specified
if(itemInfo.name == objectName)
{
type = 3;
return true;
}
return false;
}
/// <summary>
/// Finds a converter by property descriptor.
/// </summary>
/// <param name="pd">Property descriptor.</param>
/// <returns>A converter registered in TypeConverterAttribute or by property type</returns>
internal TypeConverter FindConverter(PropertyDescriptor pd)
{
TypeConverter result;
TypeConverterAttribute typeConverterAttrib = (TypeConverterAttribute)pd.Attributes[typeof(TypeConverterAttribute)];
if (typeConverterAttrib != null && typeConverterAttrib.ConverterTypeName.Length > 0)
{
result = this.FindConverterByType(typeConverterAttrib);
if (result != null)
{
return result;
}
try
{
return pd.Converter;
}
catch (SecurityException)
{
}
catch (MethodAccessException)
{
}
}
return TypeDescriptor.GetConverter(pd.PropertyType);
}
/// <summary>
/// Finds a converter by TypeConverterAttribute.
/// </summary>
/// <param name="attr">TypeConverterAttribute.</param>
/// <returns>TypeConvetrer or null</returns>
internal TypeConverter FindConverterByType( TypeConverterAttribute attr)
{
// In default Inranet zone (partial trust) ConsrtuctorInfo.Invoke (PropertyDescriptor.Converter)
// throws SecurityException or MethodAccessException when the converter class is internal.
// Thats why we have this giant if - elseif here - to create type converters whitout reflection.
if (_converterDict.Contains(attr.ConverterTypeName))
{
return (TypeConverter)_converterDict[attr.ConverterTypeName];
}
String typeStr = attr.ConverterTypeName;
if (attr.ConverterTypeName.Contains(",") )
{
typeStr = attr.ConverterTypeName.Split(',')[0];
}
TypeConverter result = null;
if (typeStr.EndsWith(".CustomPropertiesTypeConverter", StringComparison.OrdinalIgnoreCase)) { result = new CustomPropertiesTypeConverter(); }
else if (typeStr.EndsWith(".DoubleNanValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new DoubleNanValueConverter(); }
else if (typeStr.EndsWith(".DoubleDateNanValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new DoubleDateNanValueConverter(); }
#if !Microsoft_CONTROL
else if (typeStr.EndsWith(".MapAreaCoordinatesConverter", StringComparison.OrdinalIgnoreCase)) { result = new MapAreaCoordinatesConverter(); }
#endif //Microsoft_CONTROL
else if (typeStr.EndsWith(".ElementPositionConverter", StringComparison.OrdinalIgnoreCase)) { result = new ElementPositionConverter(); }
else if (typeStr.EndsWith(".SeriesAreaNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesAreaNameConverter(); }
else if (typeStr.EndsWith(".ChartDataSourceConverter", StringComparison.OrdinalIgnoreCase)) { result = new ChartDataSourceConverter(); }
else if (typeStr.EndsWith(".SeriesDataSourceMemberConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesDataSourceMemberConverter(); }
else if (typeStr.EndsWith(".SeriesLegendNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesLegendNameConverter(); }
else if (typeStr.EndsWith(".ChartTypeConverter", StringComparison.OrdinalIgnoreCase)) { result = new ChartTypeConverter(); }
else if (typeStr.EndsWith(".SeriesNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesNameConverter(); }
else if (typeStr.EndsWith(".NoNameExpandableObjectConverter", StringComparison.OrdinalIgnoreCase)) { result = new NoNameExpandableObjectConverter(); }
else if (typeStr.EndsWith(".DoubleArrayConverter", StringComparison.OrdinalIgnoreCase)) { result = new DoubleArrayConverter(); }
else if (typeStr.EndsWith(".DataPointValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new DataPointValueConverter(); }
else if (typeStr.EndsWith(".SeriesYValueTypeConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesYValueTypeConverter(typeof(ChartValueType)); }
else if (typeStr.EndsWith(".ColorArrayConverter", StringComparison.OrdinalIgnoreCase)) { result = new ColorArrayConverter(); }
else if (typeStr.EndsWith(".LegendAreaNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new LegendAreaNameConverter(); }
else if (typeStr.EndsWith(".LegendConverter", StringComparison.OrdinalIgnoreCase)) { result = new LegendConverter(); }
else if (typeStr.EndsWith(".SizeEmptyValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new SizeEmptyValueConverter(); }
else if (typeStr.EndsWith(".MarginExpandableObjectConverter", StringComparison.OrdinalIgnoreCase)) { result = new MarginExpandableObjectConverter(); }
else if (typeStr.EndsWith(".IntNanValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new IntNanValueConverter(); }
else if (typeStr.EndsWith(".AxesArrayConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxesArrayConverter(); }
else if (typeStr.EndsWith(".AxisLabelDateValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisLabelDateValueConverter(); }
else if (typeStr.EndsWith(".AxisMinMaxValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisMinMaxValueConverter(); }
else if (typeStr.EndsWith(".AxisCrossingValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisCrossingValueConverter(); }
else if (typeStr.EndsWith(".AxisMinMaxAutoValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisMinMaxAutoValueConverter(); }
else if (typeStr.EndsWith(".StripLineTitleAngleConverter", StringComparison.OrdinalIgnoreCase)) { result = new StripLineTitleAngleConverter(); }
else if (typeStr.EndsWith(".AxisIntervalValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisIntervalValueConverter(); }
else if (typeStr.EndsWith(".AxisElementIntervalValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisElementIntervalValueConverter(); }
else if (typeStr.EndsWith(".AnchorPointValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AnchorPointValueConverter(); }
else if (typeStr.EndsWith(".AnnotationAxisValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AnnotationAxisValueConverter(); }
if (result != null) _converterDict[attr.ConverterTypeName] = result;
return result;
}
#endregion
#region Serializable content list managment fields, methods and classes
/// <summary>
/// Stores information about content item (class or property)
/// </summary>
private class ItemInfo
{
public string name = "";
public bool any = false;
public bool startsWith = false;
public bool endsWith = false;
}
// Storage for serializable content items
private ArrayList serializableContentList = null;
// Storage for non serializable content items
private ArrayList nonSerializableContentList = null;
/// <summary>
/// Return serializable content list.
/// </summary>
/// <returns>Serializable content list.</returns>
private ArrayList GetSerializableContentList()
{
if(serializableContentList == null)
{
serializableContentList = new ArrayList();
FillContentList(
serializableContentList,
(this.SerializableContent.Length > 0 ) ? this.SerializableContent : "*.*");
}
return serializableContentList;
}
/// <summary>
/// Return non serializable content list.
/// </summary>
/// <returns>Non serializable content list.</returns>
private ArrayList GetNonSerializableContentList()
{
if(nonSerializableContentList == null)
{
nonSerializableContentList = new ArrayList();
FillContentList(nonSerializableContentList, this.NonSerializableContent);
}
return nonSerializableContentList;
}
/// <summary>
/// Fill content list from the string.
/// </summary>
/// <param name="list">Array list class.</param>
/// <param name="content">Content string.</param>
private void FillContentList(ArrayList list, string content)
{
if(content.Length > 0)
{
string[] classPropertyPairs = content.Split(',');
foreach(string item in classPropertyPairs)
{
// Create two content items: one for the class and one for the property
ItemInfo classInfo = new ItemInfo();
ItemInfo propertyInfo = new ItemInfo();
// Find class and property name
int pointIndex = item.IndexOf('.');
if(pointIndex == -1)
{
throw (new ArgumentException(SR.ExceptionChartSerializerContentStringFormatInvalid));
}
classInfo.name = item.Substring(0, pointIndex).Trim();
propertyInfo.name = item.Substring(pointIndex + 1).Trim();
if(classInfo.name.Length == 0)
{
throw (new ArgumentException(SR.ExceptionChartSerializerClassNameUndefined));
}
if(propertyInfo.name.Length == 0)
{
throw (new ArgumentException(SR.ExceptionChartSerializerPropertyNameUndefined));
}
// Make sure property name do not have point character
if(propertyInfo.name.IndexOf('.') != -1)
{
throw (new ArgumentException(SR.ExceptionChartSerializerContentStringFormatInvalid));
}
// Check for wildcards in names
CheckWildCars(classInfo);
CheckWildCars(propertyInfo);
// Add class & property items into the array
list.Add(classInfo);
list.Add(propertyInfo);
}
}
}
/// <summary>
/// Checks wildcards in the name of the item.
/// Possible values:
/// "*"
/// "*Name"
/// "Name*"
/// </summary>
/// <param name="info">Item information class.</param>
private void CheckWildCars(ItemInfo info)
{
// Any class mask
if(info.name == "*")
{
info.any = true;
}
// Ends with class mask
else if(info.name[info.name.Length - 1] == '*')
{
info.endsWith = true;
info.name = info.name.TrimEnd('*');
}
// Starts with class mask
else if(info.name[0] == '*')
{
info.startsWith = true;
info.name = info.name.TrimStart('*');
}
}
#endregion
}
/// <summary>
/// Utility class which serialize object using XML format
/// </summary>
internal class XmlFormatSerializer : SerializerBase
{
#region Serialization public methods
/// <summary>
/// Serialize specified object into the stream.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="stream">The stream used to write the XML document.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Serialize(object objectToSerialize, Stream stream)
{
Serialize(objectToSerialize, (object)stream);
}
/// <summary>
/// Serialize specified object into the XML writer.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="xmlWriter">The XmlWriter used to write the XML document.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Serialize(object objectToSerialize, XmlWriter xmlWriter)
{
Serialize(objectToSerialize, (object)xmlWriter);
}
/// <summary>
/// Serialize specified object into the text writer.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="textWriter">The TextWriter used to write the XML document.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Serialize(object objectToSerialize, TextWriter textWriter)
{
Serialize(objectToSerialize, (object)textWriter);
}
/// <summary>
/// Serialize specified object into the file.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="fileName">The file name used to write the XML document.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Serialize(object objectToSerialize, string fileName)
{
Serialize(objectToSerialize, (object)fileName);
}
#endregion
#region Serialization private methods
/// <summary>
/// Serialize specified object into different types of writers using XML format.
/// Here is what is serialized in the object:
/// - all public properties with Set and Get methods
/// - all public properties with Get method which derived from ICollection
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="writer">Defines the serialization destination. Can be Stream, TextWriter, XmlWriter or String (file name).</param>
internal override void Serialize(object objectToSerialize, object writer)
{
// the possible writer types
Stream stream = writer as Stream;
TextWriter textWriter = writer as TextWriter;
XmlWriter xmlWriter = writer as XmlWriter;
string writerStr = writer as string;
// Check input parameters
if(objectToSerialize == null)
{
throw(new ArgumentNullException("objectToSerialize"));
}
if(writer == null)
{
throw(new ArgumentNullException("writer"));
}
if(stream == null && textWriter == null && xmlWriter == null && writerStr == null)
{
throw (new ArgumentException(SR.ExceptionChartSerializerWriterObjectInvalid, "writer"));
}
// Create XML document
XmlDocument xmlDocument = new XmlDocument();
// Create document fragment
XmlDocumentFragment docFragment = xmlDocument.CreateDocumentFragment();
// Serialize object
SerializeObject(objectToSerialize, null, GetObjectName(objectToSerialize), docFragment, xmlDocument);
// Append document fragment
xmlDocument.AppendChild(docFragment);
// Remove empty child nodes
RemoveEmptyChildNodes(xmlDocument);
// Save XML document into the writer
if(stream != null)
{
xmlDocument.Save(stream);
// Flush stream and seek to the beginning
stream.Flush();
stream.Seek(0, SeekOrigin.Begin);
}
if(writerStr != null)
{
xmlDocument.Save(writerStr);
}
if(xmlWriter != null)
{
xmlDocument.Save(xmlWriter);
}
if(textWriter != null)
{
xmlDocument.Save(textWriter);
}
}
/// <summary>
/// Serialize specified object into the XML format.
/// Method is called recursively to serialize child objects.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="parent">Parent of the serialized object.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="xmlParentNode">The XmlNode of the parent object to serialize the data in.</param>
/// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
virtual protected void SerializeObject(object objectToSerialize, object parent, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
{
// Check input parameters
if(objectToSerialize == null)
{
return;
}
// Check if object should be serialized
if(parent != null)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
if(pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
// Hidden property
if(styleAttribute.Visibility == SerializationVisibility.Hidden)
{
return;
}
}
}
}
// Check if object is a collection
if(objectToSerialize is ICollection)
{
// Serialize collection
SerializeCollection(objectToSerialize, elementName, xmlParentNode, xmlDocument);
return;
}
// Create object element inside the parents node
XmlNode xmlNode = xmlDocument.CreateElement(elementName);
xmlParentNode.AppendChild(xmlNode);
// Write template data into collection items
bool templateListItem = false;
IList parentList = parent as IList;
if(this.IsTemplateMode && parentList != null)
{
// Create "_Template_" attribute
XmlAttribute attrib = xmlDocument.CreateAttribute("_Template_");
// Check number of items in collection
if (parentList.Count == 1)
{
// If only one iten in collection, set "All" value.
// This means that style of this object should be applied to all
// existing items of the collection.
attrib.Value = "All";
}
else
{
// If there is more than one item, use it's index.
// When loading, style of these items will be applied to existing
// items in collection in the loop.
int itemIndex = parentList.IndexOf(objectToSerialize);
attrib.Value = itemIndex.ToString(CultureInfo.InvariantCulture);
}
// Add "_Template_" attribute into the XML node
xmlNode.Attributes.Append(attrib);
templateListItem = true;
}
// Retrive properties list of the object
PropertyInfo[] properties = objectToSerialize.GetType().GetProperties();
if (properties != null)
{
// Loop through all properties and serialize public properties
foreach(PropertyInfo pi in properties)
{
// Skip "Name" property from collection items in template mode
if(templateListItem && pi.Name == "Name")
{
continue;
}
// Skip inherited properties from the root object
if(IsChartBaseProperty(objectToSerialize, parent, pi))
{
continue;
}
// Check if this property is serializable content
if (!IsSerializableContent(pi.Name, objectToSerialize))
{
continue;
}
// Serialize collection
if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null && !this.SerializeICollAsAtribute(pi, objectToSerialize))
{
// Check if SerializationVisibilityAttribute is set
bool serialize = true;
if(objectToSerialize != null)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
if(pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
if(styleAttribute.Visibility == SerializationVisibility.Hidden)
{
serialize = false;
}
}
}
}
// Check if collection has "ShouldSerialize" method
MethodInfo mi = objectToSerialize.GetType().GetMethod("ShouldSerialize" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
if(mi != null)
{
object result = mi.Invoke(objectToSerialize, null);
if(result is bool && ((bool)result) == false)
{
// Do not serialize collection
serialize = false;
}
}
// Serialize collection
if(serialize)
{
SerializeCollection(pi.GetValue(objectToSerialize, null), pi.Name, xmlNode, xmlDocument);
}
}
// Serialize public properties with Get and Set methods
else if(pi.CanRead && pi.CanWrite)
{
// Skip indexes
if(pi.Name == "Item")
{
continue;
}
// Check if an object should be serialized as a property or as a class
if(ShouldSerializeAsAttribute(pi, objectToSerialize))
{
// Serialize property
SerializeProperty(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, xmlNode, xmlDocument);
}
else
{
// Serialize inner object
SerializeObject(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, xmlNode, xmlDocument);
}
}
}
}
return;
}
/// <summary>
/// Serializes the data point.
/// </summary>
/// <param name="objectToSerialize">The object to serialize.</param>
/// <param name="xmlParentNode">The XML parent node.</param>
/// <param name="xmlDocument">The XML document.</param>
internal void SerializeDataPoint(object objectToSerialize, XmlNode xmlParentNode, XmlDocument xmlDocument)
{
// Create object element inside the parents node
XmlNode xmlNode = xmlDocument.CreateElement(GetObjectName(objectToSerialize));
xmlParentNode.AppendChild(xmlNode);
DataPoint dataPoint = objectToSerialize as DataPoint;
if (dataPoint.XValue != 0d && IsSerializableContent("XValue", objectToSerialize))
{
XmlAttribute attrib = xmlDocument.CreateAttribute("XValue");
attrib.Value = GetXmlValue(dataPoint.XValue, dataPoint, "XValue");
xmlNode.Attributes.Append(attrib);
}
if (dataPoint.YValues.Length > 0 && IsSerializableContent("YValues", objectToSerialize))
{
XmlAttribute attrib = xmlDocument.CreateAttribute("YValues");
attrib.Value = GetXmlValue(dataPoint.YValues, dataPoint, "YValues");
xmlNode.Attributes.Append(attrib);
}
if (dataPoint.IsEmpty && IsSerializableContent("IsEmpty", objectToSerialize))
{
XmlAttribute attrib = xmlDocument.CreateAttribute("IsEmpty");
attrib.Value = GetXmlValue(dataPoint.isEmptyPoint, dataPoint, "IsEmpty");
xmlNode.Attributes.Append(attrib);
}
bool hasCustomProperties = false;
foreach (DictionaryEntry entry in dataPoint.properties)
{
if (entry.Key is int)
{
CommonCustomProperties propertyType = (CommonCustomProperties)((int)entry.Key);
String properyName = propertyType.ToString();
if (IsSerializableContent(properyName, objectToSerialize))
{
XmlAttribute attrib = xmlDocument.CreateAttribute(properyName);
attrib.Value = GetXmlValue(entry.Value, dataPoint, properyName);
xmlNode.Attributes.Append(attrib);
}
}
else
{
hasCustomProperties = true;
}
}
if (hasCustomProperties && !String.IsNullOrEmpty(dataPoint.CustomProperties) && IsSerializableContent("CustomProperties", objectToSerialize))
{
XmlAttribute attrib = xmlDocument.CreateAttribute("CustomProperties");
attrib.Value = GetXmlValue(dataPoint.CustomProperties, dataPoint, "CustomProperties");
xmlNode.Attributes.Append(attrib);
}
}
/// <summary>
/// Serialize specified object into the XML text writer.
/// Method is called recursively to serialize child objects.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="xmlParentNode">The XmlNode of the parent object to serialize the data in.</param>
/// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
virtual protected void SerializeCollection(object objectToSerialize, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
{
ICollection collection = objectToSerialize as ICollection;
if(collection != null)
{
// Create object element inside the parents node
XmlNode xmlNode = xmlDocument.CreateElement(elementName);
xmlParentNode.AppendChild(xmlNode);
// Enumerate through all objects in collection and serialize them
foreach(object obj in collection)
{
if (obj is DataPoint)
{
SerializeDataPoint(obj, xmlNode, xmlDocument);
continue;
}
SerializeObject(obj, objectToSerialize, GetObjectName(obj), xmlNode, xmlDocument);
}
}
}
/// <summary>
/// Serialize specified object into the XML text writer.
/// Method is called recursively to serialize child objects.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="parent">Parent of the serialized object.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="xmlParentNode">The XmlNode of the parent object to serialize the data in.</param>
/// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
virtual protected void SerializeProperty(object objectToSerialize, object parent, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
{
// Check input parameters
if(objectToSerialize == null || parent == null)
{
return;
}
// Check if property has non-default value
PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
if(pd != null)
{
DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
if(defValueAttribute != null)
{
if(objectToSerialize.Equals(defValueAttribute.Value))
{
// Do not serialize properties with default values
return;
}
}
else
{
// Check if property has "ShouldSerialize" method
MethodInfo mi = parent.GetType().GetMethod("ShouldSerialize" + elementName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(mi != null)
{
object result = mi.Invoke(parent, null);
if(result is bool && ((bool)result) == false)
{
// Do not serialize properties with default values
return;
}
}
}
// Check XmlFormatSerializerStyle attribute
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
// Hidden property
if(styleAttribute.Visibility == SerializationVisibility.Hidden)
{
return;
}
}
}
// Serialize property as a parents node attribute
XmlAttribute attrib = xmlDocument.CreateAttribute(elementName);
attrib.Value = GetXmlValue(objectToSerialize, parent, elementName);
xmlParentNode.Attributes.Append(attrib);
}
/// <summary>
/// Converts object value into the string.
/// </summary>
/// <param name="obj">Object to convert.</param>
/// <param name="parent">Object parent.</param>
/// <param name="elementName">Object name.</param>
/// <returns>Object value as strig.</returns>
protected string GetXmlValue(object obj, object parent, string elementName)
{
string objStr = obj as string;
if(objStr != null)
{
return objStr;
}
Font font = obj as Font;
if(font != null)
{
return SerializerBase.FontToString(font);
}
if(obj is Color)
{
return colorConverter.ConvertToString(null, System.Globalization.CultureInfo.InvariantCulture, obj);
}
Color[] colors = obj as Color[];
if(colors != null)
{
return ColorArrayConverter.ColorArrayToString(colors);
}
#if !Microsoft_CONTROL
if(obj is Unit)
{
Unit unit = (Unit)obj;
return unit.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
#endif
System.Drawing.Image image = obj as System.Drawing.Image;
if(image != null)
{
return ImageToString(image);
}
// Look for the converter set with the attibute
PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
if(pd != null)
{
TypeConverter converter = this.FindConverter(pd);
if (converter != null && converter.CanConvertTo(typeof(string)))
{
return converter.ConvertToString(null, System.Globalization.CultureInfo.InvariantCulture, obj);
}
}
// Try using default string convertion
return obj.ToString();
}
/// <summary>
/// Removes all empty nodes from the XML document.
/// Method is called recursively to remove empty child nodes first.
/// </summary>
/// <param name="xmlNode">The node where to start the removing.</param>
private void RemoveEmptyChildNodes(XmlNode xmlNode)
{
// Loop through all child nodes
for(int nodeIndex = 0; nodeIndex < xmlNode.ChildNodes.Count; nodeIndex++)
{
// Remove empty child nodes of the child
RemoveEmptyChildNodes(xmlNode.ChildNodes[nodeIndex]);
// Check if there are any non-empty nodes left
XmlNode currentNode = xmlNode.ChildNodes[nodeIndex];
if( currentNode.ParentNode != null &&
!(currentNode.ParentNode is XmlDocument) )
{
if(!currentNode.HasChildNodes &&
(currentNode.Attributes == null ||
currentNode.Attributes.Count == 0))
{
// Remove node
xmlNode.RemoveChild(xmlNode.ChildNodes[nodeIndex]);
--nodeIndex;
}
}
// Remove node with one "_Template_" attribute
if(!currentNode.HasChildNodes &&
currentNode.Attributes.Count == 1 &&
currentNode.Attributes["_Template_"] != null)
{
// Remove node
xmlNode.RemoveChild(xmlNode.ChildNodes[nodeIndex]);
--nodeIndex;
}
}
}
#endregion
#region Deserialization public methods
/// <summary>
/// Deserialize specified object from the stream.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="stream">The stream used to read the XML document from.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Deserialize(object objectToDeserialize, Stream stream)
{
Deserialize(objectToDeserialize, (object)stream);
}
/// <summary>
/// Deserialize specified object from the XML reader.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="xmlReader">The XmlReader used to read the XML document from.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Deserialize(object objectToDeserialize, XmlReader xmlReader)
{
Deserialize(objectToDeserialize, (object)xmlReader);
}
/// <summary>
/// Deserialize specified object from the text reader.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="textReader">The TextReader used to write the XML document from.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Deserialize(object objectToDeserialize, TextReader textReader)
{
Deserialize(objectToDeserialize, (object)textReader);
}
/// <summary>
/// Deserialize specified object from the file.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="fileName">The file name used to read the XML document from.</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal void Deserialize(object objectToDeserialize, string fileName)
{
Deserialize(objectToDeserialize, (object)fileName);
}
#endregion
#region Deserialization private methods
/// <summary>
/// Deserialize object from different types of readers using XML format.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="reader">Defines the deserialization data source. Can be Stream, TextReader, XmlReader or String (file name).</param>
internal override void Deserialize(object objectToDeserialize, object reader)
{
// the four possible types of readers
Stream stream = reader as Stream;
TextReader textReader = reader as TextReader;
XmlReader xmlReader = reader as XmlReader;
string readerStr = reader as string;
// Check input parameters
if(objectToDeserialize == null)
{
throw(new ArgumentNullException("objectToDeserialize"));
}
if(reader == null)
{
throw(new ArgumentNullException("reader"));
}
if(stream == null && textReader == null && xmlReader == null && readerStr == null)
{
throw (new ArgumentException(SR.ExceptionChartSerializerReaderObjectInvalid, "reader"));
}
// Create XML document
XmlDocument xmlDocument = new XmlDocument();
XmlReader xmlBaseReader = null;
try
{
// process files without DTD
XmlReaderSettings settings = new XmlReaderSettings();
// settings.ProhibitDtd is obsolete inn NetFx 4.0, the #ifdef stays for compilation under NetFx 3.5.
#if OLD_DTD
settings.ProhibitDtd = true;
#else
settings.DtdProcessing = DtdProcessing.Prohibit; //don't allow DTD
#endif
// Load XML document from the reader
if (stream != null)
{
xmlBaseReader = XmlReader.Create(stream, settings);
}
if (readerStr != null)
{
xmlBaseReader = XmlReader.Create(readerStr, settings);
}
if (xmlReader != null)
{
xmlBaseReader = XmlReader.Create(xmlReader, settings);
}
if (textReader != null)
{
xmlBaseReader = XmlReader.Create(textReader, settings);
}
xmlDocument.Load(xmlBaseReader);
// Reset properties of the root object
if (IsResetWhenLoading)
{
ResetObjectProperties(objectToDeserialize);
}
// Deserialize object
DeserializeObject(objectToDeserialize, null, GetObjectName(objectToDeserialize), xmlDocument.DocumentElement, xmlDocument);
}
finally
{
if (xmlBaseReader != null)
{
((IDisposable)xmlBaseReader).Dispose();
}
}
}
/// <summary>
/// Deserialize object from the XML format.
/// Method is called recursively to deserialize child objects.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="parent">Parent of the deserialized object.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="xmlParentNode">The XmlNode of the parent object to deserialize the data from.</param>
/// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
/// <returns>Number of properties set.</returns>
virtual internal int DeserializeObject(object objectToDeserialize, object parent, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
{
int setPropertiesNumber = 0;
// Check input parameters
if(objectToDeserialize == null)
{
return setPropertiesNumber;
}
// Loop through all node properties
foreach(XmlAttribute attr in xmlParentNode.Attributes)
{
// Skip template collection item attribute
if(attr.Name == "_Template_")
{
continue;
}
// Check if this property is serializable content
if(IsSerializableContent(attr.Name, objectToDeserialize))
{
SetXmlValue(objectToDeserialize, attr.Name, attr.Value);
++setPropertiesNumber;
}
}
// Read template data into the collection
IList list = objectToDeserialize as IList;
if(this.IsTemplateMode &&
list != null &&
xmlParentNode.FirstChild.Attributes["_Template_"] != null)
{
// Loop through all items in collection
int itemIndex = 0;
foreach(object listItem in list)
{
// Find XML node appropriate for the item from the collection
XmlNode listItemNode = null;
// Loop through all child nodes
foreach(XmlNode childNode in xmlParentNode.ChildNodes)
{
string templateString = childNode.Attributes["_Template_"].Value;
if(templateString != null && templateString.Length > 0)
{
if(templateString == "All")
{
listItemNode = childNode;
break;
}
else
{
// If there is more items in collection than XML node in template
// apply items in a loop
int loopItemIndex = itemIndex;
while(loopItemIndex > xmlParentNode.ChildNodes.Count - 1)
{
loopItemIndex -= xmlParentNode.ChildNodes.Count;
}
// Convert attribute value to index
int nodeIndex = int.Parse(templateString, CultureInfo.InvariantCulture);
if(nodeIndex == loopItemIndex)
{
listItemNode = childNode;
break;
}
}
}
}
// Load data from the node
if(listItemNode != null)
{
// Load object data
DeserializeObject(listItem, objectToDeserialize, "", listItemNode, xmlDocument);
}
// Increase item index
++itemIndex;
}
// No futher loading required
return 0;
}
// Loop through all child elements
int listItemIndex = 0;
foreach(XmlNode childNode in xmlParentNode.ChildNodes)
{
// Special handling for the collections
// Bug VSTS #235707 - The collections IsSerializableContent are already checked as a property in the else statement.
if (list != null)
{
// Create new item object
string itemName = null;
if (childNode.Attributes["Name"] != null)
{
itemName = childNode.Attributes["Name"].Value;
}
bool reusedObject = false;
object listItem = GetListNewItem(list, childNode.Name, ref itemName, ref reusedObject);
// Deserialize list item object
int itemSetProperties = DeserializeObject(listItem, objectToDeserialize, "", childNode, xmlDocument);
setPropertiesNumber += itemSetProperties;
// Add item object into the list
if (itemSetProperties > 0 || reusedObject)
{
list.Insert(listItemIndex++, listItem);
}
}
else
{
// Check if this property is serializable content
if (IsSerializableContent(childNode.Name, objectToDeserialize))
{
// Deserialize the property using property descriptor
PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToDeserialize)[childNode.Name];
if (pd != null)
{
object innerObject = pd.GetValue(objectToDeserialize);
// Deserialize list item object
setPropertiesNumber += DeserializeObject(innerObject, objectToDeserialize, childNode.Name, childNode, xmlDocument);
}
else if (!IsUnknownAttributeIgnored)
{
throw (new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown(childNode.Name, objectToDeserialize.GetType().ToString())));
}
}
}
}
return setPropertiesNumber;
}
/// <summary>
/// Sets a property of an object using name and value as string.
/// </summary>
/// <param name="obj">Object to set.</param>
/// <param name="attrName">Attribute (property) name.</param>
/// <param name="attrValue">Object value..</param>
/// <returns>Object value as strig.</returns>
private void SetXmlValue(object obj, string attrName, string attrValue)
{
PropertyInfo pi = obj.GetType().GetProperty(attrName);
if(pi != null)
{
// Convert string to object value
object objValue = attrValue;
if(pi.PropertyType == typeof(string))
{
objValue = attrValue;
}
else if(pi.PropertyType == typeof(Font))
{
objValue = SerializerBase.FontFromString(attrValue);
}
else if(pi.PropertyType == typeof(Color))
{
objValue = (Color)colorConverter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, attrValue);
}
#if !Microsoft_CONTROL
else if(pi.PropertyType == typeof(Unit))
{
objValue = new Unit(Int32.Parse(attrValue, System.Globalization.CultureInfo.InvariantCulture));
}
#endif
else if(pi.PropertyType == typeof(System.Drawing.Image))
{
objValue = ImageFromString(attrValue);
}
else
{
// Look for the converter set with the attibute
PropertyDescriptor pd = TypeDescriptor.GetProperties(obj)[attrName];
if(pd != null)
{
TypeConverter converter = this.FindConverter(pd);
if (converter != null && converter.CanConvertFrom(typeof(string)))
{
objValue = converter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, attrValue);
}
}
}
// Set object value
pi.SetValue(obj, objValue, null);
}
else if(!IsUnknownAttributeIgnored)
{
throw(new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown( attrName,obj.GetType().ToString())));
}
}
#endregion
}
/// <summary>
/// Utility class which serialize object using binary format
/// </summary>
internal class BinaryFormatSerializer : SerializerBase
{
#region Serialization methods
/// <summary>
/// Serialize specified object into the destination using binary format.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="destination">Serialization destination.</param>
internal override void Serialize(object objectToSerialize, object destination)
{
// Check input parameters
if (objectToSerialize == null)
{
throw (new ArgumentNullException("objectToSerialize"));
}
if (destination == null)
{
throw (new ArgumentNullException("destination"));
}
string destinationStr = destination as string;
if (destinationStr != null)
{
Serialize(objectToSerialize, destinationStr);
return;
}
Stream stream = destination as Stream;
if (stream != null)
{
Serialize(objectToSerialize, stream);
return;
}
BinaryWriter binaryWriter = destination as BinaryWriter;
if (binaryWriter != null)
{
Serialize(objectToSerialize, binaryWriter);
return;
}
throw (new ArgumentException(SR.ExceptionChartSerializerDestinationObjectInvalid, "destination"));
}
/// <summary>
/// Serialize specified object into the file using binary format.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="fileName">File name to serialize the data in.</param>
internal void Serialize(object objectToSerialize, string fileName)
{
FileStream stream = new FileStream(fileName, FileMode.Create);
Serialize(objectToSerialize, new BinaryWriter(stream));
stream.Close();
}
/// <summary>
/// Serialize specified object into the stream using binary format.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="stream">Defines the serialization destination.</param>
internal void Serialize(object objectToSerialize, Stream stream)
{
Serialize(objectToSerialize, new BinaryWriter(stream));
}
/// <summary>
/// Serialize specified object into different types of writers using binary format.
/// Here is what is serialized in the object:
/// - all public properties with Set and Get methods
/// - all public properties with Get method which derived from ICollection
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="writer">Defines the serialization destination.</param>
internal void Serialize(object objectToSerialize, BinaryWriter writer)
{
// Check input parameters
if(objectToSerialize == null)
{
throw(new ArgumentNullException("objectToSerialize"));
}
if(writer == null)
{
throw(new ArgumentNullException("writer"));
}
// Write bnary format header into the stream, which consist of 15 characters
char[] header = new char[15] {'D', 'C', 'B', 'F', '4', '0', '0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'};
writer.Write(header);
// Serialize object
SerializeObject(objectToSerialize, null, GetObjectName(objectToSerialize), writer);
// Flush the writer stream
writer.Flush();
// Reset stream position
writer.Seek(0, SeekOrigin.Begin);
}
/// <summary>
/// Serialize specified object into the binary format.
/// Method is called recursively to serialize child objects.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="parent">Parent of the serialized object.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="writer">Binary writer object.</param>
virtual internal void SerializeObject(object objectToSerialize, object parent, string elementName, BinaryWriter writer)
{
// Check input parameters
if(objectToSerialize == null)
{
return;
}
// Check if object should be serialized
if(parent != null)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
if(pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
// Hidden property
if(styleAttribute.Visibility == SerializationVisibility.Hidden)
{
return;
}
}
}
}
// Check if object is a collection
if(objectToSerialize is ICollection)
{
// Serialize collection
SerializeCollection(objectToSerialize, elementName, writer);
return;
}
// Write object ID (hash of the name) into the writer
writer.Write(SerializerBase.GetStringHashCode(elementName));
// Remember position where object data is started
long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
// Retrive properties list of the object
ArrayList propNamesList = new ArrayList();
PropertyInfo[] properties = objectToSerialize.GetType().GetProperties();
if(properties != null)
{
// Loop through all properties and serialize public properties
foreach(PropertyInfo pi in properties)
{
// Skip inherited properties from the root object
if(IsChartBaseProperty(objectToSerialize, parent, pi))
{
continue;
}
// Serialize collection
if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null && !this.SerializeICollAsAtribute(pi, objectToSerialize))
{
bool serialize = IsSerializableContent(pi.Name, objectToSerialize);
// fixing Axes Array Framework 2.0 side effect
// fixed by:DT
if (serialize && objectToSerialize != null)
{
PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
if (pd != null)
{
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if (styleAttribute != null)
{
if (styleAttribute.Visibility == SerializationVisibility.Hidden)
{
serialize = false;
}
}
}
}
MethodInfo mi = objectToSerialize.GetType().GetMethod("ShouldSerialize" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if( serialize && mi != null)
{
object result = mi.Invoke(objectToSerialize, null);
if(result is bool && ((bool)result) == false)
{
// Do not serialize collection
serialize = false;
}
}
// Serialize collection
if(serialize)
{
propNamesList.Add(pi.Name);
SerializeCollection(pi.GetValue(objectToSerialize, null), pi.Name, writer);
}
}
// Serialize public properties with Get and Set methods
else if(pi.CanRead && pi.CanWrite)
{
// Skip indexes
if(pi.Name == "Item")
{
continue;
}
// Check if this property is serializable content
if (IsSerializableContent(pi.Name, objectToSerialize))
{
// Check if an object should be serialized as a property or as a class
if (ShouldSerializeAsAttribute(pi, objectToSerialize))
{
// Serialize property
SerializeProperty(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, writer);
}
else
{
// Serialize inner object
SerializeObject(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, writer);
}
}
propNamesList.Add(pi.Name);
}
}
// Check that all properties have unique IDs
CheckPropertiesID(propNamesList);
}
// If position of the writer did not change - nothing was written
if(writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
{
// Remove object ID from the stream
writer.Seek(-2, SeekOrigin.Current);
writer.Write((short)0);
writer.Seek(-2, SeekOrigin.Current);
}
else
{
// Write the end objectTag
writer.Write((short)0);
}
return;
}
/// <summary>
/// Serializes the data point.
/// </summary>
/// <param name="objectToSerialize">The object to serialize.</param>
/// <param name="elementName">Name of the element.</param>
/// <param name="writer">The writer.</param>
private void SerializeDataPoint(object objectToSerialize, string elementName, BinaryWriter writer)
{
// Write object ID (hash of the name) into the writer
writer.Write(SerializerBase.GetStringHashCode(elementName));
// Remember position where object data is started
long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
DataPoint dataPoint = objectToSerialize as DataPoint;
if (dataPoint.XValue != 0d && IsSerializableContent("XValue", objectToSerialize))
{
SerializeProperty(dataPoint.XValue, dataPoint, "XValue", writer);
}
if (dataPoint.YValues.Length > 0 && IsSerializableContent("YValues", objectToSerialize))
{
SerializeProperty(dataPoint.YValues, dataPoint, "YValues", writer);
}
if (dataPoint.IsEmpty && IsSerializableContent("IsEmpty", objectToSerialize))
{
SerializeProperty(dataPoint.IsEmpty, dataPoint, "IsEmpty", writer);
}
bool hasCustomProperties = false;
foreach (DictionaryEntry entry in dataPoint.properties)
{
if (entry.Key is int)
{
CommonCustomProperties propertyType = (CommonCustomProperties)((int)entry.Key);
String properyName = propertyType.ToString();
if (IsSerializableContent(properyName, objectToSerialize))
{
SerializeProperty(entry.Value, dataPoint, properyName, writer);
}
}
else
{
hasCustomProperties = true;
}
}
if (hasCustomProperties && !String.IsNullOrEmpty(dataPoint.CustomProperties) && IsSerializableContent("CustomProperties", objectToSerialize))
{
SerializeProperty(dataPoint.CustomProperties, dataPoint, "CustomProperties", writer);
}
// If position of the writer did not change - nothing was written
if (writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
{
// Remove object ID from the stream
writer.Seek(-2, SeekOrigin.Current);
writer.Write((short)0);
writer.Seek(-2, SeekOrigin.Current);
}
else
{
// Write the end objectTag
writer.Write((short)0);
}
}
/// <summary>
/// Serialize specified object into the binary writer.
/// Method is called recursively to serialize child objects.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="writer">Binary writer.</param>
virtual internal void SerializeCollection(object objectToSerialize, string elementName, BinaryWriter writer)
{
ICollection collection = objectToSerialize as ICollection;
if(collection != null)
{
// Write object ID (hash of the name) into the writer
writer.Write(SerializerBase.GetStringHashCode(elementName));
// Remember position where object data is started
long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
// Enumerate through all objects in collection and serialize them
foreach (object obj in collection)
{
if (obj is DataPoint)
{
SerializeDataPoint(obj, GetObjectName(obj), writer);
continue;
}
SerializeObject(obj, objectToSerialize, GetObjectName(obj), writer);
}
// If position of the writer did not change - nothing was written
if(writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
{
// Remove object ID from the stream
writer.Seek(-2, SeekOrigin.Current);
writer.Write((short)0);
writer.Seek(-2, SeekOrigin.Current);
}
else
{
// Write the end objectTag
writer.Write((short)0);
}
}
}
/// <summary>
/// Serialize specified object into the binary writer.
/// Method is called recursively to serialize child objects.
/// </summary>
/// <param name="objectToSerialize">Object to be serialized.</param>
/// <param name="parent">Parent of the serialized object.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="writer">Binary writer.</param>
virtual internal void SerializeProperty(object objectToSerialize, object parent, string elementName, BinaryWriter writer)
{
// Check input parameters
if(objectToSerialize == null || parent == null)
{
return;
}
// Check if property has non-default value
PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
if(pd != null)
{
DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
if(defValueAttribute != null)
{
if(objectToSerialize.Equals(defValueAttribute.Value))
{
// Do not serialize properties with default values
return;
}
}
else
{
// Check if property has "ShouldSerialize" method
MethodInfo mi = parent.GetType().GetMethod("ShouldSerialize" + elementName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
if(mi != null)
{
object result = mi.Invoke(parent, null);
if(result is bool && ((bool)result) == false)
{
// Do not serialize properties with default values
return;
}
}
}
// Check XmlFormatSerializerStyle attribute
SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
if(styleAttribute != null)
{
// Hidden property
if(styleAttribute.Visibility == SerializationVisibility.Hidden)
{
return;
}
}
}
// Write property
WritePropertyValue(objectToSerialize, elementName, writer);
}
/// <summary>
/// Converts object value into the string.
/// </summary>
/// <param name="obj">Object to convert.</param>
/// <param name="elementName">Object name.</param>
/// <param name="writer">Binary writer.</param>
/// <returns>Object value as strig.</returns>
[SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily",
Justification = "Too large of a code change to justify making this change")]
internal void WritePropertyValue(object obj, string elementName, BinaryWriter writer)
{
// Write property ID (hash of the name) into the writer
writer.Write(SerializerBase.GetStringHashCode(elementName));
if(obj is bool)
{
writer.Write(((bool)obj));
}
else if(obj is double)
{
writer.Write(((double)obj));
}
else if(obj is string)
{
writer.Write(((string)obj));
}
else if(obj is int)
{
writer.Write(((int)obj));
}
else if(obj is long)
{
writer.Write(((long)obj));
}
else if(obj is float)
{
writer.Write(((float)obj));
}
else if(obj.GetType().IsEnum)
{
// NOTE: Using 'ToString' method instead of the 'Enum.GetName' fixes
// an issue (#4314 & #4424) with flagged enumerations when there are
// more then 1 values set.
string enumValue = obj.ToString();
writer.Write(enumValue);
}
else if(obj is byte)
{
// Write as long
writer.Write((byte)obj);
}
#if !Microsoft_CONTROL
else if(obj is Unit)
{
writer.Write(((Unit)obj).Value);
}
#endif
else if(obj is Font)
{
// Write as string
writer.Write(SerializerBase.FontToString((Font)obj));
}
else if(obj is Color)
{
// Write as int
writer.Write(((Color)obj).ToArgb());
}
else if(obj is DateTime)
{
// Write as long
writer.Write(((DateTime)obj).Ticks);
}
else if(obj is Size)
{
// Write as two integers
writer.Write(((Size)obj).Width);
writer.Write(((Size)obj).Height);
}
else if(obj is double[])
{
double[] arr = (double[])obj;
// Write the size of the array (int)
writer.Write(arr.Length);
// Write each element of the array
foreach(double d in arr)
{
writer.Write(d);
}
}
else if(obj is Color[])
{
Color[] arr = (Color[])obj;
// Write the size of the array (int)
writer.Write(arr.Length);
// Write each element of the array
foreach(Color color in arr)
{
writer.Write(color.ToArgb());
}
}
else if(obj is System.Drawing.Image)
{
// Save image into the memory stream
MemoryStream imageStream = new MemoryStream();
((System.Drawing.Image)obj).Save(imageStream, ((System.Drawing.Image)obj).RawFormat);
// Write the size of the data
int imageSize = (int)imageStream.Seek(0, SeekOrigin.End);
imageStream.Seek(0, SeekOrigin.Begin);
writer.Write(imageSize);
// Write the data
writer.Write(imageStream.ToArray());
imageStream.Close();
}
else if(obj is Margins)
{
// Write as 4 integers
writer.Write(((Margins)obj).Top);
writer.Write(((Margins)obj).Bottom);
writer.Write(((Margins)obj).Left);
writer.Write(((Margins)obj).Right);
}
else
{
throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryTypeUnsupported(obj.GetType().ToString())));
}
}
/// <summary>
/// Checks if all properties will have a unique ID.
/// Property ID is a hash of it's name.
/// !!!USED IN DEBUG BUILD ONLY!!!
/// </summary>
/// <param name="propNames">Array of properties names.</param>
internal void CheckPropertiesID(ArrayList propNames)
{
#if DEBUG
if(propNames != null)
{
// Loop through all properties and check the hash values
foreach(string name1 in propNames)
{
foreach(string name2 in propNames)
{
if(name1 != name2)
{
if( SerializerBase.GetStringHashCode(name1) == SerializerBase.GetStringHashCode(name2) )
{
throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryHashCodeDuplicate(name1,name2)));
}
}
}
}
}
#endif
}
#endregion
#region Deserialization methods
/// <summary>
/// Deserialize specified object from the source using binary format.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="source">Deserialization source.</param>
internal override void Deserialize(object objectToDeserialize, object source)
{
// Check input parameters
if (objectToDeserialize == null)
{
throw (new ArgumentNullException("objectToDeserialize"));
}
if (source == null)
{
throw (new ArgumentNullException("source"));
}
string sourceStr = source as string;
if (sourceStr != null)
{
Deserialize(objectToDeserialize, sourceStr);
return;
}
Stream stream = source as Stream;
if (stream != null)
{
Deserialize(objectToDeserialize, stream);
return;
}
BinaryWriter binaryWriter = source as BinaryWriter;
if (binaryWriter != null)
{
Deserialize(objectToDeserialize, binaryWriter);
return;
}
throw (new ArgumentException(SR.ExceptionChartSerializerSourceObjectInvalid, "source"));
}
/// <summary>
/// Deserialize object from the file using binary format.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="fileName">File name to read the data from.</param>
public void Deserialize(object objectToDeserialize, string fileName)
{
FileStream stream = new FileStream(fileName, FileMode.Open);
Deserialize(objectToDeserialize, new BinaryReader(stream));
stream.Close();
}
/// <summary>
/// Deserialize object from the stream using binary format.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="stream">Stream to read the data from.</param>
public void Deserialize(object objectToDeserialize, Stream stream)
{
Deserialize(objectToDeserialize, new BinaryReader(stream));
}
/// <summary>
/// Deserialize object from different types of readers using binary format.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="reader">Binary reader.</param>
public void Deserialize(object objectToDeserialize, BinaryReader reader)
{
// Check input parameters
if(objectToDeserialize == null)
{
throw(new ArgumentNullException("objectToDeserialize"));
}
if(reader == null)
{
throw(new ArgumentNullException("reader"));
}
// Binary deserializer do not support IsUnknownAttributeIgnored property
if(base.IsUnknownAttributeIgnored)
{
throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryIgnoreUnknownAttributesUnsupported));
}
// Read 15 characters of file header
char[] header = reader.ReadChars(15);
if(header[0] != 'D' || header[1] != 'C' || header[2] != 'B' || header[3] != 'F')
{
throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryFromatInvalid));
}
// Get ID of the root object
this.ReadHashID(reader);
// Reset properties of the root object
if(IsResetWhenLoading)
{
ResetObjectProperties(objectToDeserialize);
}
// Deserialize object
DeserializeObject(objectToDeserialize, null, GetObjectName(objectToDeserialize), reader, false);
}
/// <summary>
/// Deserialize object from the binary format.
/// Method is called recursively to deserialize child objects.
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="parent">Parent of the deserialized object.</param>
/// <param name="elementName">Object element name.</param>
/// <param name="reader">Binary reader object.</param>
/// <param name="skipElement">if set to <c>true</c> the element will not be set.</param>
/// <returns>Number of properties set.</returns>
virtual protected int DeserializeObject(object objectToDeserialize, object parent, string elementName, BinaryReader reader, bool skipElement)
{
int setPropertiesNumber = 0;
// Check input parameters
if(objectToDeserialize == null)
{
return setPropertiesNumber;
}
// Special handling for the collections
Type[] assemblyTypes = null;
int listItemIndex = 0;
IList list = objectToDeserialize as IList;
if(list != null)
{
// Loop through all list items
Int16 typeHash = 0;
PropertyInfo listItemPI = objectToDeserialize.GetType().GetProperty("Item", new Type[] { typeof(int) });
while ((typeHash = this.ReadHashID(reader)) != 0)
{
// Get collection item type from hashed type name
string typeName = String.Empty;
if(listItemPI != null)
{
if ((SerializerBase.GetStringHashCode(listItemPI.PropertyType.Name)) == typeHash)
{
typeName = listItemPI.PropertyType.Name;
}
else
{
Assembly assembly = listItemPI.PropertyType.Assembly;
if (assembly != null)
{
// Find all classes derived from this type
if (assemblyTypes == null)
{
assemblyTypes = assembly.GetExportedTypes();
}
foreach (Type type in assemblyTypes)
{
if (type.IsSubclassOf(listItemPI.PropertyType))
{
if ((SerializerBase.GetStringHashCode(type.Name)) == typeHash)
{
typeName = type.Name;
break;
}
}
}
}
}
}
// Create new item object
string itemName = null;
bool reusedObject = false;
object listItem = GetListNewItem(list, typeName, ref itemName, ref reusedObject);
// Deserialize list item object
int itemSetProperties = DeserializeObject(listItem, objectToDeserialize, "", reader, skipElement);
// Add item object into the list
if (!skipElement && (itemSetProperties > 0 || reusedObject))
{
list.Insert(listItemIndex++, listItem);
}
// TD: here was removed a code which doesn't work but cause heavy workload: GetListNewItem removes the reusedObject from the list.
// Add properties set for collection item
setPropertiesNumber += itemSetProperties;
}
return setPropertiesNumber;
}
// Get list of object's properties
PropertyInfo[] properties = objectToDeserialize.GetType().GetProperties();
if(properties == null)
{
return setPropertiesNumber;
}
// Get property information by reading the ID
PropertyInfo pi = null;
while ( (pi = ReadPropertyInfo(objectToDeserialize, parent, properties, reader)) != null)
{
// Read simple properties
if(ShouldSerializeAsAttribute(pi, objectToDeserialize))
{
if(SetPropertyValue(objectToDeserialize, pi, reader, skipElement))
{
++setPropertiesNumber;
}
}
else
{
// Get property descriptor
PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToDeserialize)[pi.Name];
if(pd != null)
{
object innerObject = pd.GetValue(objectToDeserialize);
// Deserialize inner item object
setPropertiesNumber += DeserializeObject(innerObject, objectToDeserialize, pi.Name, reader, !IsSerializableContent(pi.Name, objectToDeserialize));
}
else if(!IsUnknownAttributeIgnored)
{
throw(new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown( pi.Name,objectToDeserialize.GetType().ToString())));
}
}
}
return setPropertiesNumber;
}
/// <summary>
/// Reads and sets a property of an object.
/// </summary>
/// <param name="obj">Object to set.</param>
/// <param name="pi">Property information.</param>
/// <param name="reader">Binary reader.</param>
/// <param name="skipElement">if set to <c>true</c> the property will not be set.</param>
/// <returns>True if property was set.</returns>
private bool SetPropertyValue(object obj, PropertyInfo pi, BinaryReader reader, bool skipElement)
{
if(pi != null)
{
object objValue = null;
if(pi.PropertyType == typeof(bool))
{
objValue = reader.ReadBoolean();
}
else if(pi.PropertyType == typeof(double))
{
objValue = reader.ReadDouble();
}
else if(pi.PropertyType == typeof(string))
{
objValue = reader.ReadString();
}
else if(pi.PropertyType == typeof(int))
{
objValue = reader.ReadInt32();
}
else if(pi.PropertyType == typeof(long))
{
objValue = reader.ReadInt64();
}
else if(pi.PropertyType == typeof(float))
{
objValue = reader.ReadSingle();
}
else if(pi.PropertyType.IsEnum)
{
// Read as string
objValue = Enum.Parse(pi.PropertyType, reader.ReadString());
}
else if(pi.PropertyType == typeof(byte))
{
objValue = reader.ReadByte();
}
#if !Microsoft_CONTROL
else if(pi.PropertyType == typeof(Unit))
{
objValue = new Unit((double)reader.ReadDouble());
}
#endif
else if(pi.PropertyType == typeof(Font))
{
// Read as string
objValue = SerializerBase.FontFromString(reader.ReadString());
}
else if(pi.PropertyType == typeof(Color))
{
// Read as int
objValue = Color.FromArgb(reader.ReadInt32());
}
else if(pi.PropertyType == typeof(DateTime))
{
// Read as long
objValue = new DateTime(reader.ReadInt64());
}
else if(pi.PropertyType == typeof(Size))
{
// Read as two integers
objValue = new Size(reader.ReadInt32(), reader.ReadInt32());
}
else if(pi.PropertyType == typeof(Margins) )
{
// Read as 4 integers
objValue = new Margins(
reader.ReadInt32(),
reader.ReadInt32(),
reader.ReadInt32(),
reader.ReadInt32());
}
else if(pi.PropertyType == typeof(double[]))
{
// Allocate array
double[] array = new double[reader.ReadInt32()];
// Read each element of the array
for(int arrayIndex = 0; arrayIndex < array.Length; arrayIndex++)
{
array[arrayIndex] = reader.ReadDouble();
}
objValue = array;
}
else if(pi.PropertyType == typeof(Color[]))
{
// Allocate array
Color[] array = new Color[reader.ReadInt32()];
// Read each element of the array
for(int arrayIndex = 0; arrayIndex < array.Length; arrayIndex++)
{
array[arrayIndex] = Color.FromArgb(reader.ReadInt32());
}
objValue = array;
}
else if(pi.PropertyType == typeof(System.Drawing.Image))
{
// Get image data size
int imageSize = reader.ReadInt32();
// Create image stream
MemoryStream imageStream = new MemoryStream(imageSize + 10);
// Copy image data into separate stream
imageStream.Write(reader.ReadBytes(imageSize), 0, imageSize);
// Create image object
objValue = new Bitmap(System.Drawing.Image.FromStream(imageStream)); // !!! .Net bug when image source stream is closed - can create brush using the image
// Close image stream
imageStream.Close();
}
else
{
throw(new InvalidOperationException(SR.ExceptionChartSerializerBinaryTypeUnsupported( obj.GetType().ToString() )));
}
// Check if this property is serializable content
if (!skipElement && IsSerializableContent(pi.Name, obj))
{
// Set object value
pi.SetValue(obj, objValue, null);
return true;
}
}
return false;
}
/// <summary>
/// Reads property ID and return property information
/// </summary>
/// <param name="objectToDeserialize">Object to be deserialized.</param>
/// <param name="parent">Parent of the deserialized object.</param>
/// <param name="properties">List of properties information.</param>
/// <param name="reader">Binary reader.</param>
/// <returns>Property information.</returns>
private PropertyInfo ReadPropertyInfo(object objectToDeserialize, object parent, PropertyInfo[] properties, BinaryReader reader)
{
// Read property ID
short propertyID = this.ReadHashID(reader);
// End objectTag reached
if(propertyID == 0)
{
return null;
}
// Loop through all properties and check properties IDs (hash code of name)
foreach(PropertyInfo pi in properties)
{
// Skip inherited properties from the root object
if(IsChartBaseProperty(objectToDeserialize, parent, pi))
{
continue;
}
// Check collection
if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null)
{
if((SerializerBase.GetStringHashCode(pi.Name)) == propertyID)
{
return pi;
}
}
// Check public properties with Get and Set methods
else if(pi.CanRead && pi.CanWrite)
{
// Skip indexes
if(pi.Name == "Item")
{
continue;
}
if((SerializerBase.GetStringHashCode(pi.Name)) == propertyID)
{
return pi;
}
}
}
// Property was not found
throw (new InvalidOperationException(SR.ExceptionChartSerializerPropertyNotFound));
}
#endregion
}
}
|