Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web.DataVisualization / Common / Utilities / XmlSerializer.cs
1 //-------------------------------------------------------------
2 // <copyright company=\92Microsoft Corporation\92>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:               XmlSerializer.cs
9 //
10 //  Namespace:  System.Web.UI.WebControls[Windows.Forms].Charting.Utilities
11 //
12 //      Classes:        XmlFormatSerializer, BinaryFormatSerializer
13 //                              SerializerBase, SerializationVisibilityAttribute
14 //
15 //  Purpose:    
16 //  
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. 
22 //  
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.
27 //  
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 
32 //  binary format.
33 //  
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. 
38 //  
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 
46 //  "*.BackColor".
47 //  
48 //      Reviewed:       AG - August 7, 2002
49 //              AG - Microsoft 6, 2007
50 //
51 //===================================================================
52
53
54 #region Used Namespaces
55
56 using System;
57 using System.Xml;
58 using System.Reflection;
59 using System.Collections;
60 using System.Drawing;
61 using System.Drawing.Drawing2D;
62 using System.Drawing.Imaging;
63 using System.ComponentModel;
64 using System.IO;
65 using System.Text;
66 using System.Globalization;
67 using System.Diagnostics.CodeAnalysis;
68 using System.Collections.Specialized;
69 using System.Security;
70
71 #if Microsoft_CONTROL
72     using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
73 #else
74     using System.Web.UI.WebControls;
75     using System.Web.UI.DataVisualization.Charting.ChartTypes;
76 #endif
77
78 #endregion
79
80 #if Microsoft_CONTROL
81         namespace System.Windows.Forms.DataVisualization.Charting.Utilities
82 #else
83         namespace System.Web.UI.DataVisualization.Charting.Utilities
84 #endif
85 {
86         #region Serialization enumerations
87
88         /// <summary>
89         /// Enumeration which describes how to persist property during the serialization
90         /// </summary>
91         internal enum SerializationVisibility
92         {
93                 /// <summary>
94                 /// Do not serialize
95                 /// </summary>
96                 Hidden,
97
98                 /// <summary>
99                 /// Serialize as XML attribute
100                 /// </summary>
101                 Attribute,
102
103                 /// <summary>
104                 /// Serialize as XML element
105                 /// </summary>
106                 Element
107         }
108
109     /// <summary>
110     /// Determines chart current serialization status.
111     /// </summary>
112     internal enum SerializationStatus
113     {
114         /// <summary>
115         /// Chart is not serializing
116         /// </summary>
117         None,
118
119         /// <summary>
120         /// Chart is loading
121         /// </summary>
122         Loading,
123
124         /// <summary>
125         /// Chart is saving
126         /// </summary>
127         Saving,
128
129         /// <summary>
130         /// Chart is resetting
131         /// </summary>
132         Resetting
133     }
134
135         #endregion
136
137         /// <summary>
138         /// Attribute which describes how to persist property during the serialization.
139         /// </summary>
140         [AttributeUsage(AttributeTargets.All)]
141         internal sealed class SerializationVisibilityAttribute : System.Attribute 
142         {
143                 #region Fields
144
145                 // Visibility style
146                 private SerializationVisibility _visibility = SerializationVisibility.Attribute;
147
148                 #endregion
149
150                 #region Constructor
151
152                 /// <summary>
153                 /// Public constructor
154                 /// </summary>
155                 /// <param name="visibility">Serialization visibility.</param>
156                 internal SerializationVisibilityAttribute(SerializationVisibility visibility)
157                 {
158             this._visibility = visibility;
159                 }
160
161                 #endregion
162
163                 #region Properties
164
165                 /// <summary>
166                 /// Serialization visibility property
167                 /// </summary>
168                 public SerializationVisibility Visibility
169                 {
170                         get 
171                         {
172                 return _visibility;
173                         }
174             //set
175             //{
176             //    _visibility = value;
177             //}
178                 }
179
180                 #endregion
181         }
182         
183         /// <summary>
184         /// Base class of the serializers. Common properties and methods for all serializers.
185         /// </summary>
186         internal abstract class SerializerBase
187         {
188                 #region Fields
189
190                 /// <summary>
191                 /// Indicates that unknown properties and elements are ignored
192                 /// </summary>
193                 private bool                                    _isUnknownAttributeIgnored = false;
194
195                 /// <summary>
196                 /// Indicates that serializer works in template creation mode
197                 /// </summary>
198                 private bool                                    _isTemplateMode = false;
199
200                 /// <summary>
201                 /// Indicates that object properties are reset before loading
202                 /// </summary>
203                 private bool                                    _isResetWhenLoading = true;
204
205                 /// <summary>
206                 /// Comma separated list of serializable (Save/Load/Reset) properties. "ClassName.PropertyName"
207                 /// </summary>
208                 private string                                  _serializableContent = "";
209
210                 /// <summary>
211                 /// Comma separated list of NON serializable (Save/Load/Reset) properties. "ClassName.PropertyName"
212                 /// </summary>
213                 private string                                  _nonSerializableContent = "";
214                         
215                 /// <summary>
216                 /// Font converters used while serializing/deserializing 
217                 /// </summary>
218                 internal static FontConverter           fontConverter = new FontConverter();
219
220                 /// <summary>
221                 /// Color converters used while serializing/deserializing 
222                 /// </summary>
223                 internal static ColorConverter          colorConverter = new ColorConverter();
224
225                 /// <summary>
226                 /// Hash code provider.
227                 /// </summary>
228         protected static StringComparer hashCodeProvider = StringComparer.OrdinalIgnoreCase;
229
230         /// <summary>
231         /// Contains chart specific converters
232         /// </summary>
233         HybridDictionary _converterDict = new HybridDictionary();
234
235
236                 #endregion
237
238                 #region Public properties
239
240                 /// <summary>
241                 /// Indicates that unknown properties and elements will be 
242                 /// ignored without throwing an exception.
243                 /// </summary>
244                 internal bool IsUnknownAttributeIgnored
245                 {
246                         get
247                         {
248                 return _isUnknownAttributeIgnored;
249                         }
250                         set
251                         {
252                 _isUnknownAttributeIgnored = value;
253                         }
254                 }
255
256                 /// <summary>
257                 /// Indicates that serializer works in template creation mode
258                 /// </summary>
259         internal bool IsTemplateMode
260                 {
261                         get
262                         {
263                 return _isTemplateMode;
264                         }
265                         set
266                         {
267                 _isTemplateMode = value;
268                         }
269                 }
270
271                 /// <summary>
272                 /// Indicates that object properties are reset to default
273                 /// values before loading.
274                 /// </summary>
275         internal bool IsResetWhenLoading
276                 {
277                         get
278                         {
279                                 return _isResetWhenLoading;
280                         }
281                         set
282                         {
283                                 _isResetWhenLoading = value;
284                         }
285                 }
286
287                 /// <summary>
288                 /// Comma separated list of serializable (Save/Load/Reset) properties. 
289                 /// "ClassName.PropertyName,[ClassName.PropertyName]".
290                 /// </summary>
291         internal string SerializableContent
292                 {
293                         get
294                         {
295                                 return _serializableContent;
296                         }
297                         set
298                         {
299                                 _serializableContent = value;
300
301                                 // Reset list
302                                 serializableContentList = null;
303                         }
304                 }
305
306                 /// <summary>
307                 /// Comma separated list of serializable (Save/Load/Reset) properties. 
308                 /// "ClassName.PropertyName,[ClassName.PropertyName]".
309                 /// </summary>
310         internal string NonSerializableContent
311                 {
312                         get
313                         {
314                                 return _nonSerializableContent;
315                         }
316                         set
317                         {
318                                 _nonSerializableContent = value;
319
320                                 // Reset list
321                                 nonSerializableContentList = null;
322                         }
323                 }
324
325                 #endregion
326
327                 #region Resetting methods
328         
329                 /// <summary>
330                 /// Reset properties of the object to default values.
331                 /// </summary>
332                 /// <param name="objectToReset">Object to be reset.</param>
333                 virtual internal void ResetObjectProperties(object objectToReset)
334                 {
335                         // Reset object properties
336                         ResetObjectProperties(objectToReset, null, GetObjectName(objectToReset));
337                 }
338
339                 /// <summary>
340                 /// Reset properties of the object to default values.
341                 /// Method is called recursively to reset child objects properties.
342                 /// </summary>
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)
347                 {
348                         // Check input parameters
349                         if(objectToReset == null)
350                         {
351                                 return;
352                         }
353
354             IList list = objectToReset as IList;
355
356                         // Check if object is a list
357                         if(list != null && IsSerializableContent(elementName, parent))
358                         {
359                                 // Reset list by clearing all the items
360                                 list.Clear();
361                                 return;
362                         }
363
364                         // Retrive properties list of the object
365                         PropertyInfo[] properties = objectToReset.GetType().GetProperties();
366                         if(properties != null)
367                         {
368                                 // Loop through all properties and reset public properties
369                                 foreach(PropertyInfo pi in properties)
370                                 {
371                                         // Get property descriptor
372                                         PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToReset)[pi.Name];
373
374                                         // Check XmlFormatSerializerStyle attribute
375                                         if(pd != null)
376                                         {
377                                                 SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
378                                                 if(styleAttribute != null)
379                                                 {
380                                                         // Hidden property
381                                                         if(styleAttribute.Visibility == SerializationVisibility.Hidden)
382                                                         {
383                                                                 continue;
384                                                         }
385                                                 }
386                                         }
387
388                                         // Check if this property should be reset
389                                         bool resetProperty = IsSerializableContent(pi.Name, objectToReset);
390
391                                         // Skip inherited properties from the root object
392                                         if(IsChartBaseProperty(objectToReset, parent, pi))
393                                         {
394                                                 continue;
395                                         }
396
397                                         // Reset list
398                                         if(pi.CanRead && pi.PropertyType.GetInterface("IList", true) != null)
399                                         {
400                                                 if(resetProperty)
401                                                 {
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);
405                                                         if(mi != null)
406                                                         {
407                                                                 mi.Invoke(objectToReset, null);
408                                                                 resetComplete = true;
409                                                         }
410
411                                                         // Reset list by clearing all the items
412                                                         if(!resetComplete)
413                                                         {
414                                                                 ((IList)pi.GetValue(objectToReset, null)).Clear();
415                                                         }
416                                                 }
417                                                 else
418                                                 {
419                                                         // Reset objects of the list
420                                                         foreach(object listObject in ((IList)pi.GetValue(objectToReset, null)))
421                                                         {
422                                                                 ResetObjectProperties(listObject, objectToReset, this.GetObjectName(listObject));
423                                                         }
424                                                 }
425                                         }
426
427                                                 // Reset public properties with Get and Set methods
428                                         else if(pi.CanRead && pi.CanWrite)
429                                         {
430                                                 // Skip indexes
431                                                 if(pi.Name == "Item")
432                                                 {
433                                                         continue;
434                                                 }
435
436                         // Skip Names
437                         if (pi.Name == "Name")
438                         {
439                             continue;
440                         }
441
442                                                 // Reset inner properies
443                                                 if(ShouldSerializeAsAttribute(pi, objectToReset))
444                                                 {
445                                                         if(resetProperty)
446                                                         {
447                                                                 // Reset the property using property descriptor
448                                                                 
449                                                                 if(pd != null)
450                                                                 {
451                                                                         // Get property object
452                                                                         object objectProperty = pi.GetValue(objectToReset, null);
453
454                                                                         // Get default value of the property
455                                                                         DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
456                                                                         if(defValueAttribute != null)
457                                                                         {
458                                                                                 if(objectProperty == null)
459                                                                                 {
460                                                                                         if(defValueAttribute.Value != null)
461                                                                                         {
462                                                                                                 pd.SetValue(objectToReset, defValueAttribute.Value);
463                                                                                         }
464                                                                                 }
465                                                                                 else if(! objectProperty.Equals(defValueAttribute.Value))
466                                                                                 {
467                                                                                         pd.SetValue(objectToReset, defValueAttribute.Value);
468                                                                                 }
469                                                                         }
470                                                                         else
471                                                                         {
472                                                                                 // Check if property has "Reset" method
473                                         MethodInfo mi = objectToReset.GetType().GetMethod("Reset" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
474                                                                                 if(mi != null)
475                                                                                 {
476                                                                                         mi.Invoke(objectToReset, null);
477                                                                                 }
478                                                                         }
479                                                                 }
480                                                         }
481                                                 }
482                                                 else
483                                                 {
484                                                         // Reset inner object
485                                                         ResetObjectProperties(pi.GetValue(objectToReset, null), objectToReset, pi.Name);
486                                                 }
487                                         }
488                                 }
489                         }
490                         return;
491                 }
492
493
494                 #endregion
495
496                 #region Abstract Serialization/Deserialization methods
497
498                 /// <summary>
499                 /// Serialize specified object into the destination object.
500                 /// </summary>
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);
504                 
505                 /// <summary>
506                 /// Deserialize specified object from the source object.
507                 /// </summary>
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);
511
512                 #endregion
513
514                 #region Protected helper methods
515
516                 /// <summary>
517                 /// Converts specified font object into a string.
518                 /// </summary>
519                 /// <param name="font">Font object to convert.</param>
520                 /// <returns>String that contains font data.</returns>
521                 internal static string FontToString(Font font)
522                 {
523                         // Save basic properties persisted by font converter
524                         string fontData = (string)SerializerBase.fontConverter.ConvertToInvariantString(font);
525
526                         // Persist properties not serialiazed by the converter
527                         if(font.GdiCharSet != 1)
528                         {
529                                 fontData += ", GdiCharSet=" + font.GdiCharSet.ToString(System.Globalization.CultureInfo.InvariantCulture);
530                         }
531                         if(font.GdiVerticalFont)
532                         {
533                                 fontData += ", GdiVerticalFont";
534                         }
535
536                         return fontData;
537                 }
538
539                 /// <summary>
540                 /// Converts string data into a font object.
541                 /// </summary>
542                 /// <param name="fontString">String with font data.</param>
543                 /// <returns>Newly created font object.</returns>
544                 internal static Font FontFromString(string fontString)
545                 {
546                         // Check if string contains non-standard values "GdiCharSet" or "GdiVerticalFont"
547                         string standardData = fontString;
548                         byte gdiCharSet = 1;
549                         bool gdiVerticalFont = false;
550                         int charIndex = fontString.IndexOf(", GdiCharSet=", StringComparison.Ordinal);
551                         if(charIndex >= 0)
552                         {
553                                 // Read value
554                                 string val = fontString.Substring(charIndex + 13);
555                 int commaIndex = val.IndexOf(",", StringComparison.Ordinal);
556                                 if(commaIndex >= 0)
557                                 {
558                                         val = val.Substring(0, commaIndex);
559                                 }
560
561                                 gdiCharSet = (byte)Int32.Parse(val, System.Globalization.CultureInfo.InvariantCulture);
562
563                                 // Truncate standard data string
564                                 if(standardData.Length > charIndex)
565                                 {
566                                         standardData = standardData.Substring(0, charIndex);
567                                 }
568                         }
569             charIndex = fontString.IndexOf(", GdiVerticalFont", StringComparison.Ordinal);
570                         if(charIndex >= 0)
571                         {
572                                 gdiVerticalFont = true;
573
574                                 // Truncate standard data string
575                                 if(standardData.Length > charIndex)
576                                 {
577                                         standardData = standardData.Substring(0, charIndex);
578                                 }
579                         }
580
581                         // Create Font object from standard parameters
582                         Font font = (Font)SerializerBase.fontConverter.ConvertFromInvariantString(standardData);
583
584                         // check if non-standard parameters provided
585                         if(gdiVerticalFont || gdiCharSet != 1)
586                         {
587                                 Font newFont = new Font(
588                                         font.Name,
589                                         font.SizeInPoints,
590                                         font.Style,
591                                         GraphicsUnit.Point,
592                                         gdiCharSet,
593                                         gdiVerticalFont);
594
595                                 font.Dispose();
596
597                                 return newFont;
598                         }
599
600                         return font;
601                 }
602
603                 /// <summary>
604                 /// Returns a hash code of a specified string.
605                 /// </summary>
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)
609                 {
610                         return (short)(hashCodeProvider.GetHashCode(str) + str.Length * 2);
611                 }
612
613                 /// <summary>
614                 /// Reads hash ID from the specified binary reader.
615                 /// </summary>
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)
619                 {
620                         // For later versions return ID without transformations
621                         return reader.ReadInt16();
622                 }
623
624                 /// <summary>
625                 /// Checks if property belongs to the base class of the chart "Control".
626                 /// </summary>
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)
632                 {
633                         bool    result = false;
634
635                         // Check only for the root object
636                         if(parent == null)
637                         {
638                                 Type    currentType = objectToSerialize.GetType();
639                                 while(currentType != null)
640                                 {
641                                         if(pi.DeclaringType == currentType)
642                                         {
643                                                 result = false;
644                                                 break;
645                                         }
646
647                                         // Check if it's a chart class
648                                         if( currentType == typeof(Chart))
649                                         {
650                                                 result = true;
651                                                 break;
652                                         }
653                                 
654                                         // Get base class type
655                                         currentType = currentType.BaseType;
656                                 }
657                         }
658
659                         return result;
660                 }
661
662
663                 /// <summary>
664                 /// Converts Image object into the BASE64 encoded string
665                 /// </summary>
666                 /// <param name="image">Image to convert.</param>
667                 /// <returns>BASE64 encoded image data.</returns>
668                 internal static string ImageToString(System.Drawing.Image image)
669                 {
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);
674
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);
680
681                         // Close image stream
682                         textWriter.Close();
683                         imageStream.Close();
684
685                         return stringBuilder.ToString();
686                 }
687
688                 /// <summary>
689                 /// Converts BASE64 encoded string to image.
690                 /// </summary>
691                 /// <param name="data">BASE64 encoded data.</param>
692                 /// <returns>Image.</returns>
693         internal static System.Drawing.Image ImageFromString(string data)
694                 {
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>"));
699
700                         // Read tags and BASE64 encoded data
701                         textReader.Read();
702                         int bytesRead = 0;
703                         while((bytesRead = textReader.ReadBase64(buffer, 0, 1000)) > 0)
704                         {
705                                 imageStream.Write(buffer, 0, bytesRead);
706                         }
707                         textReader.Read();
708
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
714
715                         // Close image stream
716                         textReader.Close();
717                         imageStream.Close();
718
719                         return image;
720                 }
721
722                 /// <summary>
723                 /// Get the name of the object class
724                 /// </summary>
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)
728                 {
729             string name = obj.GetType().ToString();
730                         return name.Substring(name.LastIndexOf('.') + 1);
731                 }
732
733         /// <summary>
734         /// Create new empty item for the list.
735         /// AxisName of the objects is determined by the return type of the indexer.
736         /// </summary>
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)
743                 {
744                         // Get type of item in collection
745                         Type itemType = null;
746                         if(itemTypeName.Length > 0)
747                         {
748                 itemType = Type.GetType(typeof(Chart).Namespace + "." + itemTypeName, false, true);
749                         }
750
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;
755                         if(pi != null)
756                         {
757                                 // Try to get object by name using the indexer
758                                 if(itemName != null && itemName.Length > 0)
759                                 {
760                     bool itemChecked = false;
761                     if (mi != null)
762                     {
763                         try
764                         {
765                             int index = -1;
766                             object oindex = mi.Invoke(list, new object[] { itemName });
767                             if (oindex is int)
768                             {
769                                 index = (int)oindex;
770                                 itemChecked = true;
771                             }
772                             if (index != -1)
773                             {
774                                 object objByName = list[index];
775                                 if (objByName != null)
776                                 {
777                                     // Remove found object from the list
778                                     list.Remove(objByName);
779
780                                     // Return found object
781                                     reusedObject = true;
782                                     return objByName;
783                                 }
784                             }
785                         }
786                         catch (ArgumentException)
787                         {
788                         }
789                         catch (TargetException)
790                         {
791                         }
792                         catch (TargetInvocationException)
793                         {
794                         }
795                     }
796                     if (!itemChecked)
797                     {
798                         object objByName = null;
799                         try
800                         {
801                             objByName = pi.GetValue(list, new object[] { itemName });
802                         }
803                         catch (ArgumentException)
804                         {
805                             objByName = null;
806                         }
807                         catch (TargetException)
808                         {
809                             objByName = null;
810                         }
811                         catch (TargetInvocationException)
812                         {
813                             objByName = null;
814                         }
815
816                         if (objByName != null)
817                         {
818                             try
819                             {
820                                 // Remove found object from the list
821                                 list.Remove(objByName);
822                             }
823                             catch (NotSupportedException)
824                             {
825                             }
826
827                             // Return found object
828                             reusedObject = true;
829                             return objByName;
830                         }
831                     }
832                                         itemName = null;
833                                 }
834
835                         }
836             // Get the constructor of the type returned by indexer
837             if (itemType != null)
838             {
839                 ci = itemType.GetConstructor(Type.EmptyTypes);
840             }
841             else
842             {
843                 ci = pi.PropertyType.GetConstructor(Type.EmptyTypes);
844             }
845             if (ci == null)
846             {
847                 throw (new InvalidOperationException(SR.ExceptionChartSerializerDefaultConstructorUndefined(pi.PropertyType.ToString())));
848             }
849             return ci.Invoke(null);
850                 }
851
852         /// <summary>
853         /// Returns true if the object property should be serialized as 
854         /// parent element attribute. Otherwise as a child element.
855         /// </summary>
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)
860                 {
861                         // Check if SerializationVisibilityAttribute is set
862                         if(parent != null)
863                         {
864                                 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[pi.Name];
865                                 if(pd != null)
866                                 {
867                                         SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
868                                         if(styleAttribute != null)
869                                         {
870                                                 if(styleAttribute.Visibility == SerializationVisibility.Attribute)
871                                                 {
872                                                         return true;
873                                                 }
874                                                 else if(styleAttribute.Visibility == SerializationVisibility.Element)
875                                                 {
876                                                         return false;
877                                                 }
878                                         }
879                                 }
880                         }
881
882                         // If a simple type - serialize as property
883                         if(!pi.PropertyType.IsClass)
884                         {
885                                 return true;
886                         }
887
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))
893                         {
894                                 return true;
895                         }
896
897                         return false;
898         }
899
900
901         /// <summary>
902         /// Determines if this property should be serialized as attribute
903         /// </summary>
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)
908         {
909             if (objectToSerialize != null)
910             {
911                 PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
912                 if (pd != null)
913                 {
914                     SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
915                     if (styleAttribute != null)
916                     {
917                         if (styleAttribute.Visibility == SerializationVisibility.Attribute)
918                         {
919                             return true;
920                         }
921                     }
922                 }
923             }
924             return false;
925         }
926
927         /// <summary>
928                 /// Returns true if the object property is serializable.
929                 /// </summary>
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)
934         {
935                         bool    serializable = true;
936                         if(_serializableContent.Length > 0 || _nonSerializableContent.Length > 0)
937                         {
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);
941
942                                 // Check if property in this class is part of the serializable content
943                                 serializable = IsPropertyInList(GetSerializableContentList(), ownerClassName, propertyName, out serialzableClassFitType, out serialzablePropertyFitType);
944
945                                 // Check if property in this class is part of the NON serializable content
946                                 if(serializable)
947                                 {
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);
951
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
954                                         if(nonSerializable)
955                                         {
956                                                 // Check priority
957                                                 if((nonSerialzableClassFitType + nonSerialzablePropertyFitType) > 
958                                                         (serialzableClassFitType + serialzablePropertyFitType))
959                                                 {
960                                                         serializable = false;
961                                                 }
962                                         }
963                                 }
964                         }
965
966                         return serializable;
967                 }
968
969         /// <summary>
970         /// Checks if property belongs is defined in the mask list.
971         /// </summary>
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)
979                 {
980                         // Initialize result values
981                         classFitType = 0;
982                         propertyFitType = 0;
983
984                         if(contentList != null)
985                         {
986                                 // Loop through all items in the list using step 2
987                                 for(int itemIndex = 0; itemIndex < contentList.Count; itemIndex += 2)
988                                 {
989                                         // Initialize result values
990                                         classFitType = 0;
991                                         propertyFitType = 0;
992
993                                         // Check if object class and property name match the mask
994                                         if(NameMatchMask((ItemInfo)contentList[itemIndex], className, out classFitType))
995                                         {
996                                                 if(NameMatchMask((ItemInfo)contentList[itemIndex + 1], propertyName, out propertyFitType))
997                                                 {
998                                                         return true;
999                                                 }
1000                                         }
1001                                 }
1002                         }
1003
1004                         return false;
1005                 }
1006
1007         /// <summary>
1008         /// Compares class/property name with the specified mask
1009         /// </summary>
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)
1015                 {
1016                         // Initialize type
1017                         type = 0;
1018
1019                         // Any class mask
1020                         if(itemInfo.any)
1021                         {
1022                                 type = 1;
1023                                 return true;
1024                         }
1025
1026                         // Ends with class mask
1027                         if(itemInfo.endsWith)
1028                         {
1029                                 if(itemInfo.name.Length <= objectName.Length)
1030                                 {
1031                                         if(objectName.Substring(0, itemInfo.name.Length) == itemInfo.name)
1032                                         {
1033                                                 type = 2;
1034                                                 return true;
1035                                         }
1036                                 }
1037                         }
1038
1039                         // Starts with class mask
1040                         if(itemInfo.startsWith)
1041                         {
1042                                 if(itemInfo.name.Length <= objectName.Length)
1043                                 {
1044                                         if(objectName.Substring(objectName.Length - itemInfo.name.Length, itemInfo.name.Length) == itemInfo.name)
1045                                         {
1046                                                 type = 2;
1047                                                 return true;
1048                                         }
1049                                 }
1050                         }
1051
1052                         // Exact name is specified
1053                         if(itemInfo.name == objectName)
1054                         {
1055                                 type = 3;
1056                                 return true;
1057                         }
1058
1059                         return false;
1060                 }
1061
1062
1063         /// <summary>
1064         /// Finds a converter by property descriptor.
1065         /// </summary>
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)
1069         {
1070             TypeConverter result;
1071             TypeConverterAttribute typeConverterAttrib = (TypeConverterAttribute)pd.Attributes[typeof(TypeConverterAttribute)];
1072             if (typeConverterAttrib != null && typeConverterAttrib.ConverterTypeName.Length > 0)
1073             {
1074                 result = this.FindConverterByType(typeConverterAttrib);
1075                 if (result != null)
1076                 {
1077                     return result;
1078                 }
1079                 try
1080                 {
1081                     return pd.Converter;
1082                 }
1083                 catch (SecurityException)
1084                 {
1085                 }
1086                 catch (MethodAccessException)
1087                 {
1088                 }
1089             }
1090             return TypeDescriptor.GetConverter(pd.PropertyType);
1091         }
1092
1093         /// <summary>
1094         /// Finds a converter by TypeConverterAttribute.
1095         /// </summary>
1096         /// <param name="attr">TypeConverterAttribute.</param>
1097         /// <returns>TypeConvetrer or null</returns>
1098         internal TypeConverter FindConverterByType( TypeConverterAttribute attr)
1099         {
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))
1104             {
1105                 return (TypeConverter)_converterDict[attr.ConverterTypeName];
1106             }
1107             String typeStr = attr.ConverterTypeName;
1108             
1109             if (attr.ConverterTypeName.Contains(",") )
1110             {
1111                 typeStr = attr.ConverterTypeName.Split(',')[0];
1112             }
1113
1114             TypeConverter result = null;
1115
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(); }
1149
1150             if (result != null) _converterDict[attr.ConverterTypeName] = result;
1151             
1152             return result;
1153         }
1154
1155                 #endregion
1156
1157                 #region Serializable content list managment fields, methods and classes
1158
1159                 /// <summary>
1160                 /// Stores information about content item (class or property)
1161                 /// </summary>
1162                 private class ItemInfo
1163                 {
1164                         public  string          name = "";
1165                         public  bool            any = false;
1166                         public  bool            startsWith = false;
1167                         public  bool            endsWith = false;
1168                 }
1169
1170                 // Storage for serializable content items
1171                 private ArrayList               serializableContentList = null;
1172
1173                 // Storage for non serializable content items
1174                 private ArrayList               nonSerializableContentList = null;
1175
1176                 /// <summary>
1177                 /// Return serializable content list.
1178                 /// </summary>
1179                 /// <returns>Serializable content list.</returns>
1180                 private ArrayList GetSerializableContentList()
1181                 {
1182                         if(serializableContentList == null)
1183                         {
1184                                 serializableContentList = new ArrayList();
1185                                 FillContentList(
1186                                         serializableContentList, 
1187                                         (this.SerializableContent.Length > 0 ) ? this.SerializableContent : "*.*");
1188                         }
1189
1190                         return serializableContentList;
1191                 }
1192
1193                 /// <summary>
1194                 /// Return non serializable content list.
1195                 /// </summary>
1196                 /// <returns>Non serializable content list.</returns>
1197                 private ArrayList GetNonSerializableContentList()
1198                 {
1199                         if(nonSerializableContentList == null)
1200                         {
1201                                 nonSerializableContentList = new ArrayList();
1202                                 FillContentList(nonSerializableContentList, this.NonSerializableContent);
1203                         }
1204
1205                         return nonSerializableContentList;
1206                 }
1207
1208                 /// <summary>
1209                 /// Fill content list from the string.
1210                 /// </summary>
1211                 /// <param name="list">Array list class.</param>
1212                 /// <param name="content">Content string.</param>
1213                 private void FillContentList(ArrayList list, string content)
1214                 {
1215                         if(content.Length > 0)
1216                         {
1217                                 string[]        classPropertyPairs = content.Split(',');
1218                                 foreach(string item in classPropertyPairs)
1219                                 {
1220                                         // Create two content items: one for the class and one for the property
1221                                         ItemInfo        classInfo = new ItemInfo();
1222                                         ItemInfo        propertyInfo = new ItemInfo();
1223
1224                                         // Find class and property name
1225                                         int pointIndex = item.IndexOf('.');
1226                                         if(pointIndex == -1)
1227                                         {
1228                         throw (new ArgumentException(SR.ExceptionChartSerializerContentStringFormatInvalid));
1229                                         }
1230                                         classInfo.name = item.Substring(0, pointIndex).Trim();
1231                                         propertyInfo.name = item.Substring(pointIndex + 1).Trim();
1232                                         if(classInfo.name.Length == 0)
1233                                         {
1234                         throw (new ArgumentException(SR.ExceptionChartSerializerClassNameUndefined));
1235                                         }
1236                                         if(propertyInfo.name.Length == 0)
1237                                         {
1238                         throw (new ArgumentException(SR.ExceptionChartSerializerPropertyNameUndefined));
1239                                         }
1240
1241                                         // Make sure property name do not have point character
1242                                         if(propertyInfo.name.IndexOf('.') != -1)
1243                                         {
1244                         throw (new ArgumentException(SR.ExceptionChartSerializerContentStringFormatInvalid));
1245                                         }
1246
1247                                         // Check for wildcards in names
1248                                         CheckWildCars(classInfo);
1249                                         CheckWildCars(propertyInfo);
1250
1251                                         // Add class & property items into the array
1252                                         list.Add(classInfo);
1253                                         list.Add(propertyInfo);
1254                                 }
1255                         }
1256                 }
1257
1258                 /// <summary>
1259                 /// Checks wildcards in the name of the item.
1260                 /// Possible values:
1261                 ///             "*"
1262                 ///             "*Name"
1263                 ///             "Name*"
1264                 /// </summary>
1265                 /// <param name="info">Item information class.</param>
1266                 private void CheckWildCars(ItemInfo info)
1267                 {
1268                         // Any class mask
1269                         if(info.name == "*")
1270                         {
1271                                 info.any = true;
1272                         }
1273
1274                         // Ends with class mask
1275                         else if(info.name[info.name.Length - 1] == '*')
1276                         {
1277                                 info.endsWith = true;
1278                                 info.name = info.name.TrimEnd('*');
1279                         }
1280
1281                         // Starts with class mask
1282                         else if(info.name[0] == '*')
1283                         {
1284                                 info.startsWith = true;
1285                                 info.name = info.name.TrimStart('*');
1286                         }
1287                 }
1288
1289                 #endregion
1290     }
1291
1292         /// <summary>
1293         /// Utility class which serialize object using XML format
1294         /// </summary>
1295         internal class XmlFormatSerializer : SerializerBase
1296         {
1297                 #region Serialization public methods
1298
1299                 /// <summary>
1300                 /// Serialize specified object into the stream.
1301                 /// </summary>
1302                 /// <param name="objectToSerialize">Object to be serialized.</param>
1303                 /// <param name="stream">The stream used to write the XML document.</param>
1304
1305         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1306         internal void Serialize(object objectToSerialize, Stream stream)
1307                 {
1308                         Serialize(objectToSerialize, (object)stream);
1309                 }
1310
1311                 /// <summary>
1312                 /// Serialize specified object into the XML writer.
1313                 /// </summary>
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)
1318                 {
1319                         Serialize(objectToSerialize, (object)xmlWriter);
1320                 }
1321
1322                 /// <summary>
1323                 /// Serialize specified object into the text writer.
1324                 /// </summary>
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)
1329                 {
1330                         Serialize(objectToSerialize, (object)textWriter);
1331                 }
1332
1333                 /// <summary>
1334                 /// Serialize specified object into the file.
1335                 /// </summary>
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)
1340                 {
1341                         Serialize(objectToSerialize, (object)fileName);
1342                 }
1343
1344                 #endregion
1345         
1346                 #region Serialization private methods
1347
1348                 /// <summary>
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
1353                 /// </summary>
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>
1356                 
1357         internal override void Serialize(object objectToSerialize, object writer)
1358                 {
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;
1364
1365                         // Check input parameters
1366                         if(objectToSerialize == null)
1367                         {
1368                                 throw(new ArgumentNullException("objectToSerialize"));
1369                         }
1370                         if(writer == null)
1371                         {
1372                                 throw(new ArgumentNullException("writer"));
1373                         }
1374                         if(stream == null && textWriter == null && xmlWriter == null && writerStr == null)
1375                         {
1376                 throw (new ArgumentException(SR.ExceptionChartSerializerWriterObjectInvalid, "writer"));
1377                         }
1378
1379                         // Create XML document
1380                         XmlDocument xmlDocument = new XmlDocument();
1381                 
1382                         // Create document fragment
1383                         XmlDocumentFragment docFragment = xmlDocument.CreateDocumentFragment();
1384
1385
1386
1387                         // Serialize object
1388                         SerializeObject(objectToSerialize, null, GetObjectName(objectToSerialize), docFragment, xmlDocument);
1389
1390
1391                         // Append document fragment
1392                         xmlDocument.AppendChild(docFragment);
1393
1394                         // Remove empty child nodes
1395                         RemoveEmptyChildNodes(xmlDocument);
1396
1397                         // Save XML document into the writer
1398                         if(stream != null)
1399                         {
1400                                 xmlDocument.Save(stream);
1401
1402                                 // Flush stream and seek to the beginning
1403                 stream.Flush();
1404                 stream.Seek(0, SeekOrigin.Begin);
1405                         }
1406
1407                         if(writerStr != null)
1408                         {
1409                                 xmlDocument.Save(writerStr);
1410                         }
1411
1412                         if(xmlWriter != null)
1413                         {
1414                 xmlDocument.Save(xmlWriter);
1415                         }
1416
1417                         if(textWriter != null)
1418                         {
1419                 xmlDocument.Save(textWriter);
1420                         }
1421                 }
1422         /// <summary>
1423                 /// Serialize specified object into the XML format.
1424                 /// Method is called recursively to serialize child objects.
1425                 /// </summary>
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)
1432         {
1433                         // Check input parameters
1434                         if(objectToSerialize == null)
1435                         {
1436                                 return;
1437                         }
1438
1439                         // Check if object should be serialized
1440                         if(parent != null)
1441                         {
1442                                 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
1443                                 if(pd != null)
1444                                 {
1445                                         SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
1446                                         if(styleAttribute != null)
1447                                         {
1448                                                 // Hidden property
1449                                                 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
1450                                                 {
1451                                                         return;
1452                                                 }
1453                                         }
1454                                 }
1455                         }
1456
1457                         // Check if object is a collection
1458                         if(objectToSerialize is ICollection)
1459                         {
1460                                 // Serialize collection
1461                                 SerializeCollection(objectToSerialize, elementName, xmlParentNode, xmlDocument);
1462                                 return;
1463                         }
1464
1465                         // Create object element inside the parents node
1466                         XmlNode xmlNode = xmlDocument.CreateElement(elementName);
1467                         xmlParentNode.AppendChild(xmlNode);
1468
1469                         // Write template data into collection items
1470                         bool templateListItem = false;
1471             IList parentList = parent as IList;
1472                         if(this.IsTemplateMode && parentList != null)
1473                         {
1474                                 // Create "_Template_" attribute
1475                                 XmlAttribute attrib = xmlDocument.CreateAttribute("_Template_");
1476
1477                                 // Check number of items in collection
1478                 if (parentList.Count == 1)
1479                                 {
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";
1484                                 }
1485                                 else
1486                                 {
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);
1492                                 }
1493
1494                                 // Add "_Template_" attribute into the XML node
1495                                 xmlNode.Attributes.Append(attrib);
1496                                 templateListItem = true;
1497                         }
1498
1499             // Retrive properties list of the object
1500             PropertyInfo[] properties = objectToSerialize.GetType().GetProperties();
1501             if (properties != null)
1502             {
1503                                 // Loop through all properties and serialize public properties
1504                                 foreach(PropertyInfo pi in properties)
1505                                 {
1506
1507                                         // Skip "Name" property from collection items in template mode
1508                                         if(templateListItem && pi.Name == "Name")
1509                                         {
1510                                                 continue;
1511                                         }
1512
1513                                         // Skip inherited properties from the root object
1514                                         if(IsChartBaseProperty(objectToSerialize, parent, pi))
1515                                         {
1516                                                 continue;
1517                                         }
1518
1519                     // Check if this property is serializable content
1520                     if (!IsSerializableContent(pi.Name, objectToSerialize))
1521                     {
1522                         continue;
1523                     }
1524  
1525                     // Serialize collection
1526
1527                     if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null && !this.SerializeICollAsAtribute(pi, objectToSerialize))
1528                     {
1529                         // Check if SerializationVisibilityAttribute is set
1530                                                 bool    serialize = true;
1531                                                 if(objectToSerialize != null)
1532                                                 {
1533                                                         PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
1534                                                         if(pd != null)
1535                                                         {
1536                                                                 SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
1537                                                                 if(styleAttribute != null)
1538                                                                 {
1539                                                                         if(styleAttribute.Visibility == SerializationVisibility.Hidden)
1540                                                                         {
1541                                                                                 serialize = false;
1542                                                                         }
1543                                                                 }
1544                                                         }
1545                                                 }
1546                                                 // Check if collection has "ShouldSerialize" method
1547                                                 MethodInfo mi = objectToSerialize.GetType().GetMethod("ShouldSerialize" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
1548                                                 if(mi != null)
1549                                                 {
1550                                                         object result = mi.Invoke(objectToSerialize, null);
1551                                                         if(result is bool && ((bool)result) == false)
1552                                                         {
1553                                                                 // Do not serialize collection
1554                                                                 serialize = false;
1555                                                         }
1556                                                 }
1557
1558                                                 // Serialize collection
1559                                                 if(serialize)
1560                                                 {
1561                                                         SerializeCollection(pi.GetValue(objectToSerialize, null), pi.Name, xmlNode, xmlDocument);
1562                                                 }
1563                                         }
1564
1565                                         // Serialize public properties with Get and Set methods
1566                                         else if(pi.CanRead && pi.CanWrite)
1567                                         {
1568                                                 // Skip indexes
1569                                                 if(pi.Name == "Item")
1570                                                 {
1571                                                         continue;
1572                                                 }
1573
1574                                                 // Check if an object should be serialized as a property or as a class
1575                                                 if(ShouldSerializeAsAttribute(pi, objectToSerialize))
1576                                                 {
1577                                                         // Serialize property
1578                                                         SerializeProperty(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, xmlNode, xmlDocument);
1579                                                 }
1580                                                 else
1581                                                 {
1582                                                         // Serialize inner object
1583                                                         SerializeObject(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, xmlNode, xmlDocument);
1584                                                 }
1585                                         }
1586                                 }
1587                         }
1588                         return;
1589                 }
1590
1591
1592         /// <summary>
1593         /// Serializes the data point.
1594         /// </summary>
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)
1599         {
1600             // Create object element inside the parents node
1601             XmlNode xmlNode = xmlDocument.CreateElement(GetObjectName(objectToSerialize));
1602             xmlParentNode.AppendChild(xmlNode);
1603             
1604             DataPoint dataPoint = objectToSerialize as DataPoint;
1605             if (dataPoint.XValue != 0d && IsSerializableContent("XValue", objectToSerialize))
1606             {
1607                 XmlAttribute attrib = xmlDocument.CreateAttribute("XValue");
1608                 attrib.Value = GetXmlValue(dataPoint.XValue, dataPoint, "XValue");
1609                 xmlNode.Attributes.Append(attrib);
1610             }
1611             if (dataPoint.YValues.Length > 0 && IsSerializableContent("YValues", objectToSerialize))
1612             {
1613                 XmlAttribute attrib = xmlDocument.CreateAttribute("YValues");
1614                 attrib.Value = GetXmlValue(dataPoint.YValues, dataPoint, "YValues");
1615                 xmlNode.Attributes.Append(attrib);
1616             }
1617             if (dataPoint.IsEmpty && IsSerializableContent("IsEmpty", objectToSerialize))
1618             {
1619                 XmlAttribute attrib = xmlDocument.CreateAttribute("IsEmpty");
1620                 attrib.Value = GetXmlValue(dataPoint.isEmptyPoint, dataPoint, "IsEmpty");
1621                 xmlNode.Attributes.Append(attrib);
1622             }
1623             bool hasCustomProperties = false;
1624             foreach (DictionaryEntry entry in dataPoint.properties)
1625             {
1626                 if (entry.Key is int)
1627                 {
1628                     CommonCustomProperties propertyType = (CommonCustomProperties)((int)entry.Key);
1629                     String properyName = propertyType.ToString();
1630                     if (IsSerializableContent(properyName, objectToSerialize))
1631                     {
1632                         XmlAttribute attrib = xmlDocument.CreateAttribute(properyName);
1633                         attrib.Value = GetXmlValue(entry.Value, dataPoint, properyName);
1634                         xmlNode.Attributes.Append(attrib);
1635                     }
1636                 }
1637                 else
1638                 {
1639                     hasCustomProperties = true;
1640                 }
1641             }
1642
1643             if (hasCustomProperties && !String.IsNullOrEmpty(dataPoint.CustomProperties) && IsSerializableContent("CustomProperties", objectToSerialize))
1644             {
1645                 XmlAttribute attrib = xmlDocument.CreateAttribute("CustomProperties");
1646                 attrib.Value = GetXmlValue(dataPoint.CustomProperties, dataPoint, "CustomProperties");
1647                 xmlNode.Attributes.Append(attrib);
1648             }            
1649
1650         }
1651
1652                 /// <summary>
1653                 /// Serialize specified object into the XML text writer.
1654                 /// Method is called recursively to serialize child objects.
1655                 /// </summary>
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)
1661                 {
1662             ICollection collection = objectToSerialize as ICollection;
1663                         if(collection != null)
1664                         {
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)
1670                                 {
1671
1672                     if (obj is DataPoint)
1673                     {
1674                         SerializeDataPoint(obj, xmlNode, xmlDocument);
1675                         continue;
1676                     }
1677
1678                     SerializeObject(obj, objectToSerialize, GetObjectName(obj), xmlNode, xmlDocument);
1679                                 }
1680                         }
1681                 }
1682
1683                 /// <summary>
1684                 /// Serialize specified object into the XML text writer.
1685                 /// Method is called recursively to serialize child objects.
1686                 /// </summary>
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)
1693                 {
1694                         // Check input parameters
1695                         if(objectToSerialize == null || parent == null)
1696                         {
1697                                 return;
1698                         }
1699
1700                         // Check if property has non-default value
1701                         PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
1702                         if(pd != null)
1703                         {
1704                                 DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
1705                                 if(defValueAttribute != null)
1706                                 {
1707                                         if(objectToSerialize.Equals(defValueAttribute.Value))
1708                                         {
1709                                                 // Do not serialize properties with default values
1710                                                 return;
1711                                         }
1712                                 }
1713                                 else
1714                                 {
1715                                         // Check if property has "ShouldSerialize" method
1716                     MethodInfo mi = parent.GetType().GetMethod("ShouldSerialize" + elementName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
1717                                         if(mi != null)
1718                                         {
1719                                                 object result = mi.Invoke(parent, null);
1720                                                 if(result is bool && ((bool)result) == false)
1721                                                 {
1722                                                         // Do not serialize properties with default values
1723                                                         return;
1724                                                 }
1725                                         }
1726                                 }
1727                         
1728                                 // Check XmlFormatSerializerStyle attribute
1729                                 SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
1730                                 if(styleAttribute != null)
1731                                 {
1732                                         // Hidden property
1733                                         if(styleAttribute.Visibility == SerializationVisibility.Hidden)
1734                                         {
1735                                                 return;
1736                                         }
1737                                 }
1738                         }
1739
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);
1744                 }
1745
1746                 /// <summary>
1747                 /// Converts object value into the string.
1748                 /// </summary>
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)
1754                 {
1755             string objStr = obj as string;
1756                         if(objStr != null)
1757                         {
1758                 return objStr;
1759                         }
1760
1761             Font font = obj as Font;
1762             if(font != null)
1763                         {
1764                                 return SerializerBase.FontToString(font);
1765                         }
1766
1767                         if(obj is Color)
1768                         {
1769                                 return colorConverter.ConvertToString(null, System.Globalization.CultureInfo.InvariantCulture, obj);
1770                         }
1771
1772             Color[] colors = obj as Color[];
1773                         if(colors != null)
1774                         {
1775                                 return ColorArrayConverter.ColorArrayToString(colors);
1776                         }
1777
1778 #if !Microsoft_CONTROL
1779             if(obj is Unit)
1780                         {
1781                 Unit unit = (Unit)obj;
1782                                 return unit.Value.ToString(System.Globalization.CultureInfo.InvariantCulture);
1783                         }
1784 #endif
1785
1786             System.Drawing.Image image = obj as System.Drawing.Image;
1787                         if(image != null)
1788                         {
1789                                 return ImageToString(image);
1790                         }
1791
1792                         // Look for the converter set with the attibute
1793                         PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
1794                         if(pd != null)
1795                         {
1796                 TypeConverter converter = this.FindConverter(pd);
1797                 if (converter != null && converter.CanConvertTo(typeof(string)))
1798                                 {
1799                     return converter.ConvertToString(null, System.Globalization.CultureInfo.InvariantCulture, obj);
1800                                 }
1801                         }
1802                         
1803                         // Try using default string convertion
1804                         return obj.ToString();
1805                 }
1806
1807                 /// <summary>
1808                 /// Removes all empty nodes from the XML document.
1809                 /// Method is called recursively to remove empty child nodes first.
1810                 /// </summary>
1811                 /// <param name="xmlNode">The node where to start the removing.</param>
1812                 private void RemoveEmptyChildNodes(XmlNode xmlNode)
1813                 {
1814                         // Loop through all child nodes
1815                         for(int nodeIndex = 0; nodeIndex < xmlNode.ChildNodes.Count; nodeIndex++)
1816                         {
1817                                 // Remove empty child nodes of the child
1818                                 RemoveEmptyChildNodes(xmlNode.ChildNodes[nodeIndex]);
1819
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) )
1824                                 {
1825                                         if(!currentNode.HasChildNodes && 
1826                                                 (currentNode.Attributes == null ||
1827                                                 currentNode.Attributes.Count == 0))
1828                                         {
1829                                                 // Remove node
1830                                                 xmlNode.RemoveChild(xmlNode.ChildNodes[nodeIndex]);
1831                                                 --nodeIndex;
1832                                         }
1833                                 }
1834
1835
1836
1837                                 // Remove node with one "_Template_" attribute
1838                                 if(!currentNode.HasChildNodes && 
1839                                         currentNode.Attributes.Count == 1 &&
1840                                         currentNode.Attributes["_Template_"] != null)
1841                                 {
1842                                         // Remove node
1843                                         xmlNode.RemoveChild(xmlNode.ChildNodes[nodeIndex]);
1844                                         --nodeIndex;
1845                                 }
1846
1847
1848
1849                         }
1850                 }
1851
1852                 #endregion
1853
1854                 #region Deserialization public methods
1855
1856                 /// <summary>
1857                 /// Deserialize specified object from the stream.
1858                 /// </summary>
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)
1863                 {
1864                         Deserialize(objectToDeserialize, (object)stream);
1865                 }
1866
1867                 /// <summary>
1868                 /// Deserialize specified object from the XML reader.
1869                 /// </summary>
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)
1874                 {
1875                         Deserialize(objectToDeserialize, (object)xmlReader);
1876                 }
1877
1878                 /// <summary>
1879                 /// Deserialize specified object from the text reader.
1880                 /// </summary>
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)
1885                 {
1886                         Deserialize(objectToDeserialize, (object)textReader);
1887                 }
1888
1889                 /// <summary>
1890                 /// Deserialize specified object from the file.
1891                 /// </summary>
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)
1896                 {
1897                         Deserialize(objectToDeserialize, (object)fileName);
1898                 }
1899
1900                 #endregion
1901
1902                 #region Deserialization private methods
1903
1904                 /// <summary>
1905                 /// Deserialize object from different types of readers using XML format.
1906                 /// </summary>
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)
1910                 {
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;
1916
1917                         // Check input parameters
1918                         if(objectToDeserialize == null)
1919                         {
1920                                 throw(new ArgumentNullException("objectToDeserialize"));
1921                         }
1922                         if(reader == null)
1923                         {
1924                                 throw(new ArgumentNullException("reader"));
1925                         }
1926                         if(stream == null && textReader == null && xmlReader == null && readerStr == null)
1927                         {
1928                 throw (new ArgumentException(SR.ExceptionChartSerializerReaderObjectInvalid, "reader"));
1929                         }
1930
1931                         // Create XML document
1932                         XmlDocument xmlDocument = new XmlDocument();
1933             XmlReader xmlBaseReader = null;
1934             try
1935             {
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.
1939 #if OLD_DTD
1940                 settings.ProhibitDtd = true;
1941 #else
1942                 settings.DtdProcessing = DtdProcessing.Prohibit; //don't allow DTD
1943 #endif
1944                 // Load XML document from the reader
1945                 if (stream != null)
1946                 {
1947                     xmlBaseReader = XmlReader.Create(stream, settings);
1948                 }
1949                 if (readerStr != null)
1950                 {
1951                     xmlBaseReader = XmlReader.Create(readerStr, settings);
1952                 }
1953                 if (xmlReader != null)
1954                 {
1955                     xmlBaseReader = XmlReader.Create(xmlReader, settings);
1956                 }
1957                 if (textReader != null)
1958                 {
1959                     xmlBaseReader = XmlReader.Create(textReader, settings);
1960                 }
1961
1962                 xmlDocument.Load(xmlBaseReader);
1963
1964                 // Reset properties of the root object
1965                 if (IsResetWhenLoading)
1966                 {
1967                     ResetObjectProperties(objectToDeserialize);
1968                 }
1969
1970                 // Deserialize object
1971                 DeserializeObject(objectToDeserialize, null, GetObjectName(objectToDeserialize), xmlDocument.DocumentElement, xmlDocument);
1972             }
1973             finally
1974             {
1975                 if (xmlBaseReader != null)
1976                 {
1977                     ((IDisposable)xmlBaseReader).Dispose();
1978                 }
1979             }
1980                 }
1981
1982                 /// <summary>
1983                 /// Deserialize object from the XML format.
1984                 /// Method is called recursively to deserialize child objects.
1985                 /// </summary>
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)
1993                 {
1994                         int     setPropertiesNumber = 0;
1995
1996                         // Check input parameters
1997                         if(objectToDeserialize == null)
1998                         {
1999                                 return setPropertiesNumber;
2000                         }
2001
2002                         // Loop through all node properties
2003                         foreach(XmlAttribute attr in xmlParentNode.Attributes)
2004                         {
2005                                 // Skip template collection item attribute
2006                                 if(attr.Name == "_Template_")
2007                                 {
2008                                         continue;
2009                                 }
2010
2011                                 // Check if this property is serializable content
2012                                 if(IsSerializableContent(attr.Name, objectToDeserialize))
2013                                 {
2014                                         SetXmlValue(objectToDeserialize, attr.Name, attr.Value);
2015                                         ++setPropertiesNumber;
2016                                 }
2017                         }
2018
2019
2020
2021                         // Read template data into the collection 
2022             IList list = objectToDeserialize as IList;
2023
2024                         if(this.IsTemplateMode && 
2025                                 list != null && 
2026                                 xmlParentNode.FirstChild.Attributes["_Template_"] != null)
2027                         {
2028                                 // Loop through all items in collection
2029                                 int     itemIndex = 0;
2030                                 foreach(object listItem in list)
2031                                 {
2032                                         // Find XML node appropriate for the item from the collection
2033                                         XmlNode listItemNode = null;
2034
2035                                         // Loop through all child nodes
2036                                         foreach(XmlNode childNode in xmlParentNode.ChildNodes)
2037                                         {
2038                                                 string templateString = childNode.Attributes["_Template_"].Value;
2039                                                 if(templateString != null && templateString.Length > 0)
2040                                                 {
2041                                                         if(templateString == "All")
2042                                                         {
2043                                                                 listItemNode = childNode;
2044                                                                 break;
2045                                                         }
2046                                                         else
2047                                                         {
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)
2052                                                                 {
2053                                                                         loopItemIndex -= xmlParentNode.ChildNodes.Count;
2054                                                                 }
2055
2056                                                                 // Convert attribute value to index
2057                                 int nodeIndex = int.Parse(templateString, CultureInfo.InvariantCulture);
2058                                                                 if(nodeIndex == loopItemIndex)
2059                                                                 {
2060                                                                         listItemNode = childNode;
2061                                                                         break;
2062                                                                 }
2063                                                         }
2064                                                 }
2065                                         }
2066
2067                                         // Load data from the node
2068                                         if(listItemNode != null)
2069                                         {
2070                                                 // Load object data
2071                                                 DeserializeObject(listItem, objectToDeserialize, "", listItemNode, xmlDocument);
2072                                         }
2073
2074                                         // Increase item index
2075                                         ++itemIndex;
2076                                 }
2077
2078                                 // No futher loading required
2079                                 return 0;
2080                         }
2081
2082
2083
2084                         // Loop through all child elements
2085                         int     listItemIndex = 0;
2086                         foreach(XmlNode childNode in xmlParentNode.ChildNodes)
2087                         {
2088                 // Special handling for the collections
2089                 // Bug VSTS #235707 - The collections IsSerializableContent are already checked as a property in the else statement.
2090                 if (list != null)
2091                 {
2092                     // Create new item object
2093                     string itemName = null;
2094                     if (childNode.Attributes["Name"] != null)
2095                     {
2096                         itemName = childNode.Attributes["Name"].Value;
2097                     }
2098
2099                     bool reusedObject = false;
2100                     object listItem = GetListNewItem(list, childNode.Name, ref itemName, ref reusedObject);
2101
2102                     // Deserialize list item object
2103                     int itemSetProperties = DeserializeObject(listItem, objectToDeserialize, "", childNode, xmlDocument);
2104                     setPropertiesNumber += itemSetProperties;
2105
2106                     // Add item object into the list
2107                     if (itemSetProperties > 0 || reusedObject)
2108                     {
2109                         list.Insert(listItemIndex++, listItem);
2110                     }
2111                 }
2112
2113                 else
2114                 {
2115                     // Check if this property is serializable content
2116                     if (IsSerializableContent(childNode.Name, objectToDeserialize))
2117                     {
2118                         // Deserialize the property using property descriptor
2119                         PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToDeserialize)[childNode.Name];
2120                         if (pd != null)
2121                         {
2122                             object innerObject = pd.GetValue(objectToDeserialize);
2123
2124                             // Deserialize list item object
2125                             setPropertiesNumber += DeserializeObject(innerObject, objectToDeserialize, childNode.Name, childNode, xmlDocument);
2126                         }
2127                         else if (!IsUnknownAttributeIgnored)
2128                         {
2129                             throw (new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown(childNode.Name, objectToDeserialize.GetType().ToString())));
2130                         }
2131                     }
2132                 }
2133                         }
2134
2135                         return setPropertiesNumber;
2136                 }
2137
2138         /// <summary>
2139         /// Sets a property of an object using name and value as string.
2140         /// </summary>
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)
2146                 {
2147                         PropertyInfo pi = obj.GetType().GetProperty(attrName);
2148                         if(pi != null)
2149                         {
2150                                 // Convert string to object value
2151                                 object objValue = attrValue;
2152
2153                                 if(pi.PropertyType == typeof(string))
2154                                 {
2155                                         objValue = attrValue;
2156                                 }
2157
2158                                 else if(pi.PropertyType == typeof(Font))
2159                                 {
2160                                         objValue = SerializerBase.FontFromString(attrValue);
2161                                 }
2162
2163                                 else if(pi.PropertyType == typeof(Color))
2164                                 {
2165                                         objValue = (Color)colorConverter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, attrValue);
2166                                 }
2167
2168 #if !Microsoft_CONTROL
2169                                 else if(pi.PropertyType == typeof(Unit))
2170                                 {
2171                                         objValue = new Unit(Int32.Parse(attrValue, System.Globalization.CultureInfo.InvariantCulture));
2172                                 }
2173 #endif
2174
2175                                 else if(pi.PropertyType == typeof(System.Drawing.Image))
2176                                 {
2177                                         objValue = ImageFromString(attrValue);
2178                                 }
2179
2180                                 else
2181                                 {
2182                                         // Look for the converter set with the attibute
2183                                         PropertyDescriptor pd = TypeDescriptor.GetProperties(obj)[attrName];
2184                                         if(pd != null)
2185                                         {
2186                         TypeConverter converter = this.FindConverter(pd);
2187                         if (converter != null && converter.CanConvertFrom(typeof(string)))
2188                                                 {
2189                             objValue = converter.ConvertFromString(null, System.Globalization.CultureInfo.InvariantCulture, attrValue);
2190                                                 }
2191                                         }
2192                                 }
2193
2194                                 // Set object value
2195                                 pi.SetValue(obj, objValue, null);
2196                         }
2197                         else if(!IsUnknownAttributeIgnored)
2198                         {
2199                                 throw(new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown( attrName,obj.GetType().ToString())));
2200                         }
2201                 }
2202
2203                 #endregion
2204         }
2205
2206         /// <summary>
2207         /// Utility class which serialize object using binary format
2208         /// </summary>
2209         internal class BinaryFormatSerializer : SerializerBase
2210         {
2211                 #region Serialization methods
2212
2213                 /// <summary>
2214                 /// Serialize specified object into the destination using binary format.
2215                 /// </summary>
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)
2219         {
2220             // Check input parameters
2221             if (objectToSerialize == null)
2222             {
2223                 throw (new ArgumentNullException("objectToSerialize"));
2224             }
2225             if (destination == null)
2226             {
2227                 throw (new ArgumentNullException("destination"));
2228             }
2229
2230             string destinationStr = destination as string;
2231             if (destinationStr != null)
2232             {
2233                 Serialize(objectToSerialize, destinationStr);
2234                 return;
2235             }
2236
2237             Stream stream = destination as Stream;
2238             if (stream != null)
2239             {
2240                 Serialize(objectToSerialize, stream);
2241                 return;
2242             }
2243
2244             BinaryWriter binaryWriter = destination as BinaryWriter;
2245             if (binaryWriter != null)
2246             {
2247                 Serialize(objectToSerialize, binaryWriter);
2248                 return;
2249             }
2250
2251             throw (new ArgumentException(SR.ExceptionChartSerializerDestinationObjectInvalid, "destination"));
2252         }
2253
2254                 /// <summary>
2255                 /// Serialize specified object into the file using binary format.
2256                 /// </summary>
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)
2260                 {
2261                         FileStream stream = new FileStream(fileName, FileMode.Create);
2262                         Serialize(objectToSerialize, new BinaryWriter(stream));
2263                         stream.Close();
2264                 }
2265
2266
2267                 /// <summary>
2268                 /// Serialize specified object into the stream using binary format.
2269                 /// </summary>
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)
2273                 {
2274                         Serialize(objectToSerialize, new BinaryWriter(stream));
2275                 }
2276
2277                 /// <summary>
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
2282                 /// </summary>
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)
2286                 {
2287                         // Check input parameters
2288                         if(objectToSerialize == null)
2289                         {
2290                                 throw(new ArgumentNullException("objectToSerialize"));
2291                         }
2292                         if(writer == null)
2293                         {
2294                                 throw(new ArgumentNullException("writer"));
2295                         }
2296
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);
2300                         
2301
2302                         // Serialize object
2303                         SerializeObject(objectToSerialize, null, GetObjectName(objectToSerialize), writer);
2304
2305
2306                         // Flush the writer stream
2307                         writer.Flush();
2308
2309                         // Reset stream position
2310                         writer.Seek(0, SeekOrigin.Begin);
2311                 }
2312
2313                 /// <summary>
2314                 /// Serialize specified object into the binary format.
2315                 /// Method is called recursively to serialize child objects.
2316                 /// </summary>
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)
2322                 {
2323                         // Check input parameters
2324                         if(objectToSerialize == null)
2325                         {
2326                                 return;
2327                         }
2328
2329                         // Check if object should be serialized
2330                         if(parent != null)
2331                         {
2332                                 PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
2333                                 if(pd != null)
2334                                 {
2335                                         SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
2336                                         if(styleAttribute != null)
2337                                         {
2338                                                 // Hidden property
2339                                                 if(styleAttribute.Visibility == SerializationVisibility.Hidden)
2340                                                 {
2341                                                         return;
2342                                                 }
2343                                         }
2344                                 }
2345                         }
2346
2347                         // Check if object is a collection
2348                         if(objectToSerialize is ICollection)
2349                         {
2350                                 // Serialize collection
2351                                 SerializeCollection(objectToSerialize, elementName, writer);
2352                                 return;
2353                         }
2354
2355                         // Write object ID (hash of the name) into the writer
2356                         writer.Write(SerializerBase.GetStringHashCode(elementName));
2357
2358                         // Remember position where object data is started
2359                         long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
2360
2361                         // Retrive properties list of the object
2362                         ArrayList       propNamesList = new ArrayList();
2363                         PropertyInfo[] properties = objectToSerialize.GetType().GetProperties();
2364                         if(properties != null)
2365                         {
2366                                 // Loop through all properties and serialize public properties
2367                                 foreach(PropertyInfo pi in properties)
2368                                 {
2369                                         // Skip inherited properties from the root object
2370                                         if(IsChartBaseProperty(objectToSerialize, parent, pi))
2371                                         {
2372                                                 continue;
2373                                         }
2374                                         // Serialize collection
2375                     if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null && !this.SerializeICollAsAtribute(pi, objectToSerialize))
2376                     {
2377                         bool serialize = IsSerializableContent(pi.Name, objectToSerialize);
2378
2379                         // fixing Axes Array Framework 2.0 side effect
2380                         // fixed by:DT
2381                         if (serialize && objectToSerialize != null)
2382                         {
2383                             PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToSerialize)[pi.Name];
2384                             if (pd != null)
2385                             {
2386                                 SerializationVisibilityAttribute styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
2387                                 if (styleAttribute != null)
2388                                 {
2389                                     if (styleAttribute.Visibility == SerializationVisibility.Hidden)
2390                                     {
2391                                         serialize = false;
2392                                     }
2393                                 }
2394                             }
2395                         }
2396
2397                         MethodInfo mi = objectToSerialize.GetType().GetMethod("ShouldSerialize" + pi.Name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
2398                                                 if( serialize && mi != null)
2399                                                 {
2400                                                         object result = mi.Invoke(objectToSerialize, null);
2401                                                         if(result is bool && ((bool)result) == false)
2402                                                         {
2403                                                                 // Do not serialize collection
2404                                                                 serialize = false;
2405                                                         }
2406                                                 }
2407
2408                                                 // Serialize collection
2409                                                 if(serialize)
2410                                                 {
2411                                                         propNamesList.Add(pi.Name);
2412                                                         SerializeCollection(pi.GetValue(objectToSerialize, null), pi.Name, writer);
2413                                                 }
2414                                         }
2415
2416                                         // Serialize public properties with Get and Set methods
2417                                         else if(pi.CanRead && pi.CanWrite)
2418                                         {
2419                                                 // Skip indexes
2420                                                 if(pi.Name == "Item")
2421                                                 {
2422                                                         continue;
2423                                                 }
2424                         // Check if this property is serializable content
2425                         if (IsSerializableContent(pi.Name, objectToSerialize))
2426                         {
2427                             // Check if an object should be serialized as a property or as a class
2428                             if (ShouldSerializeAsAttribute(pi, objectToSerialize))
2429                             {
2430                                 // Serialize property
2431                                 SerializeProperty(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, writer);
2432                             }
2433                             else
2434                             {
2435                                 // Serialize inner object
2436                                 SerializeObject(pi.GetValue(objectToSerialize, null), objectToSerialize, pi.Name, writer);
2437                             }
2438                         }
2439                                                 propNamesList.Add(pi.Name);
2440                                         }
2441                                 }
2442                         
2443                                 // Check that all properties have unique IDs
2444                                 CheckPropertiesID(propNamesList);
2445                         }
2446
2447
2448                         // If position of the writer did not change - nothing was written
2449                         if(writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
2450                         {
2451                                 // Remove object ID from the stream
2452                                 writer.Seek(-2, SeekOrigin.Current);
2453                                 writer.Write((short)0);
2454                                 writer.Seek(-2, SeekOrigin.Current);
2455                         }
2456                         else
2457                         {
2458                                 // Write the end objectTag 
2459                                 writer.Write((short)0);
2460                         }
2461
2462                         return;
2463                 }
2464
2465
2466         /// <summary>
2467         /// Serializes the data point.
2468         /// </summary>
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)
2473         {
2474
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);    
2479
2480             DataPoint dataPoint = objectToSerialize as DataPoint;
2481             if (dataPoint.XValue != 0d && IsSerializableContent("XValue", objectToSerialize))
2482             {
2483                 SerializeProperty(dataPoint.XValue, dataPoint, "XValue", writer);
2484             }
2485             if (dataPoint.YValues.Length > 0 && IsSerializableContent("YValues", objectToSerialize))
2486             {
2487                 SerializeProperty(dataPoint.YValues, dataPoint, "YValues", writer);
2488             }
2489             if (dataPoint.IsEmpty && IsSerializableContent("IsEmpty", objectToSerialize))
2490             {
2491                 SerializeProperty(dataPoint.IsEmpty, dataPoint, "IsEmpty", writer);
2492             }
2493             bool hasCustomProperties = false;
2494             foreach (DictionaryEntry entry in dataPoint.properties)
2495             {
2496                 if (entry.Key is int)
2497                 {
2498                     CommonCustomProperties propertyType = (CommonCustomProperties)((int)entry.Key);
2499                     String properyName = propertyType.ToString();
2500                     if (IsSerializableContent(properyName, objectToSerialize))
2501                     {
2502                         SerializeProperty(entry.Value, dataPoint, properyName, writer);
2503                     }
2504                 }
2505                 else
2506                 {
2507                     hasCustomProperties = true;
2508                 }
2509             }
2510
2511             if (hasCustomProperties && !String.IsNullOrEmpty(dataPoint.CustomProperties) && IsSerializableContent("CustomProperties", objectToSerialize))
2512             {
2513                 SerializeProperty(dataPoint.CustomProperties, dataPoint, "CustomProperties", writer);
2514             }  
2515
2516             // If position of the writer did not change - nothing was written
2517             if (writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
2518             {
2519                 // Remove object ID from the stream
2520                 writer.Seek(-2, SeekOrigin.Current);
2521                 writer.Write((short)0);
2522                 writer.Seek(-2, SeekOrigin.Current);
2523             }
2524             else
2525             {
2526                 // Write the end objectTag 
2527                 writer.Write((short)0);
2528             }
2529         }
2530
2531
2532         /// <summary>
2533                 /// Serialize specified object into the binary writer.
2534                 /// Method is called recursively to serialize child objects.
2535                 /// </summary>
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)
2540                 {
2541             ICollection collection = objectToSerialize as ICollection;
2542                         if(collection != null)
2543                         {
2544                                 // Write object ID (hash of the name) into the writer
2545                                 writer.Write(SerializerBase.GetStringHashCode(elementName));
2546
2547                                 // Remember position where object data is started
2548                                 long elementStartPosition = writer.Seek(0, SeekOrigin.Current);
2549
2550                                 // Enumerate through all objects in collection and serialize them
2551                 foreach (object obj in collection)
2552                                 {
2553
2554                     if (obj is DataPoint)
2555                     {
2556                         SerializeDataPoint(obj, GetObjectName(obj), writer);
2557                         continue;
2558                     }
2559
2560                     SerializeObject(obj, objectToSerialize, GetObjectName(obj), writer);
2561                                 }
2562
2563                                 // If position of the writer did not change - nothing was written
2564                                 if(writer.Seek(0, SeekOrigin.Current) == elementStartPosition)
2565                                 {
2566                                         // Remove object ID from the stream
2567                                         writer.Seek(-2, SeekOrigin.Current);
2568                                         writer.Write((short)0);
2569                                         writer.Seek(-2, SeekOrigin.Current);
2570                                 }
2571                                 else
2572                                 {
2573                                         // Write the end objectTag 
2574                                         writer.Write((short)0);
2575                                 }
2576
2577                         }
2578                 }
2579
2580                 /// <summary>
2581                 /// Serialize specified object into the binary writer.
2582                 /// Method is called recursively to serialize child objects.
2583                 /// </summary>
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)
2589                 {
2590                         // Check input parameters
2591                         if(objectToSerialize == null || parent == null)
2592                         {
2593                                 return;
2594                         }
2595
2596                         // Check if property has non-default value
2597                         PropertyDescriptor pd = TypeDescriptor.GetProperties(parent)[elementName];
2598                         if(pd != null)
2599                         {
2600                                 DefaultValueAttribute defValueAttribute = (DefaultValueAttribute)pd.Attributes[typeof(DefaultValueAttribute)];
2601                                 if(defValueAttribute != null)
2602                                 {
2603                                         if(objectToSerialize.Equals(defValueAttribute.Value))
2604                                         {
2605                                                 // Do not serialize properties with default values
2606                                                 return;
2607                                         }
2608                                 }
2609                                 else
2610                                 {
2611                                         // Check if property has "ShouldSerialize" method
2612                                         MethodInfo mi = parent.GetType().GetMethod("ShouldSerialize" + elementName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public );
2613                                         if(mi != null)
2614                                         {
2615                                                 object result = mi.Invoke(parent, null);
2616                                                 if(result is bool && ((bool)result) == false)
2617                                                 {
2618                                                         // Do not serialize properties with default values
2619                                                         return;
2620                                                 }
2621                                         }
2622                 }
2623                         
2624                                 // Check XmlFormatSerializerStyle attribute
2625                                 SerializationVisibilityAttribute        styleAttribute = (SerializationVisibilityAttribute)pd.Attributes[typeof(SerializationVisibilityAttribute)];
2626                                 if(styleAttribute != null)
2627                                 {
2628                                         // Hidden property
2629                                         if(styleAttribute.Visibility == SerializationVisibility.Hidden)
2630                                         {
2631                                                 return;
2632                                         }
2633                                 }
2634                         }
2635
2636                         // Write property 
2637                         WritePropertyValue(objectToSerialize, elementName, writer);
2638                 }
2639
2640                 /// <summary>
2641                 /// Converts object value into the string.
2642                 /// </summary>
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)
2650                 {
2651                         // Write property ID (hash of the name) into the writer
2652                         writer.Write(SerializerBase.GetStringHashCode(elementName));
2653                         
2654                         if(obj is bool)
2655                         {
2656                                 writer.Write(((bool)obj));
2657                         }
2658                         else if(obj is double)
2659                         {
2660                                 writer.Write(((double)obj));
2661                         }
2662                         else if(obj is string)
2663                         {
2664                                 writer.Write(((string)obj));
2665                         }
2666                         else if(obj is int)
2667                         {
2668                                 writer.Write(((int)obj));
2669                         }
2670                         else if(obj is long)
2671                         {
2672                                 writer.Write(((long)obj));
2673                         }
2674                         else if(obj is float)
2675                         {
2676                                 writer.Write(((float)obj));
2677                         }
2678                         else if(obj.GetType().IsEnum)
2679                         {
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);
2685                         }
2686
2687                         else if(obj is byte)
2688                         {
2689                                 // Write as long
2690                                 writer.Write((byte)obj);
2691                         }
2692
2693 #if !Microsoft_CONTROL
2694                         else if(obj is Unit)
2695                         {
2696                                 writer.Write(((Unit)obj).Value);
2697                         }
2698 #endif
2699
2700                         else if(obj is Font)
2701                         {
2702                                 // Write as string
2703                                 writer.Write(SerializerBase.FontToString((Font)obj));
2704                         }
2705
2706                         else if(obj is Color)
2707                         {
2708                                 // Write as int
2709                                 writer.Write(((Color)obj).ToArgb());
2710                         }
2711
2712                         else if(obj is DateTime)
2713                         {
2714                                 // Write as long
2715                                 writer.Write(((DateTime)obj).Ticks);
2716                         }
2717
2718                         else if(obj is Size)
2719                         {
2720                                 // Write as two integers
2721                                 writer.Write(((Size)obj).Width);
2722                                 writer.Write(((Size)obj).Height);
2723                         }
2724
2725                         else if(obj is double[])
2726                         {
2727                                 double[] arr = (double[])obj;
2728
2729                                 // Write the size of the array (int)
2730                                 writer.Write(arr.Length);
2731
2732                                 // Write each element of the array
2733                                 foreach(double d in arr)
2734                                 {
2735                                         writer.Write(d);
2736                                 }
2737                         }
2738                         
2739                         else if(obj is Color[])
2740                         {
2741                                 Color[] arr = (Color[])obj;
2742
2743                                 // Write the size of the array (int)
2744                                 writer.Write(arr.Length);
2745
2746                                 // Write each element of the array
2747                                 foreach(Color color in arr)
2748                                 {
2749                                         writer.Write(color.ToArgb());
2750                                 }
2751                         }
2752
2753                         else if(obj is System.Drawing.Image)
2754                         {
2755                                 // Save image into the memory stream
2756                                 MemoryStream imageStream = new MemoryStream();
2757                                 ((System.Drawing.Image)obj).Save(imageStream, ((System.Drawing.Image)obj).RawFormat);
2758                                 
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);
2763
2764                                 // Write the data
2765                                 writer.Write(imageStream.ToArray());
2766
2767                                 imageStream.Close();
2768                         }
2769
2770
2771
2772                         else if(obj is Margins)
2773                         {
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);
2779                         }
2780
2781
2782
2783                         else
2784                         {
2785                 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryTypeUnsupported(obj.GetType().ToString())));
2786                         }
2787                 }
2788
2789                 /// <summary>
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!!!
2793                 /// </summary>
2794                 /// <param name="propNames">Array of properties names.</param>
2795         internal void CheckPropertiesID(ArrayList propNames)
2796                 {
2797 #if DEBUG
2798                         if(propNames != null)
2799                         {
2800                                 // Loop through all properties and check the hash values
2801                                 foreach(string name1 in propNames)
2802                                 {
2803                                         foreach(string name2 in propNames)
2804                                         {
2805                                                 if(name1 != name2)
2806                                                 {
2807                                                         if( SerializerBase.GetStringHashCode(name1) == SerializerBase.GetStringHashCode(name2) )
2808                                                         {
2809                                 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryHashCodeDuplicate(name1,name2)));
2810                                                         }
2811                                                 }
2812                                         }
2813                                 }
2814                         }
2815 #endif
2816                 }
2817                 #endregion
2818
2819                 #region Deserialization methods
2820
2821                 /// <summary>
2822                 /// Deserialize specified object from the source using binary format.
2823                 /// </summary>
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)
2827         {
2828             // Check input parameters
2829             if (objectToDeserialize == null)
2830             {
2831                 throw (new ArgumentNullException("objectToDeserialize"));
2832             }
2833             if (source == null)
2834             {
2835                 throw (new ArgumentNullException("source"));
2836             }
2837
2838             string sourceStr = source as string;
2839             if (sourceStr != null)
2840             {
2841                 Deserialize(objectToDeserialize, sourceStr);
2842                 return;
2843             }
2844
2845             Stream stream = source as Stream;
2846             if (stream != null)
2847             {
2848                 Deserialize(objectToDeserialize, stream);
2849                 return;
2850             }
2851
2852             BinaryWriter binaryWriter = source as BinaryWriter;
2853             if (binaryWriter != null)
2854             {
2855                 Deserialize(objectToDeserialize, binaryWriter);
2856                 return;
2857             }
2858
2859             throw (new ArgumentException(SR.ExceptionChartSerializerSourceObjectInvalid, "source"));
2860         }
2861
2862                 /// <summary>
2863                 /// Deserialize object from the file using binary format.
2864                 /// </summary>
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)
2868                 {
2869                         FileStream stream = new FileStream(fileName, FileMode.Open);
2870                         Deserialize(objectToDeserialize, new BinaryReader(stream));
2871                         stream.Close();
2872                 }
2873
2874                 /// <summary>
2875                 /// Deserialize object from the stream using binary format.
2876                 /// </summary>
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)
2880                 {
2881                         Deserialize(objectToDeserialize, new BinaryReader(stream));
2882                 }
2883
2884                 /// <summary>
2885                 /// Deserialize object from different types of readers using binary format.
2886                 /// </summary>
2887                 /// <param name="objectToDeserialize">Object to be deserialized.</param>
2888                 /// <param name="reader">Binary reader.</param>
2889                 public void Deserialize(object objectToDeserialize, BinaryReader reader)
2890                 {
2891                         // Check input parameters
2892                         if(objectToDeserialize == null)
2893                         {
2894                                 throw(new ArgumentNullException("objectToDeserialize"));
2895                         }
2896                         if(reader == null)
2897                         {
2898                                 throw(new ArgumentNullException("reader"));
2899                         }
2900
2901                         // Binary deserializer do not support IsUnknownAttributeIgnored property
2902                         if(base.IsUnknownAttributeIgnored)
2903                         {
2904                 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryIgnoreUnknownAttributesUnsupported));
2905                         }
2906
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')
2910                         {
2911                 throw (new InvalidOperationException(SR.ExceptionChartSerializerBinaryFromatInvalid));
2912                         }
2913
2914                         // Get ID of the root object
2915                         this.ReadHashID(reader);
2916  
2917                         // Reset properties of the root object
2918                         if(IsResetWhenLoading)
2919                         {
2920                                 ResetObjectProperties(objectToDeserialize);
2921                         }
2922
2923                         // Deserialize object
2924                         DeserializeObject(objectToDeserialize, null, GetObjectName(objectToDeserialize), reader, false);
2925                 }
2926
2927         /// <summary>
2928         /// Deserialize object from the binary format.
2929         /// Method is called recursively to deserialize child objects.
2930         /// </summary>
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)
2938                 {
2939                         int     setPropertiesNumber = 0;
2940
2941                         // Check input parameters
2942                         if(objectToDeserialize == null)
2943                         {
2944                                 return setPropertiesNumber;
2945                         }
2946
2947                         // Special handling for the collections
2948                         Type[] assemblyTypes = null;
2949                         int     listItemIndex = 0;
2950
2951             IList list = objectToDeserialize as IList;
2952
2953                         if(list != null)
2954                         {
2955                                 // Loop through all list items
2956                                 Int16 typeHash = 0;
2957                 PropertyInfo listItemPI = objectToDeserialize.GetType().GetProperty("Item", new Type[] { typeof(int) });
2958                 while ((typeHash = this.ReadHashID(reader)) != 0)
2959                                 {
2960                                         // Get collection item type from hashed type name
2961                                         string  typeName = String.Empty;
2962                                         if(listItemPI != null)
2963                                         {
2964                         if ((SerializerBase.GetStringHashCode(listItemPI.PropertyType.Name)) == typeHash)
2965                         {
2966                             typeName = listItemPI.PropertyType.Name;
2967                         }
2968                         else
2969                         {
2970                             Assembly assembly = listItemPI.PropertyType.Assembly;
2971                             if (assembly != null)
2972                             {
2973                                 // Find all classes derived from this type
2974                                 if (assemblyTypes == null)
2975                                 {
2976                                     assemblyTypes = assembly.GetExportedTypes();
2977                                 }
2978                                 foreach (Type type in assemblyTypes)
2979                                 {
2980                                     if (type.IsSubclassOf(listItemPI.PropertyType))
2981                                     {
2982                                         if ((SerializerBase.GetStringHashCode(type.Name)) == typeHash)
2983                                         {
2984                                             typeName = type.Name;
2985                                             break;
2986                                         }
2987                                     }
2988                                 }
2989                             }
2990                         }
2991                                         }
2992                 
2993                                         // Create new item object
2994                                         string itemName = null;
2995                                         bool    reusedObject = false;
2996                                         object listItem = GetListNewItem(list, typeName, ref itemName, ref reusedObject);
2997
2998
2999                                         // Deserialize list item object
3000                                         int itemSetProperties = DeserializeObject(listItem, objectToDeserialize, "", reader, skipElement);
3001
3002                     // Add item object into the list
3003                     if (!skipElement && (itemSetProperties > 0 || reusedObject))
3004                     {
3005                         list.Insert(listItemIndex++, listItem);
3006                     }
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;
3010                                 }
3011
3012                                 return setPropertiesNumber;
3013                         }       
3014
3015                         // Get list of object's properties
3016                         PropertyInfo[] properties = objectToDeserialize.GetType().GetProperties();
3017                         if(properties == null)
3018                         {
3019                                 return setPropertiesNumber;
3020                         }
3021             
3022                         // Get property information by reading the ID
3023                         PropertyInfo pi = null;
3024             while ( (pi = ReadPropertyInfo(objectToDeserialize, parent, properties, reader)) != null)
3025                         {
3026                                 // Read simple properties
3027                                 if(ShouldSerializeAsAttribute(pi, objectToDeserialize))
3028                                 {
3029                                         if(SetPropertyValue(objectToDeserialize, pi, reader, skipElement))
3030                                         {
3031                                                 ++setPropertiesNumber;
3032                                         }
3033                                 }
3034
3035                                 else
3036                                 {
3037                                         // Get property descriptor
3038                                         PropertyDescriptor pd = TypeDescriptor.GetProperties(objectToDeserialize)[pi.Name];
3039                                         if(pd != null)
3040                                         {
3041                                                 object innerObject = pd.GetValue(objectToDeserialize);
3042                                                 
3043                                                 // Deserialize inner item object
3044                         setPropertiesNumber += DeserializeObject(innerObject, objectToDeserialize, pi.Name, reader, !IsSerializableContent(pi.Name, objectToDeserialize));
3045                                         }
3046                                         else if(!IsUnknownAttributeIgnored)
3047                                         {
3048                                                 throw(new InvalidOperationException(SR.ExceptionChartSerializerPropertyNameUnknown( pi.Name,objectToDeserialize.GetType().ToString())));
3049                                         }
3050                                 }
3051                         }
3052
3053                         return setPropertiesNumber;
3054                 }
3055
3056         /// <summary>
3057         /// Reads and sets a property of an object.
3058         /// </summary>
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)
3065                 {
3066                         if(pi != null)
3067                         {
3068                                 object objValue = null;
3069
3070
3071                                 if(pi.PropertyType == typeof(bool))
3072                                 {
3073                                         objValue = reader.ReadBoolean();
3074                                 }
3075                                 else if(pi.PropertyType == typeof(double))
3076                                 {
3077                                         objValue = reader.ReadDouble();
3078                                 }
3079                                 else if(pi.PropertyType == typeof(string))
3080                                 {
3081                                         objValue = reader.ReadString();
3082                                 }
3083                                 else if(pi.PropertyType == typeof(int))
3084                                 {
3085                                         objValue = reader.ReadInt32();
3086                                 }
3087                                 else if(pi.PropertyType == typeof(long))
3088                                 {
3089                                         objValue = reader.ReadInt64();
3090                                 }
3091                                 else if(pi.PropertyType == typeof(float))
3092                                 {
3093                                         objValue = reader.ReadSingle();
3094                                 }
3095                                 else if(pi.PropertyType.IsEnum)
3096                                 {
3097                                         // Read as string
3098                                         objValue = Enum.Parse(pi.PropertyType, reader.ReadString());
3099                                 }
3100                                 else if(pi.PropertyType == typeof(byte))
3101                                 {
3102                                         objValue = reader.ReadByte();
3103                                 }
3104
3105 #if !Microsoft_CONTROL
3106                                 else if(pi.PropertyType == typeof(Unit))
3107                                 {
3108                                         objValue = new Unit((double)reader.ReadDouble());
3109                                 }
3110 #endif
3111
3112                                 else if(pi.PropertyType == typeof(Font))
3113                                 {
3114                                         // Read as string
3115                                         objValue = SerializerBase.FontFromString(reader.ReadString());
3116                                 }
3117
3118                                 else if(pi.PropertyType == typeof(Color))
3119                                 {
3120                                         // Read as int
3121                                         objValue = Color.FromArgb(reader.ReadInt32());
3122                                 }
3123
3124                                 else if(pi.PropertyType == typeof(DateTime))
3125                                 {
3126                                         // Read as long
3127                                         objValue = new DateTime(reader.ReadInt64());
3128                                 }
3129
3130                                 else if(pi.PropertyType == typeof(Size))
3131                                 {
3132                                         // Read as two integers
3133                                         objValue = new Size(reader.ReadInt32(), reader.ReadInt32());
3134                                 }
3135
3136
3137
3138                                 else if(pi.PropertyType == typeof(Margins) )
3139                                 {
3140                                         // Read as 4 integers
3141                                         objValue = new Margins(
3142                                                 reader.ReadInt32(), 
3143                                                 reader.ReadInt32(), 
3144                                                 reader.ReadInt32(), 
3145                                                 reader.ReadInt32());
3146                                 }
3147
3148
3149
3150                                 else if(pi.PropertyType == typeof(double[]))
3151                                 {
3152                                         // Allocate array
3153                                         double[] array = new double[reader.ReadInt32()];
3154
3155                                         // Read each element of the array
3156                                         for(int arrayIndex = 0; arrayIndex < array.Length; arrayIndex++)
3157                                         {
3158                                                 array[arrayIndex] = reader.ReadDouble();
3159                                         }
3160
3161                                         objValue = array;
3162                                 }
3163
3164                                 else if(pi.PropertyType == typeof(Color[]))
3165                                 {
3166                                         // Allocate array
3167                                         Color[] array = new Color[reader.ReadInt32()];
3168
3169                                         // Read each element of the array
3170                                         for(int arrayIndex = 0; arrayIndex < array.Length; arrayIndex++)
3171                                         {
3172                                                 array[arrayIndex] = Color.FromArgb(reader.ReadInt32());
3173                                         }
3174
3175                                         objValue = array;
3176                                 }
3177
3178                                 else if(pi.PropertyType == typeof(System.Drawing.Image))
3179                                 {       
3180                                         // Get image data size
3181                                         int imageSize = reader.ReadInt32();
3182
3183                                         // Create image stream
3184                                         MemoryStream imageStream = new MemoryStream(imageSize + 10);
3185
3186                                         // Copy image data into separate stream
3187                                         imageStream.Write(reader.ReadBytes(imageSize), 0, imageSize);
3188
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
3191
3192                                         // Close image stream
3193                                         imageStream.Close();
3194                                 }                       
3195
3196                                 else
3197                                 {
3198                                         throw(new InvalidOperationException(SR.ExceptionChartSerializerBinaryTypeUnsupported( obj.GetType().ToString() )));
3199                                 }
3200
3201
3202                                 // Check if this property is serializable content
3203                 if (!skipElement && IsSerializableContent(pi.Name, obj))
3204                                 {
3205                                         // Set object value
3206                                         pi.SetValue(obj, objValue, null);
3207
3208                                         return true;
3209                                 }
3210                         }
3211                 
3212                         return false;
3213                 }
3214
3215                 /// <summary>
3216                 /// Reads property ID and return property information
3217                 /// </summary>
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)
3224                 {
3225                         // Read property ID
3226                         short   propertyID = this.ReadHashID(reader);
3227
3228                         // End objectTag reached
3229                         if(propertyID == 0)
3230                         {
3231                                 return null;
3232                         }
3233
3234                         // Loop through all properties and check properties IDs (hash code of name)
3235                         foreach(PropertyInfo pi in properties)
3236                         {
3237                                 // Skip inherited properties from the root object
3238                                 if(IsChartBaseProperty(objectToDeserialize, parent, pi))
3239                                 {
3240                                         continue;
3241                                 }
3242
3243                                 // Check collection
3244                 if (pi.CanRead && pi.PropertyType.GetInterface("ICollection", true) != null)
3245                                 {
3246                                         if((SerializerBase.GetStringHashCode(pi.Name)) == propertyID)
3247                                         {
3248                                                 return pi;
3249                                         }
3250                                 }
3251
3252                                 // Check public properties with Get and Set methods
3253                                 else if(pi.CanRead && pi.CanWrite)
3254                                 {
3255                                         // Skip indexes
3256                                         if(pi.Name == "Item")
3257                                         {
3258                                                 continue;
3259                                         }
3260
3261                                         if((SerializerBase.GetStringHashCode(pi.Name)) == propertyID)
3262                                         {
3263                                                 return pi;
3264                                         }
3265                                 }
3266                         }
3267
3268                         // Property was not found
3269             throw (new InvalidOperationException(SR.ExceptionChartSerializerPropertyNotFound));
3270                 }
3271
3272                 #endregion
3273         }
3274 }