2002-08-16 Jason Diamond <jason@injektilo.org>
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlSerializer.cs
1 //
2 // Mono Class Libraries
3 // System.Xml.Serialization.XmlSerializer
4 //
5 // Authors:
6 //   John Donagher (john@webmeta.com)
7 //   Ajay kumar Dwivedi (adwiv@yahoo.com)
8 //   Tim Coleman (tim@timcoleman.com)
9 //
10 // (C) 2002 John Donagher, Ajay kumar Dwivedi
11 // Copyright (C) Tim Coleman, 2002
12 // 
13
14 using System;
15 using System.Collections;
16 using System.IO;
17 using System.Reflection;
18 using System.Xml;
19 using System.Xml.Schema;
20
21 namespace System.Xml.Serialization {    
22         /// <summary>
23         /// Summary description for XmlSerializer.
24         /// </summary>
25         public class XmlSerializer {
26
27                 #region Fields
28
29                 Type type;
30                 XmlAttributeOverrides overrides;
31                 Type[] extraTypes;
32                 XmlRootAttribute rootAttribute;
33                 string defaultNamespace;
34                 static Hashtable typeTable;
35                 bool useOrder;
36                 bool isNullable;
37
38                 #endregion // Fields
39
40                 #region Constructors
41
42                 protected XmlSerializer ()
43                 {
44                 }
45
46                 public XmlSerializer (Type type)
47                         : this (type, null, null, null, null)
48                 {
49                 }
50
51                 [MonoTODO]
52                 public XmlSerializer (XmlTypeMapping xmltypemapping)
53                 {
54                 }
55
56                 public XmlSerializer (Type type, string defaultNamespace)
57                         : this (type, null, null, null, defaultNamespace)
58                 {
59                 }
60
61                 public XmlSerializer (Type type, Type[] extraTypes)
62                         : this (type, null, extraTypes, null, null)
63                 {
64                 }
65
66                 public XmlSerializer (Type type, XmlAttributeOverrides overrides)
67                         : this (type, overrides, null, null, null)
68                 {
69                 }
70
71                 public XmlSerializer (Type type, XmlRootAttribute root)
72                         : this (type, null, null, root, null)
73                 {
74                 }
75
76                 internal XmlSerializer (Hashtable typeTable)
77                 {
78                         typeTable = typeTable;
79                 }
80
81                 public XmlSerializer (Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, string defaultNamespace)
82                 {
83                         if (type == null)
84                                 throw new ArgumentNullException ("type", "XmlSerializer can't be constructed with a null type");
85
86                         this.type = type;
87                         this.overrides = overrides;
88                         this.extraTypes = (extraTypes == null ? new Type[0] : extraTypes);
89                 
90                         if (root != null)
91                                 this.rootAttribute = root;
92                         else {
93                                 object[] attributes = type.GetCustomAttributes (typeof (XmlRootAttribute), false);
94                                 if (attributes.Length > 0)
95                                         this.rootAttribute = (XmlRootAttribute) attributes[0];
96                         }
97                         
98                         this.defaultNamespace = defaultNamespace;
99
100                         if (typeTable == null)
101                                 typeTable = new Hashtable ();
102
103                         FillTypeTable (type);
104                 }
105
106                 #endregion // Constructors
107
108                 #region Events
109
110                 [MonoTODO]
111                 public event XmlAttributeEventHandler UnknownAttribute;
112                 [MonoTODO]
113                 public event XmlElementEventHandler UnknownElement;
114                 [MonoTODO]
115                 public event XmlNodeEventHandler UnknownNode;
116                 [MonoTODO]
117                 public event UnreferencedObjectEventHandler UnreferencedObject;
118
119                 #endregion // Events
120
121                 #region Properties
122
123                 public bool UseOrder {
124                         get { return useOrder; }
125                         set { useOrder = value; }
126                 }
127
128                 #endregion // Properties
129
130                 #region Methods
131
132                 [MonoTODO]
133                 public virtual bool CanDeserialize (XmlReader xmlReader)
134                 {
135                         throw new NotImplementedException ();
136                 }
137                 [MonoTODO]
138                 public object Deserialize (Stream stream)
139                 {
140                         throw new NotImplementedException ();
141                 }
142                 [MonoTODO]
143                 public object Deserialize (TextReader textReader)
144                 {
145                         throw new NotImplementedException ();
146                 }
147                 [MonoTODO]
148                 public object Deserialize (XmlReader xmlReader)
149                 {
150                         throw new NotImplementedException ();
151                 }
152
153                 public void Serialize (Stream stream, object o)
154                 {
155                         XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
156                         xmlWriter.Formatting = Formatting.Indented;
157                         Serialize (xmlWriter, o, null);
158                 }
159                 
160                 public void Serialize (TextWriter textWriter, object o)
161                 {
162                         XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
163                         xmlWriter.Formatting = Formatting.Indented;
164                         Serialize (xmlWriter, o, null);
165                 }
166                 
167                 public void Serialize (XmlWriter xmlWriter, object o)
168                 {
169                         Serialize (xmlWriter, o);
170                 }
171                 
172                 public void Serialize (Stream stream, object o, XmlSerializerNamespaces namespaces)
173                 {
174                         XmlTextWriter xmlWriter = new XmlTextWriter (stream, System.Text.Encoding.Default);
175                         xmlWriter.Formatting = Formatting.Indented;
176                         Serialize (xmlWriter, o, namespaces);
177                 }
178                 
179                 public void Serialize (TextWriter textWriter, object o, XmlSerializerNamespaces namespaces)
180                 {
181                         XmlTextWriter xmlWriter = new XmlTextWriter (textWriter);
182                         xmlWriter.Formatting = Formatting.Indented;
183                         Serialize (xmlWriter, o, namespaces);
184                 }
185
186                 public void Serialize (XmlWriter writer, object o, XmlSerializerNamespaces namespaces)
187                 {       
188                         Type objType = o.GetType ();
189                         string rootName = objType.Name;
190                         string rootNs = String.Empty;
191                         string rootPrefix = String.Empty;
192
193                         if (namespaces == null)
194                                 namespaces = new XmlSerializerNamespaces ();
195
196                         if (namespaces.Count == 0) {
197                                 namespaces.Add ("xsd", XmlSchema.Namespace);
198                                 namespaces.Add ("xsi", XmlSchema.InstanceNamespace);
199                         }
200
201                         XmlSerializerNamespaces nss = new XmlSerializerNamespaces ();
202                         XmlQualifiedName[] qnames;
203
204                         writer.WriteStartDocument ();
205                         
206                         object[] memberObj = (object[]) typeTable[objType];
207                         if (memberObj == null)
208                                 throw new Exception ("Unknown Type " + objType + " encountered during Serialization");
209
210                         Hashtable memberTable = (Hashtable) memberObj[0];
211                         XmlAttributes xmlAttributes = (XmlAttributes) memberTable[""];
212
213                         //If we have been passed an XmlRoot, set it on the base class
214                         if (rootAttribute != null)
215                                 xmlAttributes.XmlRoot = rootAttribute;
216                         
217                         if (xmlAttributes.XmlRoot != null) {
218                                 isNullable = xmlAttributes.XmlRoot.IsNullable;
219                                 if (xmlAttributes.XmlRoot.ElementName != null)
220                                         rootName = xmlAttributes.XmlRoot.ElementName;
221                                 rootNs  = xmlAttributes.XmlRoot.Namespace;
222                         }
223
224                         if (namespaces.GetPrefix (rootNs) != null)
225                                 rootPrefix = namespaces.GetPrefix (rootNs);
226
227                         //XMLNS attributes in the Root
228                         XmlAttributes XnsAttrs = (XmlAttributes) ((object[]) typeTable[objType])[1];
229                         if (XnsAttrs != null) {
230                                 MemberInfo member = XnsAttrs.MemberInfo;
231                                 FieldInfo fieldInfo = member as FieldInfo;
232                                 PropertyInfo propertyInfo = member as PropertyInfo;
233                                 XmlSerializerNamespaces xns;
234
235                                 if (fieldInfo != null)
236                                         xns = (XmlSerializerNamespaces) fieldInfo.GetValue (o);
237                                 else
238                                         xns = (XmlSerializerNamespaces) propertyInfo.GetValue (o, null);
239                                 
240                                 qnames = xns.ToArray ();
241
242                                 foreach (XmlQualifiedName qname in qnames)
243                                         nss.Add (qname.Name, qname.Namespace);
244                         }
245
246                         //XmlNs from the namespaces passed
247                         qnames = namespaces.ToArray ();
248                         foreach (XmlQualifiedName qname in qnames)
249                                 if (writer.LookupPrefix (qname.Namespace) != qname.Name)
250                                         nss.Add (qname.Name, qname.Namespace);
251
252                         writer.WriteStartElement (rootPrefix, rootName, rootNs);
253
254                         qnames = nss.ToArray();
255                         foreach (XmlQualifiedName qname in qnames)
256                                 if (writer.LookupPrefix (qname.Namespace) != qname.Name)
257                                         writer.WriteAttributeString ("xmlns", qname.Name, null, qname.Namespace);
258
259                         if (rootPrefix == String.Empty && rootNs != String.Empty && rootNs != null)
260                                 writer.WriteAttributeString (String.Empty, "xmlns", null, rootNs);
261
262                         SerializeMembers (writer, o, true);//, namespaces);
263                         writer.WriteEndDocument ();
264                 }
265
266                 private void SerializeMembers (XmlWriter writer, object o, bool isRoot)
267                 {
268                         Type objType = o.GetType ();
269                         XmlAttributes nsAttributes = (XmlAttributes) ((object[]) typeTable [objType])[1];
270                         ArrayList attributes = (ArrayList) ((object[]) typeTable [objType])[2];
271                         ArrayList elements = (ArrayList) ((object[]) typeTable [objType])[3];
272
273
274                         if (!isRoot && nsAttributes != null) {
275                                 MemberInfo member = nsAttributes.MemberInfo;
276                                 FieldInfo fieldInfo = member as FieldInfo;
277                                 PropertyInfo propertyInfo = member as PropertyInfo;
278
279                                 XmlSerializerNamespaces xns;
280
281                                 if (fieldInfo != null)
282                                         xns = (XmlSerializerNamespaces) fieldInfo.GetValue (o);
283                                 else
284                                         xns = (XmlSerializerNamespaces) propertyInfo.GetValue (o, null);
285                                 
286                                 XmlQualifiedName[] qnames = xns.ToArray ();
287                                 foreach (XmlQualifiedName qname in qnames)
288                                         if (writer.LookupPrefix (qname.Namespace) != qname.Name)
289                                                 writer.WriteAttributeString ("xmlns", qname.Name, null, qname.Namespace);
290                         }
291
292                         //Serialize the Attributes.
293                         foreach (XmlAttributes xmlAttributes in attributes) {
294                                 MemberInfo member = xmlAttributes.MemberInfo;
295                                 FieldInfo fieldInfo = member as FieldInfo;
296                                 PropertyInfo propertyInfo = member as PropertyInfo;
297
298                                 Type attributeType;
299                                 object attributeValue;
300                                 string attributeValueString;
301                                 string attributeName;
302                                 string attributeNs;
303
304                                 if (fieldInfo != null) {
305                                         attributeType = fieldInfo.FieldType;
306                                         attributeValue = fieldInfo.GetValue (o);
307                                 } 
308                                 else {
309                                         attributeType = propertyInfo.PropertyType;
310                                         attributeValue = propertyInfo.GetValue (o, null);
311                                 }
312
313                                 attributeName = xmlAttributes.GetAttributeName (attributeType, member.Name);
314                                 attributeNs = xmlAttributes.GetAttributeNamespace (attributeType);
315                         
316                                 if (attributeValue is XmlQualifiedName) {
317                                         XmlQualifiedName qname = (XmlQualifiedName) attributeValue;
318
319                                         if (qname.IsEmpty)
320                                                 continue;
321
322                                         writer.WriteStartAttribute (attributeName, attributeNs);
323                                         writer.WriteQualifiedName (qname.Name, qname.Namespace);
324                                         writer.WriteEndAttribute ();
325                                         continue;
326                                 }
327                                 else if (attributeValue is XmlQualifiedName[]) {
328                                         XmlQualifiedName[] qnames = (XmlQualifiedName[]) attributeValue;
329                                         writer.WriteStartAttribute (attributeName, attributeNs);
330                                         int count = 0;
331                                         foreach (XmlQualifiedName qname in qnames) {
332                                                 if (qname.IsEmpty)
333                                                         continue;
334                                                 if (count++ > 0)
335                                                         writer.WriteWhitespace (" ");
336                                                 writer.WriteQualifiedName (qname.Name, qname.Namespace);
337                                         }
338                                         writer.WriteEndAttribute ();
339                                         continue;
340                                 }
341                                 else if (attributeValue is XmlAttribute[]) {
342                                         XmlAttribute[] xmlattrs = (XmlAttribute[]) attributeValue;
343                                         foreach (XmlAttribute xmlattr in xmlattrs)
344                                                 xmlattr.WriteTo(writer);
345                                         continue;
346                                 }
347
348                                 attributeValueString = GetXmlValue (attributeValue);
349                                 if (attributeValueString != GetXmlValue (xmlAttributes.XmlDefaultValue))
350                                         writer.WriteAttributeString (attributeName, attributeNs, attributeValueString);
351                         }
352
353                         // Serialize Elements
354                         foreach (XmlAttributes xmlElements in elements) {
355                                 MemberInfo member = xmlElements.MemberInfo;
356                                 FieldInfo fieldInfo = member as FieldInfo;
357                                 PropertyInfo propertyInfo = member as PropertyInfo;
358
359                                 Type elementType;
360                                 object elementValue;
361                                 string elementName;
362                                 string elementNs;
363
364                                 if (fieldInfo != null) {
365                                         elementType = fieldInfo.FieldType;
366                                         elementValue = fieldInfo.GetValue (o);
367                                 }
368                                 else {
369                                         elementType  = propertyInfo.PropertyType;
370                                         elementValue = propertyInfo.GetValue (o, null);
371                                 }
372
373                                 elementName = xmlElements.GetElementName (elementType, member.Name);
374                                 elementNs = xmlElements.GetElementNamespace (elementType);
375                                 WriteElement (writer, xmlElements, elementName, elementNs, elementType, elementValue);
376                         }
377                 }
378
379                 [MonoTODO ("Remove FIXMEs")]
380                 private void WriteElement (XmlWriter writer, XmlAttributes attrs, string name, string ns, Type type, Object value)
381                 {
382                         if (IsInbuiltType (type)) {
383                                 string xmlValue = GetXmlValue (value);
384                                 if (xmlValue != String.Empty && xmlValue != null)
385                                         writer.WriteElementString (name, ns,  xmlValue);
386                         }
387                         else if (attrs.XmlText != null && value != null) {
388                                 if (type == typeof (object[])) {
389                                         // FIXME
390                                 }
391                                 else if (type == typeof (string[])) {
392                                         // FIXME
393                                 }
394                                 else if (type == typeof (XmlNode)) {
395                                         ((XmlNode) value).WriteTo (writer);
396                                 }
397                                 else if (type == typeof (XmlNode[])) {
398                                         XmlNode[] nodes = (XmlNode[]) value;
399                                         foreach (XmlNode node in nodes)
400                                                 node.WriteTo (writer);
401                                 }
402                         }
403                         else if (type.IsArray && value != null) {
404                                 writer.WriteStartElement (name, ns);
405                                 SerializeArray (writer, value);
406                                 writer.WriteEndElement ();
407                         }
408                         else if (value is ICollection) {
409                                 BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
410
411                                 //Find a non indexer Count Property with return type of int
412                                 PropertyInfo countInfo = type.GetProperty ("Count", flags, null, typeof (int), new Type[0], null);
413                                 PropertyInfo itemInfo = type.GetProperty ("Item", flags, null, null, new Type[1] {typeof (int)}, null);
414                                 int count = (int) countInfo.GetValue (value, null);
415
416                                 if (count > 0) 
417                                         for (int i = 0; i < count; i++) {
418                                                 object itemValue = itemInfo.GetValue (value, new object[1] {i});
419
420                                                 if (itemValue != null) {
421                                                         string itemName = attrs.GetElementName (itemValue.GetType (), name);
422                                                         string itemNs = attrs.GetElementNamespace (itemValue.GetType ());
423
424                                                         writer.WriteStartElement (itemName, itemNs);
425                                                         SerializeMembers (writer, itemValue, false);
426                                                         writer.WriteEndElement ();
427                                                 }
428                                         }
429                         }
430                         else if (value is IEnumerable) {
431                                 // FIXME
432                         }
433                         else if (type.IsEnum) {
434                                 // FIXME
435                         }
436                         else if (value != null) { //Complex Type?
437                                 string itemName = attrs.GetElementName (value.GetType (), name);
438                                 string itemNs = attrs.GetElementNamespace (value.GetType ());
439                                 writer.WriteStartElement (itemName, itemNs);
440                                 SerializeMembers (writer, value, false);
441                                 writer.WriteEndElement ();
442                         }
443                         else {
444                                 // FIXME
445                         }
446                 }
447
448                 [MonoTODO]
449                 private void SerializeArray (XmlWriter writer, object o)
450                 {
451                         throw new NotImplementedException ();
452                 }
453
454                 /// <summary>
455                 /// If the type is a string, valuetype or primitive type we do not populate the TypeTable.
456                 /// If the type is an array, we populate the TypeTable with Element type of the array.
457                 /// If the type implements ICollection, it is handled differently. We do not care for its members.
458                 /// If the type implements IEnumberable, we check that it implements Add(). Don't care for members.
459                 /// </summary>
460                 [MonoTODO ("Remove FIXMEs")]
461                 private void FillTypeTable (Type type)
462                 {
463                         if (typeTable.Contains (type))
464                                 return;
465
466                         //For value types and strings we don't need the members.
467                         //FIXME: We will need the enum types probably.
468                         if (IsInbuiltType (type))
469                                 return;
470
471                         //Array, ICollection and IEnumberable are treated differenty
472                         if (type.IsArray) {
473                                 FillArrayType (type);
474                                 return;
475                         }
476                         else if (type.IsEnum) {
477                                 FillEnum (type);
478                                 return;
479                         }
480                         else {
481                                 //There must be a public constructor
482                                 if (!HasDefaultConstructor (type))
483                                         throw new Exception ("Can't Serialize Type " + type.Name + " since it does not have default Constructor");
484
485                                 if (type.GetInterface ("ICollection") == typeof (System.Collections.ICollection)) {
486                                         FillICollectionType (type);
487                                         return;
488                                 }
489                                 if (type.GetInterface ("IEnumerable") == typeof (System.Collections.IEnumerable)) {
490                                         //FillIEnumerableType(type);
491                                         //return;
492                                 }
493                         }
494
495                         
496                         //Add the Class to the hashtable.
497                         //Each value of the hashtable has two objects, one is the hashtable with key of membername (for deserialization)
498                         //Other is an Array of XmlSerializernames, Array of XmlAttributes & Array of XmlElements.
499                         Object[] memberObj = new Object[4];
500                         typeTable.Add (type,memberObj);
501
502                         Hashtable memberTable = new Hashtable ();
503                         memberObj[0] = memberTable;
504                         memberTable.Add ("", XmlAttributes.FromClass (type));
505
506                         memberObj[1] = null;
507
508                         ArrayList attributes = new ArrayList ();
509                         memberObj[2] = attributes;
510
511                         ArrayList elements = new ArrayList ();
512                         memberObj[3] = elements;
513
514                         //Get the graph of the members. Graph is nothing but the order
515                         //in which MS implementation serializes the members.
516                         MemberInfo[] minfo = GetGraph (type);
517
518                         foreach (MemberInfo member in minfo) {
519                                 FieldInfo fieldInfo = (member as FieldInfo);
520                                 PropertyInfo propertyInfo = (member as PropertyInfo);
521
522                                 if (fieldInfo != null) {
523                                         //If field is readOnly or const, do not serialize it.
524                                         if (fieldInfo.IsLiteral || fieldInfo.IsInitOnly)
525                                                 continue;
526
527                                         XmlAttributes xmlAttributes = XmlAttributes.FromField (member, fieldInfo);
528
529                                         //If XmlAttributes have XmlIgnore, ignore this member
530
531                                         if (xmlAttributes.XmlIgnore)
532                                                 continue;
533
534                                         //If this member is a XmlNs type, set the XmlNs object.
535                                         if (xmlAttributes.Xmlns) {
536                                                 memberObj[1] = xmlAttributes;
537                                                 continue;
538                                         }
539
540                                         //If the member is a attribute Type, Add to attribute list
541                                         if (xmlAttributes.isAttribute)
542                                                 attributes.Add (xmlAttributes);
543                                         else //Add to elements
544                                                 elements.Add (xmlAttributes);
545
546                                         //Add in the Hashtable.
547                                         memberTable.Add (member.Name, xmlAttributes);
548                                         
549                                         if (xmlAttributes.XmlAnyAttribute != null  || xmlAttributes.XmlText != null)
550                                                 continue;
551
552                                         if (xmlAttributes.XmlElements.Count > 0) {
553                                                 foreach (XmlElementAttribute elem in xmlAttributes.XmlElements) {
554                                                         if (elem.Type != null)
555                                                                 FillTypeTable (elem.Type);
556                                                         else
557                                                                 FillTypeTable (fieldInfo.FieldType);
558                                                 }
559                                                 continue;
560                                         }
561
562                                         if (!IsInbuiltType (fieldInfo.FieldType))
563                                                 FillTypeTable (fieldInfo.FieldType);
564                                 } 
565                                 else if (propertyInfo != null) {
566                                         //If property is readonly or writeonly, do not serialize it.
567                                         //Exceptions are properties whose return type is array, ICollection or IEnumerable
568                                         //Indexers are not serialized unless the class Implements ICollection.
569                                         if (!(propertyInfo.PropertyType.IsArray || Implements (propertyInfo.PropertyType, typeof (ICollection)) || 
570                                                 (propertyInfo.PropertyType != typeof (string) && Implements (propertyInfo.PropertyType, typeof (IEnumerable))))) {
571                                                 if(!(propertyInfo.CanRead && propertyInfo.CanWrite) || propertyInfo.GetIndexParameters ().Length != 0)
572                                                         continue;
573                                         }
574
575                                         XmlAttributes xmlAttributes = XmlAttributes.FromProperty (member, propertyInfo);
576
577                                         // If XmlAttributes have XmlIgnore, ignore this member
578                                         if (xmlAttributes.XmlIgnore)
579                                                 continue;
580
581                                         // If this member is a XmlNs type, set the XmlNs object.
582                                         if (xmlAttributes.Xmlns) {
583                                                 memberObj[1] = xmlAttributes;
584                                                 continue;
585                                         }
586                                         // If the member is a attribute Type, Add to attribute list
587                                         if (xmlAttributes.isAttribute)
588                                                 attributes.Add (xmlAttributes);
589                                         else  //Add to elements
590                                                 elements.Add (xmlAttributes);
591
592                                         // OtherWise add in the Hashtable.
593                                         memberTable.Add (member.Name, xmlAttributes);
594
595                                         if (xmlAttributes.XmlAnyAttribute != null || xmlAttributes.XmlText != null)
596                                                 continue;
597
598                                         if (xmlAttributes.XmlElements.Count > 0) {
599                                                 foreach (XmlElementAttribute elem in xmlAttributes.XmlElements) {
600                                                         if (elem.Type != null)
601                                                                 FillTypeTable (elem.Type);
602                                                         else
603                                                                 FillTypeTable (propertyInfo.PropertyType);
604                                                 }
605                                                 continue;
606                                         }
607
608                                         if (!IsInbuiltType (propertyInfo.PropertyType))
609                                                 FillTypeTable (propertyInfo.PropertyType);
610                                 }
611                         }
612
613                         // Sort the attributes for the members according to their Order
614                         // This is an extension to MS's Implementation and will be useful
615                         // if our reflection does not return the same order of elements
616                         // as MS .NET impl
617                         if (useOrder)
618                                 BubbleSort (elements, XmlAttributes.attrComparer);
619                 }
620
621                 private void FillArrayType (Type type)
622                 {
623                         if (type.GetArrayRank () != 1)
624                                 throw new Exception ("MultiDimensional Arrays are not Supported");
625
626                         Type arrayType = type.GetElementType ();
627
628                         if (arrayType.IsArray)
629                                 FillArrayType (arrayType);
630                         else if (!IsInbuiltType (arrayType))
631                                 FillTypeTable (arrayType);
632                 }
633
634                 private void FillICollectionType (Type type)
635                 {
636                         //Must have an public Indexer that takes an integer and
637                         //a public Count Property which returns an int.
638
639                         BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
640                         
641                         //Find a non indexer Count Property with return type of int
642                         PropertyInfo countProp = type.GetProperty ("Count", flags, null, typeof (int), new Type[0], null);
643                         if (countProp == null || !countProp.CanRead)
644                                 throw new Exception ("Cannot Serialize " + type + " because it implements ICollectoion, but does not implement public Count property");
645                         //Find a indexer Item Property which takes an int
646                         PropertyInfo itemProp = type.GetProperty ("Item", flags, null, null, new Type[1] {typeof (int)}, null);
647                         if (itemProp == null || !itemProp.CanRead || !itemProp.CanWrite)
648                                 throw new Exception ("Cannot Serialize " + type + " because it does not have a read/write indexer property that takes an int as argument");
649                         FillTypeTable (itemProp.PropertyType);
650                 }
651
652                 [MonoTODO]
653                 private void FillIEnumerableType (Type type)
654                 {
655                         //Must implement a public Add method that takes a single parameter.
656                         //The Add method's parameter must be of the same type as is returned from 
657                         //the Current property on the value returned from GetEnumerator, or one of that type's bases.
658
659                         // We currently ignore enumerable types anyway, so this method was junked.
660                         // The code did not do what the documentation above says (if that is even possible!)
661                         return;
662                 }
663
664                 private void FillEnum (Type type)
665                 {
666                         Hashtable memberTable = new Hashtable ();
667                         BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
668                         typeTable.Add (type, memberTable);
669                         string[] names = Enum.GetNames (type);
670
671                         foreach (string name in names) {
672                                 MemberInfo[] members = type.GetMember (name);
673                                 if (members.Length != 1)
674                                         throw new Exception("Should never happen. Enum member not present or more than one. " + name);
675                                 XmlAttributes xmlAttributes = new XmlAttributes (members[0]);
676
677                                 if (xmlAttributes.XmlIgnore)
678                                         continue;
679
680                                 if (xmlAttributes.XmlEnum != null)
681                                         memberTable.Add (members[0].Name, xmlAttributes.XmlEnum.Name);
682                                 else
683                                         memberTable.Add (members[0].Name, members[0].Name);
684                         }
685                 }
686
687                 private bool HasDefaultConstructor (Type type)
688                 {
689                         ConstructorInfo defaultConstructor = type.GetConstructor (new Type[0]);
690                         if (defaultConstructor == null || defaultConstructor.IsAbstract || defaultConstructor.IsStatic || !defaultConstructor.IsPublic)
691                                 return false;
692                         
693                         return true;
694                 }
695
696                 private bool IsInbuiltType (Type type)
697                 {
698                         if (type.IsEnum)
699                                 return false;
700                         if (type.IsValueType || type == typeof (string) || type.IsPrimitive)
701                                 return true;
702                         return false;
703                 }
704
705                 private static MemberInfo[] GetGraph(Type type)
706                 {
707                         ArrayList typeGraph = new ArrayList ();
708                         GetGraph (type, typeGraph);
709                         return (MemberInfo[]) typeGraph.ToArray (typeof (MemberInfo));
710                 }
711
712                 private static void GetGraph (Type type, ArrayList typeGraph)
713                 {
714                         BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
715                         if (type.BaseType == null)
716                                 return;
717                         GetGraph (type.BaseType, typeGraph);
718
719                         typeGraph.AddRange (type.GetFields (flags));
720                         typeGraph.AddRange (type.GetProperties (flags));
721                 }
722
723                 private string GetXmlValue (object value)
724                 {
725                         if (value == null)
726                                 return null;
727
728                         if (value is Enum) {
729                                 Type type = value.GetType ();
730                                 
731                                 if (typeTable.ContainsKey (type)) {
732                                         Hashtable memberTable = (Hashtable) (typeTable[type]);
733                                         if (type.IsDefined (typeof (FlagsAttribute), false)) {
734                                                 //If value is exactly a single enum member
735                                                 if (memberTable.Contains (value.ToString ()))
736                                                         return (string) memberTable[value.ToString ()];
737
738                                                 string retval = "";
739                                                 int enumval = (int) value;
740                                                 string[] names = Enum.GetNames (type);
741
742                                                 foreach (string key in names) {
743                                                         if (!memberTable.ContainsKey (key))
744                                                                 continue;
745
746                                                         //Otherwise multiple values.
747                                                         int val = (int) Enum.Parse (type, key);
748                                                         if (val != 0 && (enumval & val) == val)
749                                                                 retval += " " + (string) memberTable[Enum.GetName (type, val)];
750                                                 }
751
752                                                 retval = retval.Trim ();
753
754                                                 if (retval.Length == 0)
755                                                         return null;
756
757                                                 return retval;
758                                         }
759                                         else if (memberTable.ContainsKey (value.ToString ()))
760                                                 return (string) memberTable[value.ToString()];
761                                         else
762                                                 return null;
763                                 }
764                                 else
765                                         throw new Exception ("Unknown Enumeration");
766                         }
767                         if (value is bool)
768                                 return (bool) value ? "true" : "false";
769                         if (value is XmlQualifiedName) {
770                                 if (((XmlQualifiedName) value).IsEmpty)
771                                         return null;
772                         }
773                         return (value == null) ? null : value.ToString ();
774                 }
775
776                 [MonoTODO ("Remove FIXMEs")]
777                 private static void ProcessAttributes (XmlAttributes attrs, Hashtable memberTable)
778                 {
779                         if (attrs.XmlAnyAttribute != null) {
780                                 // FIXME
781                         }
782                         foreach (XmlAnyElementAttribute anyelem in attrs.XmlAnyElements) 
783                                 memberTable.Add (new XmlQualifiedName (anyelem.Name, anyelem.Namespace), attrs);
784
785                         if (attrs.XmlArray != null) {
786                                 // FIXME
787                         }
788
789                         foreach (XmlArrayItemAttribute item in attrs.XmlArrayItems)
790                                 memberTable.Add (new XmlQualifiedName (item.ElementName, item.Namespace), attrs);
791
792                         if (attrs.XmlAttribute != null)
793                                 memberTable.Add (new XmlQualifiedName (attrs.XmlAttribute.AttributeName,attrs.XmlAttribute.Namespace), attrs);
794
795                         if (attrs.XmlChoiceIdentifier != null) {
796                                 // FIXME
797                         }
798
799                         foreach (XmlElementAttribute elem in attrs.XmlElements)
800                                 memberTable.Add (new XmlQualifiedName (elem.ElementName, elem.Namespace), attrs);
801
802                         if (attrs.XmlEnum != null) {
803                                 // FIXME
804                         }
805
806                         if (attrs.XmlType != null)
807                                 memberTable.Add (new XmlQualifiedName (attrs.XmlType.TypeName, attrs.XmlType.Namespace), attrs);
808                 }
809
810                 private bool Implements (Type type, Type interfaceType)
811                 {
812                         return (type.GetInterface (interfaceType.Name) == interfaceType);
813                 }
814
815                 private static void BubbleSort (ArrayList array, IComparer comparer)
816                 {
817                         int len = array.Count;
818                         object obj1, obj2;
819                         for (int i=0; i < len; i++) {
820                                 for (int j=0; j < len -i -1; j++) {
821                                         obj1 = array[j];
822                                         obj2 = array[j+1];
823                                         if (comparer.Compare (obj2 , obj1 ) < 0) {
824                                                 array[j] = obj2;
825                                                 array[j+1] = obj1;
826                                         }
827                                 }
828                         }
829                 }
830                 #endregion // Methods
831         }
832 }