1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 // File: XmlSerializer.cs
10 // Namespace: System.Web.UI.WebControls[Windows.Forms].Charting.Utilities
12 // Classes: XmlFormatSerializer, BinaryFormatSerializer
13 // SerializerBase, SerializationVisibilityAttribute
17 // Chart serializer allows persisting of all chart data and
18 // settings into the stream or file using XML or binary format.
19 // This data can be later loaded back into the chart completely
20 // restoring its state. Serialize can also be used to reset chart
21 // control state to its default values.
23 // Both XML and Binary serialization methods use reflection to
24 // discover class properties which need to be serialized. Only
25 // properties with non-default values are persisted. Full Trust
26 // is required to use chartserialization.
28 // SerializeBase class implements all the chart serializer
29 // properties and methods to reset chart content. XmlFormatSerializer
30 // and BinaryFormatSerializer classes derive from the SerializeBase
31 // class and provide saving and loading functionality for XML and
34 // By default, all chart content is Saved, Loaded or Reset, but
35 // this can be changed using serializer Content, SerializableContent
36 // and NonSerializableContent properties. Content property allows a
37 // simple way to serialize everything, appearance or just chart data.
39 // SerializableContent and NonSerializableContent properties provide
40 // more control over what is beign persisted and they override the
41 // Content property settings. Each of the properties is a string
42 // which is a comma-separated listing of all chart properties to be
43 // serialized. The syntax of this property is "Class.Property[,Class.Property]",
44 // and wildcards may be used (represented by an asterisk). For example,
45 // to serialize all chart BackColor properties set this property to
48 // Reviewed: AG - August 7, 2002
49 // AG - Microsoft 6, 2007
51 //===================================================================
54 #region Used Namespaces
58 using System.Reflection;
59 using System.Collections;
61 using System.Drawing.Drawing2D;
62 using System.Drawing.Imaging;
63 using System.ComponentModel;
66 using System.Globalization;
67 using System.Diagnostics.CodeAnalysis;
68 using System.Collections.Specialized;
69 using System.Security;
72 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
74 using System.Web.UI.WebControls;
75 using System.Web.UI.DataVisualization.Charting.ChartTypes;
81 namespace System.Windows.Forms.DataVisualization.Charting.Utilities
83 namespace System.Web.UI.DataVisualization.Charting.Utilities
86 #region Serialization enumerations
89 /// Enumeration which describes how to persist property during the serialization
91 internal enum SerializationVisibility
99 /// Serialize as XML attribute
104 /// Serialize as XML element
110 /// Determines chart current serialization status.
112 internal enum SerializationStatus
115 /// Chart is not serializing
130 /// Chart is resetting
138 /// Attribute which describes how to persist property during the serialization.
140 [AttributeUsage(AttributeTargets.All)]
141 internal sealed class SerializationVisibilityAttribute : System.Attribute
146 private SerializationVisibility _visibility = SerializationVisibility.Attribute;
153 /// Public constructor
155 /// <param name="visibility">Serialization visibility.</param>
156 internal SerializationVisibilityAttribute(SerializationVisibility visibility)
158 this._visibility = visibility;
166 /// Serialization visibility property
168 public SerializationVisibility Visibility
176 // _visibility = value;
184 /// Base class of the serializers. Common properties and methods for all serializers.
186 internal abstract class SerializerBase
191 /// Indicates that unknown properties and elements are ignored
193 private bool _isUnknownAttributeIgnored = false;
196 /// Indicates that serializer works in template creation mode
198 private bool _isTemplateMode = false;
201 /// Indicates that object properties are reset before loading
203 private bool _isResetWhenLoading = true;
206 /// Comma separated list of serializable (Save/Load/Reset) properties. "ClassName.PropertyName"
208 private string _serializableContent = "";
211 /// Comma separated list of NON serializable (Save/Load/Reset) properties. "ClassName.PropertyName"
213 private string _nonSerializableContent = "";
216 /// Font converters used while serializing/deserializing
218 internal static FontConverter fontConverter = new FontConverter();
221 /// Color converters used while serializing/deserializing
223 internal static ColorConverter colorConverter = new ColorConverter();
226 /// Hash code provider.
228 protected static StringComparer hashCodeProvider = StringComparer.OrdinalIgnoreCase;
231 /// Contains chart specific converters
233 HybridDictionary _converterDict = new HybridDictionary();
238 #region Public properties
241 /// Indicates that unknown properties and elements will be
242 /// ignored without throwing an exception.
244 internal bool IsUnknownAttributeIgnored
248 return _isUnknownAttributeIgnored;
252 _isUnknownAttributeIgnored = value;
257 /// Indicates that serializer works in template creation mode
259 internal bool IsTemplateMode
263 return _isTemplateMode;
267 _isTemplateMode = value;
272 /// Indicates that object properties are reset to default
273 /// values before loading.
275 internal bool IsResetWhenLoading
279 return _isResetWhenLoading;
283 _isResetWhenLoading = value;
288 /// Comma separated list of serializable (Save/Load/Reset) properties.
289 /// "ClassName.PropertyName,[ClassName.PropertyName]".
291 internal string SerializableContent
295 return _serializableContent;
299 _serializableContent = value;
302 serializableContentList = null;
307 /// Comma separated list of serializable (Save/Load/Reset) properties.
308 /// "ClassName.PropertyName,[ClassName.PropertyName]".
310 internal string NonSerializableContent
314 return _nonSerializableContent;
318 _nonSerializableContent = value;
321 nonSerializableContentList = null;
327 #region Resetting methods
330 /// Reset properties of the object to default values.
332 /// <param name="objectToReset">Object to be reset.</param>
333 virtual internal void ResetObjectProperties(object objectToReset)
335 // Reset object properties
336 ResetObjectProperties(objectToReset, null, GetObjectName(objectToReset));
340 /// Reset properties of the object to default values.
341 /// Method is called recursively to reset child objects properties.
343 /// <param name="objectToReset">Object to be reset.</param>
344 /// <param name="parent">Parent of the reset object.</param>
345 /// <param name="elementName">Object element name.</param>
346 virtual internal void ResetObjectProperties(object objectToReset, object parent, string elementName)
348 // Check input parameters
349 if(objectToReset == null)
354 IList list = objectToReset as IList;
356 // Check if object is a list
357 if(list != null && IsSerializableContent(elementName, parent))
359 // Reset list by clearing all the items
364 // Retrive properties list of the object
365 PropertyInfo[] properties = objectToReset.GetType().GetProperties();
366 if(properties != null)
368 // Loop through all properties and reset public properties
369 foreach(PropertyInfo pi in properties)
371 // Get property descriptor
372 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToReset)[pi.Name];
374 // Check XmlFormatSerializerStyle attribute
377 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
378 if(styleAttribute != null)
381 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
388 // Check if this property should be reset
389 bool resetProperty = IsSerializableContent(pi.Name, objectToReset);
391 // Skip inherited properties from the root object
392 if(IsChartBaseProperty(objectToReset, parent, pi))
398 if(pi.CanRead && pi.PropertyType.GetInterface("IList", true) != null)
402 // Check if collection has "Reset" method
403 bool resetComplete = false;
404 MethodInfo mi = objectToReset.GetType().GetMethod("Reset" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
407 mi.Invoke(objectToReset, null);
408 resetComplete = true;
411 // Reset list by clearing all the items
414 ((IList)pi.GetValue(objectToReset, null)).Clear();
419 // Reset objects of the list
420 foreach(object listObject in ((IList)pi.GetValue(objectToReset, null)))
422 ResetObjectProperties(listObject, objectToReset, this.GetObjectName(listObject));
427 // Reset public properties with Get and Set methods
428 else if(pi.CanRead && pi.CanWrite)
431 if(pi.Name == "Item")
437 if (pi.Name == "Name")
442 // Reset inner properies
443 if(ShouldSerializeAsAttribute(pi, objectToReset))
447 // Reset the property using property descriptor
451 // Get property object
452 object objectProperty = pi.GetValue(objectToReset, null);
454 // Get default value of the property
455 DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
456 if(defValueAttribute != null)
458 if(objectProperty == null)
460 if(defValueAttribute.Value != null)
462 pd.SetValue(objectToReset, defValueAttribute.Value);
465 else if(! objectProperty.Equals(defValueAttribute.Value))
467 pd.SetValue(objectToReset, defValueAttribute.Value);
472 // Check if property has "Reset" method
473 MethodInfo mi = objectToReset.GetType().GetMethod("Reset" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
476 mi.Invoke(objectToReset, null);
484 // Reset inner object
485 ResetObjectProperties(pi.GetValue(objectToReset, null), objectToReset, pi.Name);
496 #region Abstract Serialization/Deserialization methods
499 /// Serialize specified object into the destination object.
501 /// <param name="objectToSerialize">Object to be serialized.</param>
502 /// <param name="destination">Destination of the serialization.</param>
503 internal abstract void Serialize(object objectToSerialize, object destination);
506 /// Deserialize specified object from the source object.
508 /// <param name="objectToDeserialize">Object to be deserialized.</param>
509 /// <param name="source">Source of the deserialization.</param>
510 internal abstract void Deserialize(object objectToDeserialize, object source);
514 #region Protected helper methods
517 /// Converts specified font object into a string.
519 /// <param name="font">Font object to convert.</param>
520 /// <returns>String that contains font data.</returns>
521 internal static string FontToString(Font font)
523 // Save basic properties persisted by font converter
524 string fontData = (string)SerializerBase.fontConverter.ConvertToInvariantString(font);
526 // Persist properties not serialiazed by the converter
527 if(font.GdiCharSet != 1)
529 fontData += ", GdiCharSet=" + font.GdiCharSet.ToString(System.Globalization.CultureInfo.InvariantCulture);
531 if(font.GdiVerticalFont)
533 fontData += ", GdiVerticalFont";
540 /// Converts string data into a font object.
542 /// <param name="fontString">String with font data.</param>
543 /// <returns>Newly created font object.</returns>
544 internal static Font FontFromString(string fontString)
546 // Check if string contains non-standard values "GdiCharSet" or "GdiVerticalFont"
547 string standardData = fontString;
549 bool gdiVerticalFont = false;
550 int charIndex = fontString.IndexOf(", GdiCharSet=", StringComparison.Ordinal);
554 string val = fontString.Substring(charIndex + 13);
555 int commaIndex = val.IndexOf(",", StringComparison.Ordinal);
558 val = val.Substring(0, commaIndex);
561 gdiCharSet = (byte)Int32.Parse(val, System.Globalization.CultureInfo.InvariantCulture);
563 // Truncate standard data string
564 if(standardData.Length > charIndex)
566 standardData = standardData.Substring(0, charIndex);
569 charIndex = fontString.IndexOf(", GdiVerticalFont", StringComparison.Ordinal);
572 gdiVerticalFont = true;
574 // Truncate standard data string
575 if(standardData.Length > charIndex)
577 standardData = standardData.Substring(0, charIndex);
581 // Create Font object from standard parameters
582 Font font = (Font)SerializerBase.fontConverter.ConvertFromInvariantString(standardData);
584 // check if non-standard parameters provided
585 if(gdiVerticalFont || gdiCharSet != 1)
587 Font newFont = new Font(
604 /// Returns a hash code of a specified string.
606 /// <param name="str">String to get the hash code for.</param>
607 /// <returns>String hash code.</returns>
608 internal static short GetStringHashCode(string str)
610 return (short)(hashCodeProvider.GetHashCode(str) + str.Length * 2);
614 /// Reads hash ID from the specified binary reader.
616 /// <param name="reader">Binary reader to get the data from.</param>
617 /// <returns>Property name or collection member type ID.</returns>
618 internal Int16 ReadHashID(BinaryReader reader)
620 // For later versions return ID without transformations
621 return reader.ReadInt16();
625 /// Checks if property belongs to the base class of the chart "Control".
627 /// <param name="objectToSerialize">Serializable object.</param>
628 /// <param name="parent">Object parent.</param>
629 /// <param name="pi">Serializable property information.</param>
630 /// <returns>True if property belongs to the base class.</returns>
631 internal bool IsChartBaseProperty(object objectToSerialize, object parent, PropertyInfo pi)
635 // Check only for the root object
638 Type currentType = objectToSerialize.GetType();
639 while(currentType != null)
641 if(pi.DeclaringType == currentType)
647 // Check if it's a chart class
648 if( currentType == typeof(Chart))
654 // Get base class type
655 currentType = currentType.BaseType;
664 /// Converts Image object into the BASE64 encoded string
666 /// <param name="image">Image to convert.</param>
667 /// <returns>BASE64 encoded image data.</returns>
668 internal static string ImageToString(System.Drawing.Image image)
670 // Save image into the stream using BASE64 encoding
671 MemoryStream imageStream = new MemoryStream();
672 image.Save(imageStream, ImageFormat.Png);
673 imageStream.Seek(0, SeekOrigin.Begin);
675 // Create XmlTextWriter and save image in BASE64
676 StringBuilder stringBuilder = new StringBuilder();
677 XmlTextWriter textWriter = new XmlTextWriter(new StringWriter(stringBuilder, CultureInfo.InvariantCulture));
678 byte[] imageByteData = imageStream.ToArray();
679 textWriter.WriteBase64(imageByteData, 0, imageByteData.Length);
681 // Close image stream
685 return stringBuilder.ToString();
689 /// Converts BASE64 encoded string to image.
691 /// <param name="data">BASE64 encoded data.</param>
692 /// <returns>Image.</returns>
693 internal static System.Drawing.Image ImageFromString(string data)
695 // Create XML text reader
696 byte[] buffer = new byte[1000];
697 MemoryStream imageStream = new MemoryStream();
698 XmlTextReader textReader = new XmlTextReader(new StringReader("<base64>" + data + "</base64>"));
700 // Read tags and BASE64 encoded data
703 while((bytesRead = textReader.ReadBase64(buffer, 0, 1000)) > 0)
705 imageStream.Write(buffer, 0, bytesRead);
709 // Create image from stream
710 imageStream.Seek(0, SeekOrigin.Begin);
711 System.Drawing.Image tempImage = System.Drawing.Image.FromStream(imageStream);
712 System.Drawing.Bitmap image = new Bitmap(tempImage); // !!! .Net bug when image source stream is closed - can create brush using the image
713 image.SetResolution(tempImage.HorizontalResolution, tempImage.VerticalResolution); //The bitmap created using the constructor does not copy the resolution of the image
715 // Close image stream
723 /// Get the name of the object class
725 /// <param name="obj">Object to get the name of.</param>
726 /// <returns>Name of the object class (without namespace).</returns>
727 internal string GetObjectName(object obj)
729 string name = obj.GetType().ToString();
730 return name.Substring(name.LastIndexOf('.') + 1);
734 /// Create new empty item for the list.
735 /// AxisName of the objects is determined by the return type of the indexer.
737 /// <param name="list">List used to detect type of the item objects.</param>
738 /// <param name="itemTypeName">Name of collection type.</param>
739 /// <param name="itemName">Optional item name to return.</param>
740 /// <param name="reusedObject">Indicates that object with specified name was already in the collection and it being reused.</param>
741 /// <returns>New list item object.</returns>
742 internal object GetListNewItem(IList list, string itemTypeName, ref string itemName, ref bool reusedObject)
744 // Get type of item in collection
745 Type itemType = null;
746 if(itemTypeName.Length > 0)
748 itemType = Type.GetType(typeof(Chart).Namespace + "." + itemTypeName, false, true);
751 reusedObject = false;
752 PropertyInfo pi = list.GetType().GetProperty("Item", itemType, new Type[] {typeof(string)} );
753 MethodInfo mi = list.GetType().GetMethod("IndexOf", new Type[] { typeof(String) });
754 ConstructorInfo ci = null;
757 // Try to get object by name using the indexer
758 if(itemName != null && itemName.Length > 0)
760 bool itemChecked = false;
766 object oindex = mi.Invoke(list, new object[] { itemName });
774 object objByName = list[index];
775 if (objByName != null)
777 // Remove found object from the list
778 list.Remove(objByName);
780 // Return found object
786 catch (ArgumentException)
789 catch (TargetException)
792 catch (TargetInvocationException)
798 object objByName = null;
801 objByName = pi.GetValue(list, new object[] { itemName });
803 catch (ArgumentException)
807 catch (TargetException)
811 catch (TargetInvocationException)
816 if (objByName != null)
820 // Remove found object from the list
821 list.Remove(objByName);
823 catch (NotSupportedException)
827 // Return found object
836 // Get the constructor of the type returned by indexer
837 if (itemType != null)
839 ci = itemType.GetConstructor(Type.EmptyTypes);
843 ci = pi.PropertyType.GetConstructor(Type.EmptyTypes);
847 throw (new InvalidOperationException(SR.ExceptionChartSerializerDefaultConstructorUndefined(pi.PropertyType.ToString())));
849 return ci.Invoke(null);
853 /// Returns true if the object property should be serialized as
854 /// parent element attribute. Otherwise as a child element.
856 /// <param name="pi">Property information.</param>
857 /// <param name="parent">Object that the property belongs to.</param>
858 /// <returns>True if property should be serialized as attribute.</returns>
859 internal bool ShouldSerializeAsAttribute(PropertyInfo pi, object parent)
861 // Check if SerializationVisibilityAttribute is set
864 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[pi.Name];
867 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
868 if(styleAttribute != null)
870 if(styleAttribute.Visibility == SerializationVisibility.Attribute)
874 else if(styleAttribute.Visibility == SerializationVisibility.Element)
882 // If a simple type - serialize as property
883 if(!pi.PropertyType.IsClass)
888 // Some classes are serialized as properties
889 if(pi.PropertyType == typeof(string) ||
890 pi.PropertyType == typeof(Font) ||
891 pi.PropertyType == typeof(Color) ||
892 pi.PropertyType == typeof(System.Drawing.Image))
902 /// Determines if this property should be serialized as attribute
904 /// <param name="pi">Property information.</param>
905 /// <param name="objectToSerialize">Object that the property belongs to.</param>
906 /// <returns>True if should be serialized as attribute</returns>
907 internal bool SerializeICollAsAtribute(PropertyInfo pi, object objectToSerialize)
909 if (objectToSerialize != null)
911 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
914 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
915 if (styleAttribute != null)
917 if (styleAttribute.Visibility == SerializationVisibility.Attribute)
928 /// Returns true if the object property is serializable.
930 /// <param name="propertyName">Property name.</param>
931 /// <param name="parent">Object that the property belongs to.</param>
932 /// <returns>True if property is serializable.</returns>
933 internal bool IsSerializableContent(string propertyName, object parent)
935 bool serializable = true;
936 if(_serializableContent.Length > 0 || _nonSerializableContent.Length > 0)
938 int serialzableClassFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
939 int serialzablePropertyFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
940 string ownerClassName = GetObjectName(parent);
942 // Check if property in this class is part of the serializable content
943 serializable = IsPropertyInList(GetSerializableContentList(), ownerClassName, propertyName, out serialzableClassFitType, out serialzablePropertyFitType);
945 // Check if property in this class is part of the NON serializable content
948 int nonSerialzableClassFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
949 int nonSerialzablePropertyFitType = 0; // 0 - undefined; 1 - '*'; 2 - 'Back*'; 3 - Exact
950 bool nonSerializable = IsPropertyInList(GetNonSerializableContentList(), ownerClassName, propertyName, out nonSerialzableClassFitType, out nonSerialzablePropertyFitType);
952 // If property was found in non serializable content list - check the type priority
953 // Priority order: Exact match, 'Back*' mask match, '*' all mask match
957 if((nonSerialzableClassFitType + nonSerialzablePropertyFitType) >
958 (serialzableClassFitType + serialzablePropertyFitType))
960 serializable = false;
970 /// Checks if property belongs is defined in the mask list.
972 /// <param name="contentList">Array list of class/property items.</param>
973 /// <param name="className">Class name.</param>
974 /// <param name="propertyName">Property name.</param>
975 /// <param name="classFitType">Return class mask fit type.</param>
976 /// <param name="propertyFitType">Return property mask fit type.</param>
977 /// <returns>True if property was found in the list.</returns>
978 private bool IsPropertyInList(ArrayList contentList, string className, string propertyName, out int classFitType, out int propertyFitType)
980 // Initialize result values
984 if(contentList != null)
986 // Loop through all items in the list using step 2
987 for(int itemIndex = 0; itemIndex < contentList.Count; itemIndex += 2)
989 // Initialize result values
993 // Check if object class and property name match the mask
994 if(NameMatchMask((ItemInfo)contentList[itemIndex], className, out classFitType))
996 if(NameMatchMask((ItemInfo)contentList[itemIndex + 1], propertyName, out propertyFitType))
1008 /// Compares class/property name with the specified mask
1010 /// <param name="itemInfo">Class/Property item information.</param>
1011 /// <param name="objectName">Class/Property name.</param>
1012 /// <param name="type">AxisName of matching. 0-No Match; 1-'*' any wild card; 2-'Back*' contain wild card; 3-exact match</param>
1013 /// <returns>True if name match the mask.</returns>
1014 private bool NameMatchMask(ItemInfo itemInfo, string objectName, out int type)
1026 // Ends with class mask
1027 if(itemInfo.endsWith)
1029 if(itemInfo.name.Length <= objectName.Length)
1031 if(objectName.Substring(0, itemInfo.name.Length) == itemInfo.name)
1039 // Starts with class mask
1040 if(itemInfo.startsWith)
1042 if(itemInfo.name.Length <= objectName.Length)
1044 if(objectName.Substring(objectName.Length - itemInfo.name.Length, itemInfo.name.Length) == itemInfo.name)
1052 // Exact name is specified
1053 if(itemInfo.name == objectName)
1064 /// Finds a converter by property descriptor.
1066 /// <param name="pd">Property descriptor.</param>
1067 /// <returns>A converter registered in TypeConverterAttribute or by property type</returns>
1068 internal TypeConverter FindConverter(PropertyDescriptor pd)
1070 TypeConverter result;
1071 TypeConverterAttribute typeConverterAttrib = (TypeConverterAttribute)pd.Attributes[typeof(TypeConverterAttribute)];
1072 if (typeConverterAttrib != null && typeConverterAttrib.ConverterTypeName.Length > 0)
1074 result = this.FindConverterByType(typeConverterAttrib);
1081 return pd.Converter;
1083 catch (SecurityException)
1086 catch (MethodAccessException)
1090 return TypeDescriptor.GetConverter(pd.PropertyType);
1094 /// Finds a converter by TypeConverterAttribute.
1096 /// <param name="attr">TypeConverterAttribute.</param>
1097 /// <returns>TypeConvetrer or null</returns>
1098 internal TypeConverter FindConverterByType( TypeConverterAttribute attr)
1100 // In default Inranet zone (partial trust) ConsrtuctorInfo.Invoke (PropertyDescriptor.Converter)
1101 // throws SecurityException or MethodAccessException when the converter class is internal.
1102 // Thats why we have this giant if - elseif here - to create type converters whitout reflection.
1103 if (_converterDict.Contains(attr.ConverterTypeName))
1105 return (TypeConverter)_converterDict[attr.ConverterTypeName];
1107 String typeStr = attr.ConverterTypeName;
1109 if (attr.ConverterTypeName.Contains(",") )
1111 typeStr = attr.ConverterTypeName.Split(',')[0];
1114 TypeConverter result = null;
1116 if (typeStr.EndsWith(".CustomPropertiesTypeConverter", StringComparison.OrdinalIgnoreCase)) { result = new CustomPropertiesTypeConverter(); }
1117 else if (typeStr.EndsWith(".DoubleNanValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new DoubleNanValueConverter(); }
1118 else if (typeStr.EndsWith(".DoubleDateNanValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new DoubleDateNanValueConverter(); }
1119 #if !Microsoft_CONTROL
1120 else if (typeStr.EndsWith(".MapAreaCoordinatesConverter", StringComparison.OrdinalIgnoreCase)) { result = new MapAreaCoordinatesConverter(); }
1121 #endif //Microsoft_CONTROL
1122 else if (typeStr.EndsWith(".ElementPositionConverter", StringComparison.OrdinalIgnoreCase)) { result = new ElementPositionConverter(); }
1123 else if (typeStr.EndsWith(".SeriesAreaNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesAreaNameConverter(); }
1124 else if (typeStr.EndsWith(".ChartDataSourceConverter", StringComparison.OrdinalIgnoreCase)) { result = new ChartDataSourceConverter(); }
1125 else if (typeStr.EndsWith(".SeriesDataSourceMemberConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesDataSourceMemberConverter(); }
1126 else if (typeStr.EndsWith(".SeriesLegendNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesLegendNameConverter(); }
1127 else if (typeStr.EndsWith(".ChartTypeConverter", StringComparison.OrdinalIgnoreCase)) { result = new ChartTypeConverter(); }
1128 else if (typeStr.EndsWith(".SeriesNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesNameConverter(); }
1129 else if (typeStr.EndsWith(".NoNameExpandableObjectConverter", StringComparison.OrdinalIgnoreCase)) { result = new NoNameExpandableObjectConverter(); }
1130 else if (typeStr.EndsWith(".DoubleArrayConverter", StringComparison.OrdinalIgnoreCase)) { result = new DoubleArrayConverter(); }
1131 else if (typeStr.EndsWith(".DataPointValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new DataPointValueConverter(); }
1132 else if (typeStr.EndsWith(".SeriesYValueTypeConverter", StringComparison.OrdinalIgnoreCase)) { result = new SeriesYValueTypeConverter(typeof(ChartValueType)); }
1133 else if (typeStr.EndsWith(".ColorArrayConverter", StringComparison.OrdinalIgnoreCase)) { result = new ColorArrayConverter(); }
1134 else if (typeStr.EndsWith(".LegendAreaNameConverter", StringComparison.OrdinalIgnoreCase)) { result = new LegendAreaNameConverter(); }
1135 else if (typeStr.EndsWith(".LegendConverter", StringComparison.OrdinalIgnoreCase)) { result = new LegendConverter(); }
1136 else if (typeStr.EndsWith(".SizeEmptyValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new SizeEmptyValueConverter(); }
1137 else if (typeStr.EndsWith(".MarginExpandableObjectConverter", StringComparison.OrdinalIgnoreCase)) { result = new MarginExpandableObjectConverter(); }
1138 else if (typeStr.EndsWith(".IntNanValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new IntNanValueConverter(); }
1139 else if (typeStr.EndsWith(".AxesArrayConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxesArrayConverter(); }
1140 else if (typeStr.EndsWith(".AxisLabelDateValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisLabelDateValueConverter(); }
1141 else if (typeStr.EndsWith(".AxisMinMaxValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisMinMaxValueConverter(); }
1142 else if (typeStr.EndsWith(".AxisCrossingValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisCrossingValueConverter(); }
1143 else if (typeStr.EndsWith(".AxisMinMaxAutoValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisMinMaxAutoValueConverter(); }
1144 else if (typeStr.EndsWith(".StripLineTitleAngleConverter", StringComparison.OrdinalIgnoreCase)) { result = new StripLineTitleAngleConverter(); }
1145 else if (typeStr.EndsWith(".AxisIntervalValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisIntervalValueConverter(); }
1146 else if (typeStr.EndsWith(".AxisElementIntervalValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AxisElementIntervalValueConverter(); }
1147 else if (typeStr.EndsWith(".AnchorPointValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AnchorPointValueConverter(); }
1148 else if (typeStr.EndsWith(".AnnotationAxisValueConverter", StringComparison.OrdinalIgnoreCase)) { result = new AnnotationAxisValueConverter(); }
1150 if (result != null) _converterDict[attr.ConverterTypeName] = result;
1157 #region Serializable content list managment fields, methods and classes
1160 /// Stores information about content item (class or property)
1162 private class ItemInfo
1164 public string name = "";
1165 public bool any = false;
1166 public bool startsWith = false;
1167 public bool endsWith = false;
1170 // Storage for serializable content items
1171 private ArrayList serializableContentList = null;
1173 // Storage for non serializable content items
1174 private ArrayList nonSerializableContentList = null;
1177 /// Return serializable content list.
1179 /// <returns>Serializable content list.</returns>
1180 private ArrayList GetSerializableContentList()
1182 if(serializableContentList == null)
1184 serializableContentList = new ArrayList();
1186 serializableContentList,
1187 (this.SerializableContent.Length > 0 ) ? this.SerializableContent : "*.*");
1190 return serializableContentList;
1194 /// Return non serializable content list.
1196 /// <returns>Non serializable content list.</returns>
1197 private ArrayList GetNonSerializableContentList()
1199 if(nonSerializableContentList == null)
1201 nonSerializableContentList = new ArrayList();
1202 FillContentList(nonSerializableContentList, this.NonSerializableContent);
1205 return nonSerializableContentList;
1209 /// Fill content list from the string.
1211 /// <param name="list">Array list class.</param>
1212 /// <param name="content">Content string.</param>
1213 private void FillContentList(ArrayList list, string content)
1215 if(content.Length > 0)
1217 string[] classPropertyPairs = content.Split(',');
1218 foreach(string item in classPropertyPairs)
1220 // Create two content items: one for the class and one for the property
1221 ItemInfo classInfo = new ItemInfo();
1222 ItemInfo propertyInfo = new ItemInfo();
1224 // Find class and property name
1225 int pointIndex = item.IndexOf('.');
1226 if(pointIndex == -1)
1228 throw (new ArgumentException(SR.ExceptionChartSerializerContentStringFormatInvalid));
1230 classInfo.name = item.Substring(0, pointIndex).Trim();
1231 propertyInfo.name = item.Substring(pointIndex + 1).Trim();
1232 if(classInfo.name.Length == 0)
1234 throw (new ArgumentException(SR.ExceptionChartSerializerClassNameUndefined));
1236 if(propertyInfo.name.Length == 0)
1238 throw (new ArgumentException(SR.ExceptionChartSerializerPropertyNameUndefined));
1241 // Make sure property name do not have point character
1242 if(propertyInfo.name.IndexOf('.') != -1)
1244 throw (new ArgumentException(SR.ExceptionChartSerializerContentStringFormatInvalid));
1247 // Check for wildcards in names
1248 CheckWildCars(classInfo);
1249 CheckWildCars(propertyInfo);
1251 // Add class & property items into the array
1252 list.Add(classInfo);
1253 list.Add(propertyInfo);
1259 /// Checks wildcards in the name of the item.
1260 /// Possible values:
1265 /// <param name="info">Item information class.</param>
1266 private void CheckWildCars(ItemInfo info)
1269 if(info.name == "*")
1274 // Ends with class mask
1275 else if(info.name[info.name.Length - 1] == '*')
1277 info.endsWith = true;
1278 info.name = info.name.TrimEnd('*');
1281 // Starts with class mask
1282 else if(info.name[0] == '*')
1284 info.startsWith = true;
1285 info.name = info.name.TrimStart('*');
1293 /// Utility class which serialize object using XML format
1295 internal class XmlFormatSerializer : SerializerBase
1297 #region Serialization public methods
1300 /// Serialize specified object into the stream.
1302 /// <param name="objectToSerialize">Object to be serialized.</param>
1303 /// <param name="stream">The stream used to write the XML document.</param>
1305 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1306 internal void Serialize(object objectToSerialize, Stream stream)
1308 Serialize(objectToSerialize, (object)stream);
1312 /// Serialize specified object into the XML writer.
1314 /// <param name="objectToSerialize">Object to be serialized.</param>
1315 /// <param name="xmlWriter">The XmlWriter used to write the XML document.</param>
1316 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1317 internal void Serialize(object objectToSerialize, XmlWriter xmlWriter)
1319 Serialize(objectToSerialize, (object)xmlWriter);
1323 /// Serialize specified object into the text writer.
1325 /// <param name="objectToSerialize">Object to be serialized.</param>
1326 /// <param name="textWriter">The TextWriter used to write the XML document.</param>
1327 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1328 internal void Serialize(object objectToSerialize, TextWriter textWriter)
1330 Serialize(objectToSerialize, (object)textWriter);
1334 /// Serialize specified object into the file.
1336 /// <param name="objectToSerialize">Object to be serialized.</param>
1337 /// <param name="fileName">The file name used to write the XML document.</param>
1338 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1339 internal void Serialize(object objectToSerialize, string fileName)
1341 Serialize(objectToSerialize, (object)fileName);
1346 #region Serialization private methods
1349 /// Serialize specified object into different types of writers using XML format.
1350 /// Here is what is serialized in the object:
1351 /// - all public properties with Set and Get methods
1352 /// - all public properties with Get method which derived from ICollection
1354 /// <param name="objectToSerialize">Object to be serialized.</param>
1355 /// <param name="writer">Defines the serialization destination. Can be Stream, TextWriter, XmlWriter or String (file name).</param>
1357 internal override void Serialize(object objectToSerialize, object writer)
1359 // the possible writer types
1360 Stream stream = writer as Stream;
1361 TextWriter textWriter = writer as TextWriter;
1362 XmlWriter xmlWriter = writer as XmlWriter;
1363 string writerStr = writer as string;
1365 // Check input parameters
1366 if(objectToSerialize == null)
1368 throw(new ArgumentNullException("objectToSerialize"));
1372 throw(new ArgumentNullException("writer"));
1374 if(stream == null && textWriter == null && xmlWriter == null && writerStr == null)
1376 throw (new ArgumentException(SR.ExceptionChartSerializerWriterObjectInvalid, "writer"));
1379 // Create XML document
1380 XmlDocument xmlDocument = new XmlDocument();
1382 // Create document fragment
1383 XmlDocumentFragment docFragment = xmlDocument.CreateDocumentFragment();
1388 SerializeObject(objectToSerialize, null, GetObjectName(objectToSerialize), docFragment, xmlDocument);
1391 // Append document fragment
1392 xmlDocument.AppendChild(docFragment);
1394 // Remove empty child nodes
1395 RemoveEmptyChildNodes(xmlDocument);
1397 // Save XML document into the writer
1400 xmlDocument.Save(stream);
1402 // Flush stream and seek to the beginning
1404 stream.Seek(0, SeekOrigin.Begin);
1407 if(writerStr != null)
1409 xmlDocument.Save(writerStr);
1412 if(xmlWriter != null)
1414 xmlDocument.Save(xmlWriter);
1417 if(textWriter != null)
1419 xmlDocument.Save(textWriter);
1423 /// Serialize specified object into the XML format.
1424 /// Method is called recursively to serialize child objects.
1426 /// <param name="objectToSerialize">Object to be serialized.</param>
1427 /// <param name="parent">Parent of the serialized object.</param>
1428 /// <param name="elementName">Object element name.</param>
1429 /// <param name="xmlParentNode">The XmlNode of the parent object to serialize the data in.</param>
1430 /// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
1431 virtual protected void SerializeObject(object objectToSerialize, object parent, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
1433 // Check input parameters
1434 if(objectToSerialize == null)
1439 // Check if object should be serialized
1442 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
1445 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
1446 if(styleAttribute != null)
1449 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
1457 // Check if object is a collection
1458 if(objectToSerialize is ICollection)
1460 // Serialize collection
1461 SerializeCollection(objectToSerialize, elementName, xmlParentNode, xmlDocument);
1465 // Create object element inside the parents node
1466 XmlNode xmlNode = xmlDocument.CreateElement(elementName);
1467 xmlParentNode.AppendChild(xmlNode);
1469 // Write template data into collection items
1470 bool templateListItem = false;
1471 IList parentList = parent as IList;
1472 if(this.IsTemplateMode && parentList != null)
1474 // Create "_Template_" attribute
1475 XmlAttribute attrib = xmlDocument.CreateAttribute("_Template_");
1477 // Check number of items in collection
1478 if (parentList.Count == 1)
1480 // If only one iten in collection, set "All" value.
1481 // This means that style of this object should be applied to all
1482 // existing items of the collection.
1483 attrib.Value = "All";
1487 // If there is more than one item, use it's index.
1488 // When loading, style of these items will be applied to existing
1489 // items in collection in the loop.
1490 int itemIndex = parentList.IndexOf(objectToSerialize);
1491 attrib.Value = itemIndex.ToString(CultureInfo.InvariantCulture);
1494 // Add "_Template_" attribute into the XML node
1495 xmlNode.Attributes.Append(attrib);
1496 templateListItem = true;
1499 // Retrive properties list of the object
1500 PropertyInfo[] properties = objectToSerialize.GetType().GetProperties();
1501 if (properties != null)
1503 // Loop through all properties and serialize public properties
1504 foreach(PropertyInfo pi in properties)
1507 // Skip "Name" property from collection items in template mode
1508 if(templateListItem && pi.Name == "Name")
1513 // Skip inherited properties from the root object
1514 if(IsChartBaseProperty(objectToSerialize, parent, pi))
1519 // Check if this property is serializable content
1520 if (!IsSerializableContent(pi.Name, objectToSerialize))
1525 // Serialize collection
1527 if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null && !this.SerializeICollAsAtribute(pi, objectToSerialize))
1529 // Check if SerializationVisibilityAttribute is set
1530 bool serialize = true;
1531 if(objectToSerialize != null)
1533 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
1536 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
1537 if(styleAttribute != null)
1539 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
1546 // Check if collection has "ShouldSerialize" method
1547 MethodInfo mi = objectToSerialize.GetType().GetMethod("ShouldSerialize" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
1550 object result = mi.Invoke(objectToSerialize, null);
1551 if(result is bool && ((bool)result) == false)
1553 // Do not serialize collection
1558 // Serialize collection
1561 SerializeCollection(pi.GetValue(objectToSerialize, null), pi.Name, xmlNode, xmlDocument);
1565 // Serialize public properties with Get and Set methods
1566 else if(pi.CanRead && pi.CanWrite)
1569 if(pi.Name == "Item")
1574 // Check if an object should be serialized as a property or as a class
1575 if(ShouldSerializeAsAttribute(pi, objectToSerialize))
1577 // Serialize property
1578 SerializeProperty(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, xmlNode, xmlDocument);
1582 // Serialize inner object
1583 SerializeObject(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, xmlNode, xmlDocument);
1593 /// Serializes the data point.
1595 /// <param name="objectToSerialize">The object to serialize.</param>
1596 /// <param name="xmlParentNode">The XML parent node.</param>
1597 /// <param name="xmlDocument">The XML document.</param>
1598 internal void SerializeDataPoint(object objectToSerialize, XmlNode xmlParentNode, XmlDocument xmlDocument)
1600 // Create object element inside the parents node
1601 XmlNode xmlNode = xmlDocument.CreateElement(GetObjectName(objectToSerialize));
1602 xmlParentNode.AppendChild(xmlNode);
1604 DataPoint dataPoint = objectToSerialize as DataPoint;
1605 if (dataPoint.XValue != 0d && IsSerializableContent("XValue", objectToSerialize))
1607 XmlAttribute attrib = xmlDocument.CreateAttribute("XValue");
1608 attrib.Value = GetXmlValue(dataPoint.XValue, dataPoint, "XValue");
1609 xmlNode.Attributes.Append(attrib);
1611 if (dataPoint.YValues.Length > 0 && IsSerializableContent("YValues", objectToSerialize))
1613 XmlAttribute attrib = xmlDocument.CreateAttribute("YValues");
1614 attrib.Value = GetXmlValue(dataPoint.YValues, dataPoint, "YValues");
1615 xmlNode.Attributes.Append(attrib);
1617 if (dataPoint.IsEmpty && IsSerializableContent("IsEmpty", objectToSerialize))
1619 XmlAttribute attrib = xmlDocument.CreateAttribute("IsEmpty");
1620 attrib.Value = GetXmlValue(dataPoint.isEmptyPoint, dataPoint, "IsEmpty");
1621 xmlNode.Attributes.Append(attrib);
1623 bool hasCustomProperties = false;
1624 foreach (DictionaryEntry entry in dataPoint.properties)
1626 if (entry.Key is int)
1628 CommonCustomProperties propertyType = (CommonCustomProperties)((int)entry.Key);
1629 String properyName = propertyType.ToString();
1630 if (IsSerializableContent(properyName, objectToSerialize))
1632 XmlAttribute attrib = xmlDocument.CreateAttribute(properyName);
1633 attrib.Value = GetXmlValue(entry.Value, dataPoint, properyName);
1634 xmlNode.Attributes.Append(attrib);
1639 hasCustomProperties = true;
1643 if (hasCustomProperties && !String.IsNullOrEmpty(dataPoint.CustomProperties) && IsSerializableContent("CustomProperties", objectToSerialize))
1645 XmlAttribute attrib = xmlDocument.CreateAttribute("CustomProperties");
1646 attrib.Value = GetXmlValue(dataPoint.CustomProperties, dataPoint, "CustomProperties");
1647 xmlNode.Attributes.Append(attrib);
1653 /// Serialize specified object into the XML text writer.
1654 /// Method is called recursively to serialize child objects.
1656 /// <param name="objectToSerialize">Object to be serialized.</param>
1657 /// <param name="elementName">Object element name.</param>
1658 /// <param name="xmlParentNode">The XmlNode of the parent object to serialize the data in.</param>
1659 /// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
1660 virtual protected void SerializeCollection(object objectToSerialize, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
1662 ICollection collection = objectToSerialize as ICollection;
1663 if(collection != null)
1665 // Create object element inside the parents node
1666 XmlNode xmlNode = xmlDocument.CreateElement(elementName);
1667 xmlParentNode.AppendChild(xmlNode);
1668 // Enumerate through all objects in collection and serialize them
1669 foreach(object obj in collection)
1672 if (obj is DataPoint)
1674 SerializeDataPoint(obj, xmlNode, xmlDocument);
1678 SerializeObject(obj, objectToSerialize, GetObjectName(obj), xmlNode, xmlDocument);
1684 /// Serialize specified object into the XML text writer.
1685 /// Method is called recursively to serialize child objects.
1687 /// <param name="objectToSerialize">Object to be serialized.</param>
1688 /// <param name="parent">Parent of the serialized object.</param>
1689 /// <param name="elementName">Object element name.</param>
1690 /// <param name="xmlParentNode">The XmlNode of the parent object to serialize the data in.</param>
1691 /// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
1692 virtual protected void SerializeProperty(object objectToSerialize, object parent, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
1694 // Check input parameters
1695 if(objectToSerialize == null || parent == null)
1700 // Check if property has non-default value
1701 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
1704 DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
1705 if(defValueAttribute != null)
1707 if(objectToSerialize.Equals(defValueAttribute.Value))
1709 // Do not serialize properties with default values
1715 // Check if property has "ShouldSerialize" method
1716 MethodInfo mi = parent.GetType().GetMethod("ShouldSerialize" + elementName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
1719 object result = mi.Invoke(parent, null);
1720 if(result is bool && ((bool)result) == false)
1722 // Do not serialize properties with default values
1728 // Check XmlFormatSerializerStyle attribute
1729 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
1730 if(styleAttribute != null)
1733 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
1740 // Serialize property as a parents node attribute
1741 XmlAttribute attrib = xmlDocument.CreateAttribute(elementName);
1742 attrib.Value = GetXmlValue(objectToSerialize, parent, elementName);
1743 xmlParentNode.Attributes.Append(attrib);
1747 /// Converts object value into the string.
1749 /// <param name="obj">Object to convert.</param>
1750 /// <param name="parent">Object parent.</param>
1751 /// <param name="elementName">Object name.</param>
1752 /// <returns>Object value as strig.</returns>
1753 protected string GetXmlValue(object obj, object parent, string elementName)
1755 string objStr = obj as string;
1761 Font font = obj as Font;
1764 return SerializerBase.FontToString(font);
1769 return colorConverter.ConvertToString(null, System.Globalization.CultureInfo.InvariantCulture, obj);
1772 Color[] colors = obj as Color[];
1775 return ColorArrayConverter.ColorArrayToString(colors);
1778 #if !Microsoft_CONTROL
1781 Unit unit = (Unit)obj;
1782 return unit.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
1786 System.Drawing.Image image = obj as System.Drawing.Image;
1789 return ImageToString(image);
1792 // Look for the converter set with the attibute
1793 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
1796 TypeConverter converter = this.FindConverter(pd);
1797 if (converter != null && converter.CanConvertTo(typeof(string)))
1799 return converter.ConvertToString(null, System.Globalization.CultureInfo.InvariantCulture, obj);
1803 // Try using default string convertion
1804 return obj.ToString();
1808 /// Removes all empty nodes from the XML document.
1809 /// Method is called recursively to remove empty child nodes first.
1811 /// <param name="xmlNode">The node where to start the removing.</param>
1812 private void RemoveEmptyChildNodes(XmlNode xmlNode)
1814 // Loop through all child nodes
1815 for(int nodeIndex = 0; nodeIndex < xmlNode.ChildNodes.Count; nodeIndex++)
1817 // Remove empty child nodes of the child
1818 RemoveEmptyChildNodes(xmlNode.ChildNodes[nodeIndex]);
1820 // Check if there are any non-empty nodes left
1821 XmlNode currentNode = xmlNode.ChildNodes[nodeIndex];
1822 if( currentNode.ParentNode != null &&
1823 !(currentNode.ParentNode is XmlDocument) )
1825 if(!currentNode.HasChildNodes &&
1826 (currentNode.Attributes == null ||
1827 currentNode.Attributes.Count == 0))
1830 xmlNode.RemoveChild(xmlNode.ChildNodes[nodeIndex]);
1837 // Remove node with one "_Template_" attribute
1838 if(!currentNode.HasChildNodes &&
1839 currentNode.Attributes.Count == 1 &&
1840 currentNode.Attributes["_Template_"] != null)
1843 xmlNode.RemoveChild(xmlNode.ChildNodes[nodeIndex]);
1854 #region Deserialization public methods
1857 /// Deserialize specified object from the stream.
1859 /// <param name="objectToDeserialize">Object to be deserialized.</param>
1860 /// <param name="stream">The stream used to read the XML document from.</param>
1861 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1862 internal void Deserialize(object objectToDeserialize, Stream stream)
1864 Deserialize(objectToDeserialize, (object)stream);
1868 /// Deserialize specified object from the XML reader.
1870 /// <param name="objectToDeserialize">Object to be deserialized.</param>
1871 /// <param name="xmlReader">The XmlReader used to read the XML document from.</param>
1872 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1873 internal void Deserialize(object objectToDeserialize, XmlReader xmlReader)
1875 Deserialize(objectToDeserialize, (object)xmlReader);
1879 /// Deserialize specified object from the text reader.
1881 /// <param name="objectToDeserialize">Object to be deserialized.</param>
1882 /// <param name="textReader">The TextReader used to write the XML document from.</param>
1883 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1884 internal void Deserialize(object objectToDeserialize, TextReader textReader)
1886 Deserialize(objectToDeserialize, (object)textReader);
1890 /// Deserialize specified object from the file.
1892 /// <param name="objectToDeserialize">Object to be deserialized.</param>
1893 /// <param name="fileName">The file name used to read the XML document from.</param>
1894 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1895 internal void Deserialize(object objectToDeserialize, string fileName)
1897 Deserialize(objectToDeserialize, (object)fileName);
1902 #region Deserialization private methods
1905 /// Deserialize object from different types of readers using XML format.
1907 /// <param name="objectToDeserialize">Object to be deserialized.</param>
1908 /// <param name="reader">Defines the deserialization data source. Can be Stream, TextReader, XmlReader or String (file name).</param>
1909 internal override void Deserialize(object objectToDeserialize, object reader)
1911 // the four possible types of readers
1912 Stream stream = reader as Stream;
1913 TextReader textReader = reader as TextReader;
1914 XmlReader xmlReader = reader as XmlReader;
1915 string readerStr = reader as string;
1917 // Check input parameters
1918 if(objectToDeserialize == null)
1920 throw(new ArgumentNullException("objectToDeserialize"));
1924 throw(new ArgumentNullException("reader"));
1926 if(stream == null && textReader == null && xmlReader == null && readerStr == null)
1928 throw (new ArgumentException(SR.ExceptionChartSerializerReaderObjectInvalid, "reader"));
1931 // Create XML document
1932 XmlDocument xmlDocument = new XmlDocument();
1933 XmlReader xmlBaseReader = null;
1936 // process files without DTD
1937 XmlReaderSettings settings = new XmlReaderSettings();
1938 // settings.ProhibitDtd is obsolete inn NetFx 4.0, the #ifdef stays for compilation under NetFx 3.5.
1940 settings.ProhibitDtd = true;
1942 settings.DtdProcessing = DtdProcessing.Prohibit; //don't allow DTD
1944 // Load XML document from the reader
1947 xmlBaseReader = XmlReader.Create(stream, settings);
1949 if (readerStr != null)
1951 xmlBaseReader = XmlReader.Create(readerStr, settings);
1953 if (xmlReader != null)
1955 xmlBaseReader = XmlReader.Create(xmlReader, settings);
1957 if (textReader != null)
1959 xmlBaseReader = XmlReader.Create(textReader, settings);
1962 xmlDocument.Load(xmlBaseReader);
1964 // Reset properties of the root object
1965 if (IsResetWhenLoading)
1967 ResetObjectProperties(objectToDeserialize);
1970 // Deserialize object
1971 DeserializeObject(objectToDeserialize, null, GetObjectName(objectToDeserialize), xmlDocument.DocumentElement, xmlDocument);
1975 if (xmlBaseReader != null)
1977 ((IDisposable)xmlBaseReader).Dispose();
1983 /// Deserialize object from the XML format.
1984 /// Method is called recursively to deserialize child objects.
1986 /// <param name="objectToDeserialize">Object to be deserialized.</param>
1987 /// <param name="parent">Parent of the deserialized object.</param>
1988 /// <param name="elementName">Object element name.</param>
1989 /// <param name="xmlParentNode">The XmlNode of the parent object to deserialize the data from.</param>
1990 /// <param name="xmlDocument">The XmlDocument the parent node belongs to.</param>
1991 /// <returns>Number of properties set.</returns>
1992 virtual internal int DeserializeObject(object objectToDeserialize, object parent, string elementName, XmlNode xmlParentNode, XmlDocument xmlDocument)
1994 int setPropertiesNumber = 0;
1996 // Check input parameters
1997 if(objectToDeserialize == null)
1999 return setPropertiesNumber;
2002 // Loop through all node properties
2003 foreach(XmlAttribute attr in xmlParentNode.Attributes)
2005 // Skip template collection item attribute
2006 if(attr.Name == "_Template_")
2011 // Check if this property is serializable content
2012 if(IsSerializableContent(attr.Name, objectToDeserialize))
2014 SetXmlValue(objectToDeserialize, attr.Name, attr.Value);
2015 ++setPropertiesNumber;
2021 // Read template data into the collection
2022 IList list = objectToDeserialize as IList;
2024 if(this.IsTemplateMode &&
2026 xmlParentNode.FirstChild.Attributes["_Template_"] != null)
2028 // Loop through all items in collection
2030 foreach(object listItem in list)
2032 // Find XML node appropriate for the item from the collection
2033 XmlNode listItemNode = null;
2035 // Loop through all child nodes
2036 foreach(XmlNode childNode in xmlParentNode.ChildNodes)
2038 string templateString = childNode.Attributes["_Template_"].Value;
2039 if(templateString != null && templateString.Length > 0)
2041 if(templateString == "All")
2043 listItemNode = childNode;
2048 // If there is more items in collection than XML node in template
2049 // apply items in a loop
2050 int loopItemIndex = itemIndex;
2051 while(loopItemIndex > xmlParentNode.ChildNodes.Count - 1)
2053 loopItemIndex -= xmlParentNode.ChildNodes.Count;
2056 // Convert attribute value to index
2057 int nodeIndex = int.Parse(templateString, CultureInfo.InvariantCulture);
2058 if(nodeIndex == loopItemIndex)
2060 listItemNode = childNode;
2067 // Load data from the node
2068 if(listItemNode != null)
2071 DeserializeObject(listItem, objectToDeserialize, "", listItemNode, xmlDocument);
2074 // Increase item index
2078 // No futher loading required
2084 // Loop through all child elements
2085 int listItemIndex = 0;
2086 foreach(XmlNode childNode in xmlParentNode.ChildNodes)
2088 // Special handling for the collections
2089 // Bug VSTS #235707 - The collections IsSerializableContent are already checked as a property in the else statement.
2092 // Create new item object
2093 string itemName = null;
2094 if (childNode.Attributes["Name"] != null)
2096 itemName = childNode.Attributes["Name"].Value;
2099 bool reusedObject = false;
2100 object listItem = GetListNewItem(list, childNode.Name, ref itemName, ref reusedObject);
2102 // Deserialize list item object
2103 int itemSetProperties = DeserializeObject(listItem, objectToDeserialize, "", childNode, xmlDocument);
2104 setPropertiesNumber += itemSetProperties;
2106 // Add item object into the list
2107 if (itemSetProperties > 0 || reusedObject)
2109 list.Insert(listItemIndex++, listItem);
2115 // Check if this property is serializable content
2116 if (IsSerializableContent(childNode.Name, objectToDeserialize))
2118 // Deserialize the property using property descriptor
2119 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToDeserialize)[childNode.Name];
2122 object innerObject = pd.GetValue(objectToDeserialize);
2124 // Deserialize list item object
2125 setPropertiesNumber += DeserializeObject(innerObject, objectToDeserialize, childNode.Name, childNode, xmlDocument);
2127 else if (!IsUnknownAttributeIgnored)
2129 throw (new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown(childNode.Name, objectToDeserialize.GetType().ToString())));
2135 return setPropertiesNumber;
2139 /// Sets a property of an object using name and value as string.
2141 /// <param name="obj">Object to set.</param>
2142 /// <param name="attrName">Attribute (property) name.</param>
2143 /// <param name="attrValue">Object value..</param>
2144 /// <returns>Object value as strig.</returns>
2145 private void SetXmlValue(object obj, string attrName, string attrValue)
2147 PropertyInfo pi = obj.GetType().GetProperty(attrName);
2150 // Convert string to object value
2151 object objValue = attrValue;
2153 if(pi.PropertyType == typeof(string))
2155 objValue = attrValue;
2158 else if(pi.PropertyType == typeof(Font))
2160 objValue = SerializerBase.FontFromString(attrValue);
2163 else if(pi.PropertyType == typeof(Color))
2165 objValue = (Color)colorConverter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, attrValue);
2168 #if !Microsoft_CONTROL
2169 else if(pi.PropertyType == typeof(Unit))
2171 objValue = new Unit(Int32.Parse(attrValue, System.Globalization.CultureInfo.InvariantCulture));
2175 else if(pi.PropertyType == typeof(System.Drawing.Image))
2177 objValue = ImageFromString(attrValue);
2182 // Look for the converter set with the attibute
2183 PropertyDescriptor pd = TypeDescriptor.GetProperties(obj)[attrName];
2186 TypeConverter converter = this.FindConverter(pd);
2187 if (converter != null && converter.CanConvertFrom(typeof(string)))
2189 objValue = converter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, attrValue);
2195 pi.SetValue(obj, objValue, null);
2197 else if(!IsUnknownAttributeIgnored)
2199 throw(new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown( attrName,obj.GetType().ToString())));
2207 /// Utility class which serialize object using binary format
2209 internal class BinaryFormatSerializer : SerializerBase
2211 #region Serialization methods
2214 /// Serialize specified object into the destination using binary format.
2216 /// <param name="objectToSerialize">Object to be serialized.</param>
2217 /// <param name="destination">Serialization destination.</param>
2218 internal override void Serialize(object objectToSerialize, object destination)
2220 // Check input parameters
2221 if (objectToSerialize == null)
2223 throw (new ArgumentNullException("objectToSerialize"));
2225 if (destination == null)
2227 throw (new ArgumentNullException("destination"));
2230 string destinationStr = destination as string;
2231 if (destinationStr != null)
2233 Serialize(objectToSerialize, destinationStr);
2237 Stream stream = destination as Stream;
2240 Serialize(objectToSerialize, stream);
2244 BinaryWriter binaryWriter = destination as BinaryWriter;
2245 if (binaryWriter != null)
2247 Serialize(objectToSerialize, binaryWriter);
2251 throw (new ArgumentException(SR.ExceptionChartSerializerDestinationObjectInvalid, "destination"));
2255 /// Serialize specified object into the file using binary format.
2257 /// <param name="objectToSerialize">Object to be serialized.</param>
2258 /// <param name="fileName">File name to serialize the data in.</param>
2259 internal void Serialize(object objectToSerialize, string fileName)
2261 FileStream stream = new FileStream(fileName, FileMode.Create);
2262 Serialize(objectToSerialize, new BinaryWriter(stream));
2268 /// Serialize specified object into the stream using binary format.
2270 /// <param name="objectToSerialize">Object to be serialized.</param>
2271 /// <param name="stream">Defines the serialization destination.</param>
2272 internal void Serialize(object objectToSerialize, Stream stream)
2274 Serialize(objectToSerialize, new BinaryWriter(stream));
2278 /// Serialize specified object into different types of writers using binary format.
2279 /// Here is what is serialized in the object:
2280 /// - all public properties with Set and Get methods
2281 /// - all public properties with Get method which derived from ICollection
2283 /// <param name="objectToSerialize">Object to be serialized.</param>
2284 /// <param name="writer">Defines the serialization destination.</param>
2285 internal void Serialize(object objectToSerialize, BinaryWriter writer)
2287 // Check input parameters
2288 if(objectToSerialize == null)
2290 throw(new ArgumentNullException("objectToSerialize"));
2294 throw(new ArgumentNullException("writer"));
2297 // Write bnary format header into the stream, which consist of 15 characters
2298 char[] header = new char[15] {'D', 'C', 'B', 'F', '4', '0', '0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'};
2299 writer.Write(header);
2303 SerializeObject(objectToSerialize, null, GetObjectName(objectToSerialize), writer);
2306 // Flush the writer stream
2309 // Reset stream position
2310 writer.Seek(0, SeekOrigin.Begin);
2314 /// Serialize specified object into the binary format.
2315 /// Method is called recursively to serialize child objects.
2317 /// <param name="objectToSerialize">Object to be serialized.</param>
2318 /// <param name="parent">Parent of the serialized object.</param>
2319 /// <param name="elementName">Object element name.</param>
2320 /// <param name="writer">Binary writer object.</param>
2321 virtual internal void SerializeObject(object objectToSerialize, object parent, string elementName, BinaryWriter writer)
2323 // Check input parameters
2324 if(objectToSerialize == null)
2329 // Check if object should be serialized
2332 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
2335 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
2336 if(styleAttribute != null)
2339 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
2347 // Check if object is a collection
2348 if(objectToSerialize is ICollection)
2350 // Serialize collection
2351 SerializeCollection(objectToSerialize, elementName, writer);
2355 // Write object ID (hash of the name) into the writer
2356 writer.Write(SerializerBase.GetStringHashCode(elementName));
2358 // Remember position where object data is started
2359 long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
2361 // Retrive properties list of the object
2362 ArrayList propNamesList = new ArrayList();
2363 PropertyInfo[] properties = objectToSerialize.GetType().GetProperties();
2364 if(properties != null)
2366 // Loop through all properties and serialize public properties
2367 foreach(PropertyInfo pi in properties)
2369 // Skip inherited properties from the root object
2370 if(IsChartBaseProperty(objectToSerialize, parent, pi))
2374 // Serialize collection
2375 if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null && !this.SerializeICollAsAtribute(pi, objectToSerialize))
2377 bool serialize = IsSerializableContent(pi.Name, objectToSerialize);
2379 // fixing Axes Array Framework 2.0 side effect
2381 if (serialize && objectToSerialize != null)
2383 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
2386 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
2387 if (styleAttribute != null)
2389 if (styleAttribute.Visibility == SerializationVisibility.Hidden)
2397 MethodInfo mi = objectToSerialize.GetType().GetMethod("ShouldSerialize" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
2398 if( serialize && mi != null)
2400 object result = mi.Invoke(objectToSerialize, null);
2401 if(result is bool && ((bool)result) == false)
2403 // Do not serialize collection
2408 // Serialize collection
2411 propNamesList.Add(pi.Name);
2412 SerializeCollection(pi.GetValue(objectToSerialize, null), pi.Name, writer);
2416 // Serialize public properties with Get and Set methods
2417 else if(pi.CanRead && pi.CanWrite)
2420 if(pi.Name == "Item")
2424 // Check if this property is serializable content
2425 if (IsSerializableContent(pi.Name, objectToSerialize))
2427 // Check if an object should be serialized as a property or as a class
2428 if (ShouldSerializeAsAttribute(pi, objectToSerialize))
2430 // Serialize property
2431 SerializeProperty(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, writer);
2435 // Serialize inner object
2436 SerializeObject(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, writer);
2439 propNamesList.Add(pi.Name);
2443 // Check that all properties have unique IDs
2444 CheckPropertiesID(propNamesList);
2448 // If position of the writer did not change - nothing was written
2449 if(writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
2451 // Remove object ID from the stream
2452 writer.Seek(-2, SeekOrigin.Current);
2453 writer.Write((short)0);
2454 writer.Seek(-2, SeekOrigin.Current);
2458 // Write the end objectTag
2459 writer.Write((short)0);
2467 /// Serializes the data point.
2469 /// <param name="objectToSerialize">The object to serialize.</param>
2470 /// <param name="elementName">Name of the element.</param>
2471 /// <param name="writer">The writer.</param>
2472 private void SerializeDataPoint(object objectToSerialize, string elementName, BinaryWriter writer)
2475 // Write object ID (hash of the name) into the writer
2476 writer.Write(SerializerBase.GetStringHashCode(elementName));
2477 // Remember position where object data is started
2478 long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
2480 DataPoint dataPoint = objectToSerialize as DataPoint;
2481 if (dataPoint.XValue != 0d && IsSerializableContent("XValue", objectToSerialize))
2483 SerializeProperty(dataPoint.XValue, dataPoint, "XValue", writer);
2485 if (dataPoint.YValues.Length > 0 && IsSerializableContent("YValues", objectToSerialize))
2487 SerializeProperty(dataPoint.YValues, dataPoint, "YValues", writer);
2489 if (dataPoint.IsEmpty && IsSerializableContent("IsEmpty", objectToSerialize))
2491 SerializeProperty(dataPoint.IsEmpty, dataPoint, "IsEmpty", writer);
2493 bool hasCustomProperties = false;
2494 foreach (DictionaryEntry entry in dataPoint.properties)
2496 if (entry.Key is int)
2498 CommonCustomProperties propertyType = (CommonCustomProperties)((int)entry.Key);
2499 String properyName = propertyType.ToString();
2500 if (IsSerializableContent(properyName, objectToSerialize))
2502 SerializeProperty(entry.Value, dataPoint, properyName, writer);
2507 hasCustomProperties = true;
2511 if (hasCustomProperties && !String.IsNullOrEmpty(dataPoint.CustomProperties) && IsSerializableContent("CustomProperties", objectToSerialize))
2513 SerializeProperty(dataPoint.CustomProperties, dataPoint, "CustomProperties", writer);
2516 // If position of the writer did not change - nothing was written
2517 if (writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
2519 // Remove object ID from the stream
2520 writer.Seek(-2, SeekOrigin.Current);
2521 writer.Write((short)0);
2522 writer.Seek(-2, SeekOrigin.Current);
2526 // Write the end objectTag
2527 writer.Write((short)0);
2533 /// Serialize specified object into the binary writer.
2534 /// Method is called recursively to serialize child objects.
2536 /// <param name="objectToSerialize">Object to be serialized.</param>
2537 /// <param name="elementName">Object element name.</param>
2538 /// <param name="writer">Binary writer.</param>
2539 virtual internal void SerializeCollection(object objectToSerialize, string elementName, BinaryWriter writer)
2541 ICollection collection = objectToSerialize as ICollection;
2542 if(collection != null)
2544 // Write object ID (hash of the name) into the writer
2545 writer.Write(SerializerBase.GetStringHashCode(elementName));
2547 // Remember position where object data is started
2548 long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
2550 // Enumerate through all objects in collection and serialize them
2551 foreach (object obj in collection)
2554 if (obj is DataPoint)
2556 SerializeDataPoint(obj, GetObjectName(obj), writer);
2560 SerializeObject(obj, objectToSerialize, GetObjectName(obj), writer);
2563 // If position of the writer did not change - nothing was written
2564 if(writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
2566 // Remove object ID from the stream
2567 writer.Seek(-2, SeekOrigin.Current);
2568 writer.Write((short)0);
2569 writer.Seek(-2, SeekOrigin.Current);
2573 // Write the end objectTag
2574 writer.Write((short)0);
2581 /// Serialize specified object into the binary writer.
2582 /// Method is called recursively to serialize child objects.
2584 /// <param name="objectToSerialize">Object to be serialized.</param>
2585 /// <param name="parent">Parent of the serialized object.</param>
2586 /// <param name="elementName">Object element name.</param>
2587 /// <param name="writer">Binary writer.</param>
2588 virtual internal void SerializeProperty(object objectToSerialize, object parent, string elementName, BinaryWriter writer)
2590 // Check input parameters
2591 if(objectToSerialize == null || parent == null)
2596 // Check if property has non-default value
2597 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
2600 DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
2601 if(defValueAttribute != null)
2603 if(objectToSerialize.Equals(defValueAttribute.Value))
2605 // Do not serialize properties with default values
2611 // Check if property has "ShouldSerialize" method
2612 MethodInfo mi = parent.GetType().GetMethod("ShouldSerialize" + elementName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
2615 object result = mi.Invoke(parent, null);
2616 if(result is bool && ((bool)result) == false)
2618 // Do not serialize properties with default values
2624 // Check XmlFormatSerializerStyle attribute
2625 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
2626 if(styleAttribute != null)
2629 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
2637 WritePropertyValue(objectToSerialize, elementName, writer);
2641 /// Converts object value into the string.
2643 /// <param name="obj">Object to convert.</param>
2644 /// <param name="elementName">Object name.</param>
2645 /// <param name="writer">Binary writer.</param>
2646 /// <returns>Object value as strig.</returns>
2647 [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily",
2648 Justification = "Too large of a code change to justify making this change")]
2649 internal void WritePropertyValue(object obj, string elementName, BinaryWriter writer)
2651 // Write property ID (hash of the name) into the writer
2652 writer.Write(SerializerBase.GetStringHashCode(elementName));
2656 writer.Write(((bool)obj));
2658 else if(obj is double)
2660 writer.Write(((double)obj));
2662 else if(obj is string)
2664 writer.Write(((string)obj));
2668 writer.Write(((int)obj));
2670 else if(obj is long)
2672 writer.Write(((long)obj));
2674 else if(obj is float)
2676 writer.Write(((float)obj));
2678 else if(obj.GetType().IsEnum)
2680 // NOTE: Using 'ToString' method instead of the 'Enum.GetName' fixes
2681 // an issue (#4314 & #4424) with flagged enumerations when there are
2682 // more then 1 values set.
2683 string enumValue = obj.ToString();
2684 writer.Write(enumValue);
2687 else if(obj is byte)
2690 writer.Write((byte)obj);
2693 #if !Microsoft_CONTROL
2694 else if(obj is Unit)
2696 writer.Write(((Unit)obj).Value);
2700 else if(obj is Font)
2703 writer.Write(SerializerBase.FontToString((Font)obj));
2706 else if(obj is Color)
2709 writer.Write(((Color)obj).ToArgb());
2712 else if(obj is DateTime)
2715 writer.Write(((DateTime)obj).Ticks);
2718 else if(obj is Size)
2720 // Write as two integers
2721 writer.Write(((Size)obj).Width);
2722 writer.Write(((Size)obj).Height);
2725 else if(obj is double[])
2727 double[] arr = (double[])obj;
2729 // Write the size of the array (int)
2730 writer.Write(arr.Length);
2732 // Write each element of the array
2733 foreach(double d in arr)
2739 else if(obj is Color[])
2741 Color[] arr = (Color[])obj;
2743 // Write the size of the array (int)
2744 writer.Write(arr.Length);
2746 // Write each element of the array
2747 foreach(Color color in arr)
2749 writer.Write(color.ToArgb());
2753 else if(obj is System.Drawing.Image)
2755 // Save image into the memory stream
2756 MemoryStream imageStream = new MemoryStream();
2757 ((System.Drawing.Image)obj).Save(imageStream, ((System.Drawing.Image)obj).RawFormat);
2759 // Write the size of the data
2760 int imageSize = (int)imageStream.Seek(0, SeekOrigin.End);
2761 imageStream.Seek(0, SeekOrigin.Begin);
2762 writer.Write(imageSize);
2765 writer.Write(imageStream.ToArray());
2767 imageStream.Close();
2772 else if(obj is Margins)
2774 // Write as 4 integers
2775 writer.Write(((Margins)obj).Top);
2776 writer.Write(((Margins)obj).Bottom);
2777 writer.Write(((Margins)obj).Left);
2778 writer.Write(((Margins)obj).Right);
2785 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryTypeUnsupported(obj.GetType().ToString())));
2790 /// Checks if all properties will have a unique ID.
2791 /// Property ID is a hash of it's name.
2792 /// !!!USED IN DEBUG BUILD ONLY!!!
2794 /// <param name="propNames">Array of properties names.</param>
2795 internal void CheckPropertiesID(ArrayList propNames)
2798 if(propNames != null)
2800 // Loop through all properties and check the hash values
2801 foreach(string name1 in propNames)
2803 foreach(string name2 in propNames)
2807 if( SerializerBase.GetStringHashCode(name1) == SerializerBase.GetStringHashCode(name2) )
2809 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryHashCodeDuplicate(name1,name2)));
2819 #region Deserialization methods
2822 /// Deserialize specified object from the source using binary format.
2824 /// <param name="objectToDeserialize">Object to be deserialized.</param>
2825 /// <param name="source">Deserialization source.</param>
2826 internal override void Deserialize(object objectToDeserialize, object source)
2828 // Check input parameters
2829 if (objectToDeserialize == null)
2831 throw (new ArgumentNullException("objectToDeserialize"));
2835 throw (new ArgumentNullException("source"));
2838 string sourceStr = source as string;
2839 if (sourceStr != null)
2841 Deserialize(objectToDeserialize, sourceStr);
2845 Stream stream = source as Stream;
2848 Deserialize(objectToDeserialize, stream);
2852 BinaryWriter binaryWriter = source as BinaryWriter;
2853 if (binaryWriter != null)
2855 Deserialize(objectToDeserialize, binaryWriter);
2859 throw (new ArgumentException(SR.ExceptionChartSerializerSourceObjectInvalid, "source"));
2863 /// Deserialize object from the file using binary format.
2865 /// <param name="objectToDeserialize">Object to be deserialized.</param>
2866 /// <param name="fileName">File name to read the data from.</param>
2867 public void Deserialize(object objectToDeserialize, string fileName)
2869 FileStream stream = new FileStream(fileName, FileMode.Open);
2870 Deserialize(objectToDeserialize, new BinaryReader(stream));
2875 /// Deserialize object from the stream using binary format.
2877 /// <param name="objectToDeserialize">Object to be deserialized.</param>
2878 /// <param name="stream">Stream to read the data from.</param>
2879 public void Deserialize(object objectToDeserialize, Stream stream)
2881 Deserialize(objectToDeserialize, new BinaryReader(stream));
2885 /// Deserialize object from different types of readers using binary format.
2887 /// <param name="objectToDeserialize">Object to be deserialized.</param>
2888 /// <param name="reader">Binary reader.</param>
2889 public void Deserialize(object objectToDeserialize, BinaryReader reader)
2891 // Check input parameters
2892 if(objectToDeserialize == null)
2894 throw(new ArgumentNullException("objectToDeserialize"));
2898 throw(new ArgumentNullException("reader"));
2901 // Binary deserializer do not support IsUnknownAttributeIgnored property
2902 if(base.IsUnknownAttributeIgnored)
2904 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryIgnoreUnknownAttributesUnsupported));
2907 // Read 15 characters of file header
2908 char[] header = reader.ReadChars(15);
2909 if(header[0] != 'D' || header[1] != 'C' || header[2] != 'B' || header[3] != 'F')
2911 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryFromatInvalid));
2914 // Get ID of the root object
2915 this.ReadHashID(reader);
2917 // Reset properties of the root object
2918 if(IsResetWhenLoading)
2920 ResetObjectProperties(objectToDeserialize);
2923 // Deserialize object
2924 DeserializeObject(objectToDeserialize, null, GetObjectName(objectToDeserialize), reader, false);
2928 /// Deserialize object from the binary format.
2929 /// Method is called recursively to deserialize child objects.
2931 /// <param name="objectToDeserialize">Object to be deserialized.</param>
2932 /// <param name="parent">Parent of the deserialized object.</param>
2933 /// <param name="elementName">Object element name.</param>
2934 /// <param name="reader">Binary reader object.</param>
2935 /// <param name="skipElement">if set to <c>true</c> the element will not be set.</param>
2936 /// <returns>Number of properties set.</returns>
2937 virtual protected int DeserializeObject(object objectToDeserialize, object parent, string elementName, BinaryReader reader, bool skipElement)
2939 int setPropertiesNumber = 0;
2941 // Check input parameters
2942 if(objectToDeserialize == null)
2944 return setPropertiesNumber;
2947 // Special handling for the collections
2948 Type[] assemblyTypes = null;
2949 int listItemIndex = 0;
2951 IList list = objectToDeserialize as IList;
2955 // Loop through all list items
2957 PropertyInfo listItemPI = objectToDeserialize.GetType().GetProperty("Item", new Type[] { typeof(int) });
2958 while ((typeHash = this.ReadHashID(reader)) != 0)
2960 // Get collection item type from hashed type name
2961 string typeName = String.Empty;
2962 if(listItemPI != null)
2964 if ((SerializerBase.GetStringHashCode(listItemPI.PropertyType.Name)) == typeHash)
2966 typeName = listItemPI.PropertyType.Name;
2970 Assembly assembly = listItemPI.PropertyType.Assembly;
2971 if (assembly != null)
2973 // Find all classes derived from this type
2974 if (assemblyTypes == null)
2976 assemblyTypes = assembly.GetExportedTypes();
2978 foreach (Type type in assemblyTypes)
2980 if (type.IsSubclassOf(listItemPI.PropertyType))
2982 if ((SerializerBase.GetStringHashCode(type.Name)) == typeHash)
2984 typeName = type.Name;
2993 // Create new item object
2994 string itemName = null;
2995 bool reusedObject = false;
2996 object listItem = GetListNewItem(list, typeName, ref itemName, ref reusedObject);
2999 // Deserialize list item object
3000 int itemSetProperties = DeserializeObject(listItem, objectToDeserialize, "", reader, skipElement);
3002 // Add item object into the list
3003 if (!skipElement && (itemSetProperties > 0 || reusedObject))
3005 list.Insert(listItemIndex++, listItem);
3007 // TD: here was removed a code which doesn't work but cause heavy workload: GetListNewItem removes the reusedObject from the list.
3008 // Add properties set for collection item
3009 setPropertiesNumber += itemSetProperties;
3012 return setPropertiesNumber;
3015 // Get list of object's properties
3016 PropertyInfo[] properties = objectToDeserialize.GetType().GetProperties();
3017 if(properties == null)
3019 return setPropertiesNumber;
3022 // Get property information by reading the ID
3023 PropertyInfo pi = null;
3024 while ( (pi = ReadPropertyInfo(objectToDeserialize, parent, properties, reader)) != null)
3026 // Read simple properties
3027 if(ShouldSerializeAsAttribute(pi, objectToDeserialize))
3029 if(SetPropertyValue(objectToDeserialize, pi, reader, skipElement))
3031 ++setPropertiesNumber;
3037 // Get property descriptor
3038 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToDeserialize)[pi.Name];
3041 object innerObject = pd.GetValue(objectToDeserialize);
3043 // Deserialize inner item object
3044 setPropertiesNumber += DeserializeObject(innerObject, objectToDeserialize, pi.Name, reader, !IsSerializableContent(pi.Name, objectToDeserialize));
3046 else if(!IsUnknownAttributeIgnored)
3048 throw(new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown( pi.Name,objectToDeserialize.GetType().ToString())));
3053 return setPropertiesNumber;
3057 /// Reads and sets a property of an object.
3059 /// <param name="obj">Object to set.</param>
3060 /// <param name="pi">Property information.</param>
3061 /// <param name="reader">Binary reader.</param>
3062 /// <param name="skipElement">if set to <c>true</c> the property will not be set.</param>
3063 /// <returns>True if property was set.</returns>
3064 private bool SetPropertyValue(object obj, PropertyInfo pi, BinaryReader reader, bool skipElement)
3068 object objValue = null;
3071 if(pi.PropertyType == typeof(bool))
3073 objValue = reader.ReadBoolean();
3075 else if(pi.PropertyType == typeof(double))
3077 objValue = reader.ReadDouble();
3079 else if(pi.PropertyType == typeof(string))
3081 objValue = reader.ReadString();
3083 else if(pi.PropertyType == typeof(int))
3085 objValue = reader.ReadInt32();
3087 else if(pi.PropertyType == typeof(long))
3089 objValue = reader.ReadInt64();
3091 else if(pi.PropertyType == typeof(float))
3093 objValue = reader.ReadSingle();
3095 else if(pi.PropertyType.IsEnum)
3098 objValue = Enum.Parse(pi.PropertyType, reader.ReadString());
3100 else if(pi.PropertyType == typeof(byte))
3102 objValue = reader.ReadByte();
3105 #if !Microsoft_CONTROL
3106 else if(pi.PropertyType == typeof(Unit))
3108 objValue = new Unit((double)reader.ReadDouble());
3112 else if(pi.PropertyType == typeof(Font))
3115 objValue = SerializerBase.FontFromString(reader.ReadString());
3118 else if(pi.PropertyType == typeof(Color))
3121 objValue = Color.FromArgb(reader.ReadInt32());
3124 else if(pi.PropertyType == typeof(DateTime))
3127 objValue = new DateTime(reader.ReadInt64());
3130 else if(pi.PropertyType == typeof(Size))
3132 // Read as two integers
3133 objValue = new Size(reader.ReadInt32(), reader.ReadInt32());
3138 else if(pi.PropertyType == typeof(Margins) )
3140 // Read as 4 integers
3141 objValue = new Margins(
3145 reader.ReadInt32());
3150 else if(pi.PropertyType == typeof(double[]))
3153 double[] array = new double[reader.ReadInt32()];
3155 // Read each element of the array
3156 for(int arrayIndex = 0; arrayIndex < array.Length; arrayIndex++)
3158 array[arrayIndex] = reader.ReadDouble();
3164 else if(pi.PropertyType == typeof(Color[]))
3167 Color[] array = new Color[reader.ReadInt32()];
3169 // Read each element of the array
3170 for(int arrayIndex = 0; arrayIndex < array.Length; arrayIndex++)
3172 array[arrayIndex] = Color.FromArgb(reader.ReadInt32());
3178 else if(pi.PropertyType == typeof(System.Drawing.Image))
3180 // Get image data size
3181 int imageSize = reader.ReadInt32();
3183 // Create image stream
3184 MemoryStream imageStream = new MemoryStream(imageSize + 10);
3186 // Copy image data into separate stream
3187 imageStream.Write(reader.ReadBytes(imageSize), 0, imageSize);
3189 // Create image object
3190 objValue = new Bitmap(System.Drawing.Image.FromStream(imageStream)); // !!! .Net bug when image source stream is closed - can create brush using the image
3192 // Close image stream
3193 imageStream.Close();
3198 throw(new InvalidOperationException(SR.ExceptionChartSerializerBinaryTypeUnsupported( obj.GetType().ToString() )));
3202 // Check if this property is serializable content
3203 if (!skipElement && IsSerializableContent(pi.Name, obj))
3206 pi.SetValue(obj, objValue, null);
3216 /// Reads property ID and return property information
3218 /// <param name="objectToDeserialize">Object to be deserialized.</param>
3219 /// <param name="parent">Parent of the deserialized object.</param>
3220 /// <param name="properties">List of properties information.</param>
3221 /// <param name="reader">Binary reader.</param>
3222 /// <returns>Property information.</returns>
3223 private PropertyInfo ReadPropertyInfo(object objectToDeserialize, object parent, PropertyInfo[] properties, BinaryReader reader)
3226 short propertyID = this.ReadHashID(reader);
3228 // End objectTag reached
3234 // Loop through all properties and check properties IDs (hash code of name)
3235 foreach(PropertyInfo pi in properties)
3237 // Skip inherited properties from the root object
3238 if(IsChartBaseProperty(objectToDeserialize, parent, pi))
3244 if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null)
3246 if((SerializerBase.GetStringHashCode(pi.Name)) == propertyID)
3252 // Check public properties with Get and Set methods
3253 else if(pi.CanRead && pi.CanWrite)
3256 if(pi.Name == "Item")
3261 if((SerializerBase.GetStringHashCode(pi.Name)) == propertyID)
3268 // Property was not found
3269 throw (new InvalidOperationException(SR.ExceptionChartSerializerPropertyNotFound));