Moved ProviderCollectionTest.cs from System assembly to System.Configuration.
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / SerializationMap.cs
1 //
2 // SerializationMap.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Ankit Jain <JAnkit@novell.com>
7 //      Duncan Mak (duncan@ximian.com)
8 //      Eyal Alaluf (eyala@mainsoft.com)
9 //
10 // Copyright (C) 2005 Novell, Inc.  http://www.novell.com
11 // Copyright (C) 2006 Novell, Inc.  http://www.novell.com
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32 #if NET_2_0
33 using System;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.ObjectModel;
37 using System.Reflection;
38 using System.Xml;
39 using System.Xml.Schema;
40 using System.Xml.Serialization;
41
42 using QName = System.Xml.XmlQualifiedName;
43
44 namespace System.Runtime.Serialization
45 {
46 /*
47         XmlFormatter implementation design inference:
48
49         type definitions:
50         - No XML Schema types are directly used. There are some maps from
51           xs:blahType to ms:blahType where the namespaceURI for prefix "ms" is
52           "http://schemas.microsoft.com/2003/10/Serialization/" .
53
54         serializable types:
55         - An object being serialized 1) must be of type System.Object, or
56           2) must be null, or 3) must have either a [DataContract] attribute
57           or a [Serializable] attribute to be serializable.
58         - When the object is either of type System.Object or null, then the
59           XML type is "anyType".
60         - When the object is [Serializable], then the runtime-serialization
61           compatible object graph is written.
62         - Otherwise the serialization is based on contract attributes.
63           ([Serializable] takes precedence).
64
65         type derivation:
66         - For type A to be serializable, the base type B of A must be
67           serializable.
68         - If a type which is [Serializable] and whose base type has a
69           [DataContract], then for base type members [DataContract] is taken.
70         - It is vice versa i.e. if the base type is [Serializable] and the
71           derived type has a [DataContract], then [Serializable] takes place
72           for base members.
73
74         known type collection:
75         - It internally manages mapping store keyed by contract QNames.
76           KnownTypeCollection.Add() checks if the same QName contract already
77           exists (and raises InvalidOperationException if required).
78
79 */
80         internal abstract class SerializationMap
81         {
82                 public const BindingFlags AllInstanceFlags =
83                         BindingFlags.Public | BindingFlags.NonPublic |
84                         BindingFlags.Instance;
85
86                 public readonly KnownTypeCollection KnownTypes;
87                 public readonly Type RuntimeType;
88                 public readonly QName XmlName;
89                 public List<DataMemberInfo> Members;
90 #if !NET_2_1
91                 XmlSchemaSet schema_set;
92 #endif
93  
94                 //FIXME FIXME
95                 Dictionary<Type, QName> qname_table = new Dictionary<Type, QName> ();
96
97                 protected SerializationMap (
98                         Type type, QName qname, KnownTypeCollection knownTypes)
99                 {
100                         KnownTypes = knownTypes;
101                         RuntimeType = type;
102                         if (qname.Namespace == String.Empty)
103                                 qname = new QName (qname.Name,
104                                         "http://schemas.datacontract.org/2004/07/" + type.Namespace);
105
106                         XmlName = qname;
107                         Members = new List<DataMemberInfo> ();
108                 }
109
110                 public DataMemberAttribute GetDataMemberAttribute (
111                         MemberInfo mi)
112                 {
113                         object [] atts = mi.GetCustomAttributes (
114                                 typeof (DataMemberAttribute), false);
115                         if (atts.Length == 0)
116                                 return null;
117                         return (DataMemberAttribute) atts [0];
118                 }
119
120                 bool IsPrimitive (Type type)
121                 {
122                         return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
123                 }
124
125 #if !NET_2_1
126                 /* Returns the XmlSchemaType AND adds it to @schemas */
127                 public virtual XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
128                 {
129                         if (IsPrimitive (RuntimeType))
130                                 return null;
131
132                         if (generated_schema_types.ContainsKey (XmlName)) // Caching  
133                                 return generated_schema_types [XmlName] as XmlSchemaType;
134
135                         XmlSchemaComplexType complex_type = null;
136
137                         complex_type = new XmlSchemaComplexType ();
138                         complex_type.Name = XmlName.Name;
139                         generated_schema_types [XmlName] = complex_type;
140
141                         if (RuntimeType.BaseType == typeof (object)) {
142                                 complex_type.Particle = GetSequence (schemas, generated_schema_types);
143                         } else {
144                                 //Has a non-System.Object base class
145                                 XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension ();
146                                 XmlSchemaComplexContent content = new XmlSchemaComplexContent ();
147
148                                 complex_type.ContentModel = content;
149                                 content.Content = extension;
150
151                                 KnownTypes.Add (RuntimeType.BaseType);
152                                 SerializationMap map = KnownTypes.FindUserMap (RuntimeType.BaseType);
153                                 //FIXME: map == null ?
154                                 map.GetSchemaType (schemas, generated_schema_types);
155
156                                 extension.Particle = GetSequence (schemas, generated_schema_types);
157                                 extension.BaseTypeName = GetQualifiedName (RuntimeType.BaseType);
158                         }
159                         
160                         XmlSchemaElement schemaElement = GetSchemaElement (XmlName, complex_type);
161                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
162                         schema.Items.Add (complex_type);
163                         schema.Items.Add (schemaElement);
164                         schemas.Reprocess (schema);
165
166                         return complex_type;
167                 }
168
169                 /* Returns the <xs:sequence> for the data members */
170                 XmlSchemaSequence GetSequence (XmlSchemaSet schemas,
171                                 Dictionary<QName, XmlSchemaType> generated_schema_types)
172                 {
173                         List<DataMemberInfo> members = GetMembers ();
174
175                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
176                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
177                         foreach (DataMemberInfo dmi in members) {
178                                 // delegates are not supported.
179                                 if (!dmi.MemberType.IsAbstract && typeof (System.Delegate).IsAssignableFrom (dmi.MemberType))
180                                         continue;
181
182                                 XmlSchemaElement element = new XmlSchemaElement ();
183                                 element.Name = dmi.XmlName;
184
185                                 KnownTypes.Add (dmi.MemberType);
186                                 SerializationMap map = KnownTypes.FindUserMap (dmi.MemberType);
187                                 if (map != null) {
188                                         XmlSchemaType schema_type = map.GetSchemaType (schemas, generated_schema_types);
189                                         if (schema_type is XmlSchemaComplexType)
190                                                 element.IsNillable = true;
191                                 } else {
192                                         //Primitive type
193                                         if (dmi.MemberType == typeof (string))
194                                                 element.IsNillable = true;
195                                 }
196
197                                 element.MinOccurs = 0;
198
199                                 element.SchemaTypeName = GetQualifiedName (dmi.MemberType);
200                                 AddImport (schema, element.SchemaTypeName.Namespace);
201
202                                 sequence.Items.Add (element);
203                         }
204
205                         schemas.Reprocess (schema);
206                         return sequence;
207                 }
208
209                 //FIXME: Replace with a dictionary ?
210                 void AddImport (XmlSchema schema, string ns)
211                 {
212                         if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
213                                 return;
214
215                         foreach (XmlSchemaObject o in schema.Includes) {
216                                 XmlSchemaImport import = o as XmlSchemaImport;
217                                 if (import == null)
218                                         continue;
219                                 if (import.Namespace == ns)
220                                         return;
221                         }
222
223                         XmlSchemaImport imp = new XmlSchemaImport ();
224                         imp.Namespace = ns;
225                         schema.Includes.Add (imp);
226                 }
227 #endif
228
229                 //Returns list of data members for this type ONLY
230                 public virtual List<DataMemberInfo> GetMembers ()
231                 {
232                         throw new NotImplementedException (String.Format ("Implement me for {0}", this));
233                 }
234
235 #if !NET_2_1
236                 protected XmlSchemaElement GetSchemaElement (QName qname, XmlSchemaType schemaType)
237                 {
238                         XmlSchemaElement schemaElement = new XmlSchemaElement ();
239                         schemaElement.Name = qname.Name;
240                         schemaElement.SchemaTypeName = qname;
241
242                         if (schemaType is XmlSchemaComplexType)
243                                 schemaElement.IsNillable = true;
244
245                         return schemaElement;
246                 }
247
248                 protected XmlSchema GetSchema (XmlSchemaSet schemas, string ns)
249                 {
250                         ICollection colln = schemas.Schemas (ns);
251                         if (colln.Count > 0) {
252                                 if (colln.Count > 1)
253                                         throw new Exception (String.Format (
254                                                 "More than 1 schema for namespace '{0}' found.", ns));
255                                 foreach (object o in colln)
256                                         //return colln [0]
257                                         return (o as XmlSchema);
258                         }
259
260                         XmlSchema schema = new XmlSchema ();
261                         schema.TargetNamespace = ns;
262                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
263                         schemas.Add (schema);
264
265                         return schema;
266                 }
267 #endif
268
269                 //FIXME: redundant?
270                 protected XmlQualifiedName GetQualifiedName (Type type)
271                 {
272                         if (qname_table.ContainsKey (type))
273                                 return qname_table [type];
274
275                         QName qname = KnownTypes.GetQName (type);
276                         if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
277                                 qname = new QName (qname.Name, XmlSchema.Namespace);
278
279                         qname_table [type] = qname;
280                         return qname;
281                 }
282
283                 public virtual void Serialize (object graph,
284                         XmlFormatterSerializer serializer)
285                 {
286                         foreach (DataMemberInfo dmi in Members) {
287                                 FieldInfo fi = dmi.Member as FieldInfo;
288                                 PropertyInfo pi = fi == null ?
289                                         (PropertyInfo) dmi.Member : null;
290                                 Type type = fi != null ?
291                                         fi.FieldType : pi.PropertyType;
292                                 object value = fi != null ?
293                                         fi.GetValue (graph) :
294                                         pi.GetValue (graph, null);
295
296                                 serializer.WriteStartElement (dmi.XmlName, dmi.XmlRootNamespace, dmi.XmlNamespace);
297                                 serializer.Serialize (type, value);
298                                 serializer.WriteEndElement ();
299                         }
300                 }
301                 
302                 /* Deserialize non-primitive types */
303                 public virtual object DeserializeContent (XmlReader reader,
304                         XmlFormatterDeserializer deserializer)
305                 {
306                         object instance = FormatterServices.GetUninitializedObject (RuntimeType);
307                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
308                         bool [] filled = new bool [Members.Count];
309                         int memberInd = -1;
310                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
311                                 DataMemberInfo dmi = null;
312                                 for (int i = memberInd + 1; i < Members.Count; i++) {
313                                         if (reader.LocalName == Members [i].XmlName &&
314                                                 reader.NamespaceURI == Members [i].XmlRootNamespace) {
315                                                 memberInd = i;
316                                                 dmi = Members [i];
317                                                 break;
318                                         }
319                                 }
320
321                                 if (dmi == null) {
322                                         reader.Skip ();
323                                         continue;
324                                 }
325                                 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
326                                 filled [memberInd] = true;
327                         }
328                         for (int i = 0; i < Members.Count; i++)
329                                 if (!filled [i] && Members [i].IsRequired)
330                                         throw MissingRequiredMember (Members [i], reader);
331
332                         return instance;
333                 }
334
335                 // For now it could be private.
336                 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
337                 {
338                         return new ArgumentException (String.Format ("Data contract member {0} is required, but missing in the input XML.", new QName (dmi.XmlName, dmi.XmlNamespace)));
339                 }
340
341                 // For now it could be private.
342                 protected void SetValue (DataMemberInfo dmi, object obj, object value)
343                 {
344                         if (dmi.Member is PropertyInfo)
345                                 ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
346                         else
347                                 ((FieldInfo) dmi.Member).SetValue (obj, value);
348                 }
349
350                 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
351                 {
352                         KnownTypes.Add (type);
353                         QName qname = KnownTypes.GetQName (type);
354                         string rootNamespace = KnownTypes.GetQName (mi.DeclaringType).Namespace;
355                         if (KnownTypeCollection.IsPrimitiveType (qname))
356                                 return new DataMemberInfo (mi, dma, rootNamespace, null);
357                         else
358                                 return new DataMemberInfo (mi, dma, rootNamespace, qname.Namespace);
359                 }
360         }
361
362         internal class XmlSerializableMap : SerializationMap
363         {
364                 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
365                         : base (type, qname, knownTypes)
366                 {
367                 }
368
369                 public override void Serialize (object graph, XmlFormatterSerializer serializer)
370                 {
371                         IXmlSerializable ixs = graph as IXmlSerializable;
372                         if (ixs == null)
373                                 //FIXME: Throw what exception here?
374                                 throw new SerializationException ();
375
376                         ixs.WriteXml (serializer.Writer);
377                 }
378         }
379
380         internal class SharedContractMap : SerializationMap
381         {
382                 public SharedContractMap (
383                         Type type, QName qname, KnownTypeCollection knownTypes)
384                         : base (type, qname, knownTypes)
385                 {
386                         Type baseType = type;
387                         List <DataMemberInfo> members = new List <DataMemberInfo> ();
388                         
389                         while (baseType != null) {
390                                 QName bqname = knownTypes.GetQName (baseType);
391                                         
392                                 members = GetMembers (baseType, bqname, true);
393                                 Members.InsertRange (0, members);
394                                 members.Clear ();
395
396                                 baseType = baseType.BaseType;
397                         }
398
399 //                      Members.Sort (delegate (
400 //                              DataMemberInfo d1, DataMemberInfo d2) {
401 //                                      return d1.Order - d2.Order;
402 //                              });
403                 }
404
405                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
406                 {
407                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
408                         BindingFlags flags = AllInstanceFlags;
409                         if (declared_only)
410                                 flags |= BindingFlags.DeclaredOnly;
411
412                         foreach (PropertyInfo pi in type.GetProperties (flags)) {
413                                 DataMemberAttribute dma =
414                                         GetDataMemberAttribute (pi);
415                                 if (dma == null)
416                                         continue;
417                                 if (!pi.CanRead || !pi.CanWrite)
418                                         throw new InvalidDataContractException (String.Format (
419                                                         "DataMember property {0} must have both getter and setter.", pi));
420                                 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType));
421                         }
422
423                         foreach (FieldInfo fi in type.GetFields (flags)) {
424                                 DataMemberAttribute dma =
425                                         GetDataMemberAttribute (fi);
426                                 if (dma == null)
427                                         continue;
428                                 if (fi.IsInitOnly)
429                                         throw new InvalidDataContractException (String.Format (
430                                                         "DataMember field {0} must not be read-only.", fi));
431                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
432                         }
433
434                         data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
435
436                         return data_members;
437                 }
438
439                 public override List<DataMemberInfo> GetMembers ()
440                 {
441                         return GetMembers (RuntimeType, XmlName, true);
442                 }
443         }
444
445         internal class CollectionTypeMap : SerializationMap
446         {
447                 Type element_type;
448                 QName element_qname;
449
450                 public CollectionTypeMap (
451                         Type type, Type elementType,
452                         QName qname, KnownTypeCollection knownTypes)
453                         : base (type, qname, knownTypes)
454                 {
455                         element_type = elementType;
456                         element_qname = KnownTypes.GetQName (element_type);
457                 }
458
459                 public override void Serialize (object graph,
460                         XmlFormatterSerializer serializer)
461                 {
462                         string ns = element_qname.Namespace;
463                         if (ns == KnownTypeCollection.MSSimpleNamespace)
464                                 ns = KnownTypeCollection.MSArraysNamespace;
465
466                         foreach (object o in (IEnumerable) graph) {
467                                 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, ns);
468                                 serializer.Serialize (element_type, o);
469                                 serializer.WriteEndElement ();
470                         }
471                 }
472
473         public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
474         {
475             object instance;
476             if (RuntimeType.IsArray)
477                 instance = new ArrayList ();
478             else
479                 instance = Activator.CreateInstance (RuntimeType, true);
480             int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
481             while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
482                 object elem = deserializer.Deserialize (element_type, reader);
483                 if (instance is IList)
484                     ((IList)instance).Add (elem);
485                 else
486                     throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
487             }
488             if (RuntimeType.IsArray)
489                 return ((ArrayList)instance).ToArray (element_type);
490             return instance;
491         }
492
493                 public override List<DataMemberInfo> GetMembers ()
494                 {
495                         //Shouldn't come here at all!
496                         throw new NotImplementedException ();
497                 }
498                 
499 #if !NET_2_1
500                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
501                 {
502                         if (generated_schema_types.ContainsKey (XmlName))
503                                 return null;
504
505                         if (generated_schema_types.ContainsKey (XmlName))
506                                 return generated_schema_types [XmlName];
507
508                         QName element_qname = GetQualifiedName (element_type);
509
510                         XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
511                         complex_type.Name = XmlName.Name;
512
513                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
514                         XmlSchemaElement element = new XmlSchemaElement ();
515
516                         element.MinOccurs = 0;
517                         element.MaxOccursString = "unbounded";
518                         element.Name = element_qname.Name;
519
520                         KnownTypes.Add (element_type);
521                         SerializationMap map = KnownTypes.FindUserMap (element_type);
522                         if (map != null) {// non-primitive type
523                                 map.GetSchemaType (schemas, generated_schema_types);
524                                 element.IsNillable = true;
525                         }
526
527                         element.SchemaTypeName = element_qname;
528
529                         sequence.Items.Add (element);
530                         complex_type.Particle = sequence;
531
532                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
533                         schema.Items.Add (complex_type);
534                         schema.Items.Add (GetSchemaElement (XmlName, complex_type));
535                         schemas.Reprocess (schema);
536
537                         generated_schema_types [XmlName] = complex_type;
538
539                         return complex_type;
540                 }
541 #endif
542         }
543
544         internal class SharedTypeMap : SerializationMap
545         {
546                 public SharedTypeMap (
547                         Type type, QName qname, KnownTypeCollection knownTypes)
548                         : base (type, qname, knownTypes)
549                 {
550                         Members = GetMembers (type, XmlName, false);
551                 }
552
553                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
554                 {
555                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
556                         int order = 0;
557                         BindingFlags flags = AllInstanceFlags;
558                         if (declared_only)
559                                 flags |= BindingFlags.DeclaredOnly;
560                         
561                         foreach (FieldInfo fi in type.GetFields (flags)) {
562                                 if (fi.GetCustomAttributes (
563                                         typeof (NonSerializedAttribute),
564                                         false).Length > 0)
565                                         continue;
566
567                                 if (fi.IsInitOnly)
568                                         throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
569                                 DataMemberAttribute dma = new DataMemberAttribute ();
570                                 dma.Order = order++;
571                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
572                         }
573
574                         return data_members;
575                 }
576
577                 public override List<DataMemberInfo> GetMembers ()
578                 {
579                         return GetMembers (RuntimeType, XmlName, true);
580                 }
581         }
582
583         internal class EnumMap : SerializationMap
584         {
585                 List<EnumMemberInfo> enum_members;
586
587                 public EnumMap (
588                         Type type, QName qname, KnownTypeCollection knownTypes)
589                         : base (type, qname, knownTypes)
590                 {
591                         bool has_dc = false;
592                         object [] atts = RuntimeType.GetCustomAttributes (
593                                 typeof (DataContractAttribute), false);
594                         if (atts.Length != 0)
595                                 has_dc = true;
596
597                         enum_members = new List<EnumMemberInfo> ();
598                         BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
599                         
600                         foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
601                                 string name = fi.Name;
602                                 if (has_dc) {
603                                         EnumMemberAttribute ema =
604                                                 GetEnumMemberAttribute (fi);
605                                         if (ema == null)
606                                                 continue;
607
608                                         if (ema.Value != null)
609                                                 name = ema.Value;
610                                 }
611
612                                 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
613                         }
614                 }
615
616                 private EnumMemberAttribute GetEnumMemberAttribute (
617                         MemberInfo mi)
618                 {
619                         object [] atts = mi.GetCustomAttributes (
620                                 typeof (EnumMemberAttribute), false);
621                         if (atts.Length == 0)
622                                 return null;
623                         return (EnumMemberAttribute) atts [0];
624                 }
625
626 #if !NET_2_1
627                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
628                 {
629                         if (generated_schema_types.ContainsKey (XmlName))
630                                 return generated_schema_types [XmlName];
631
632                         XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();
633                         simpleType.Name = XmlName.Name;
634
635                         XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();
636                         simpleType.Content = simpleRestriction;
637                         simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", XmlSchema.Namespace);
638
639                         foreach (EnumMemberInfo emi in enum_members) {
640                                 XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();
641                                 e.Value = emi.XmlName;
642                                 simpleRestriction.Facets.Add (e);
643                         }
644
645                         generated_schema_types [XmlName] = simpleType;
646                         
647                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
648                         XmlSchemaElement element = GetSchemaElement (XmlName, simpleType);
649                         element.IsNillable = true;
650
651                         schema.Items.Add (simpleType);
652                         schema.Items.Add (element);
653
654                         return simpleType;
655                 }
656 #endif
657
658                 public override void Serialize (object graph,
659                         XmlFormatterSerializer serializer)
660                 {
661                         foreach (EnumMemberInfo emi in enum_members) {
662                                 if (Enum.Equals (emi.Value, graph)) {
663                                         serializer.Writer.WriteString (emi.XmlName);
664                                         return;
665                                 }
666                         }
667
668                         throw new SerializationException (String.Format (
669                                 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
670                 }
671
672                 public override object DeserializeContent (XmlReader reader,
673                         XmlFormatterDeserializer deserializer)
674                 {
675                         string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
676
677                         if (value != String.Empty) {
678                                 foreach (EnumMemberInfo emi in enum_members)
679                                         if (emi.XmlName == value)
680                                                 return emi.Value;
681                         }
682
683                         throw new SerializationException (String.Format (
684                                 "Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
685                 }
686         }
687
688         internal struct EnumMemberInfo
689         {
690                 public readonly string XmlName;
691                 public readonly object Value;
692
693                 public EnumMemberInfo (string name, object value)
694                 {
695                         XmlName = name;
696                         Value = value;
697                 }
698         }
699
700         internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
701         {
702                 public readonly int Order;
703                 public readonly bool IsRequired;
704                 public readonly string XmlName;
705                 public readonly MemberInfo Member;
706                 public readonly string XmlNamespace;
707                 public readonly string XmlRootNamespace;
708                 public readonly Type MemberType;
709
710                 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
711                 {
712                         if (dma == null)
713                                 throw new ArgumentNullException ("dma");
714                         Order = dma.Order;
715                         Member = member;
716                         IsRequired = dma.IsRequired;
717                         XmlName = dma.Name != null ? dma.Name : member.Name;
718                         XmlNamespace = ns;
719                         XmlRootNamespace = rootNamespce;
720                         if (Member is FieldInfo)
721                                 MemberType = ((FieldInfo) Member).FieldType;
722                         else
723                                 MemberType = ((PropertyInfo) Member).PropertyType;
724                 }
725
726                 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
727                         , IComparer // see bug #76361
728                 {
729                         public static readonly DataMemberInfoComparer Instance
730                                 = new DataMemberInfoComparer ();
731
732                         private DataMemberInfoComparer () {}
733
734                         public int Compare (object o1, object o2)
735                         {
736                                 return Compare ((DataMemberInfo) o1,
737                                         (DataMemberInfo) o2);
738                         }
739
740                         public int Compare (DataMemberInfo d1, DataMemberInfo d2)
741                         {
742                                 if (d1.Order == -1 || d2.Order == -1)
743                                         return String.CompareOrdinal (d1.XmlName, d2.XmlName);
744
745                                 return d1.Order - d2.Order;
746                         }
747                 }
748         }
749 }
750 #endif