Files
Pat Tullmann 0cb742dafb binfmt-detector-cli: rewrite to support PE32+ binaries (#38)
Rewrite with hard-coded offsets into the PE file format to discern
if a binary is PE32 or PE32+, and then to determine if it contains
a "CLR Data Directory" entry that looks valid.

Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native
binaries, and a random assortment of garbage files.

Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
2023-10-16 20:16:47 +02:00

3275 lines
107 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//-------------------------------------------------------------
// <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>"));
// 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
}
}