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