You've already forked linux-packaging-mono
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
3275 lines
107 KiB
C#
3275 lines
107 KiB
C#
//-------------------------------------------------------------
|
||
// <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
|
||
}
|
||
}
|