2009-05-26 Atsushi Enomoto <atsushi@ximian.com>
[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.Linq;
38 using System.Reflection;
39 using System.Xml;
40 using System.Xml.Schema;
41 using System.Xml.Serialization;
42
43 using QName = System.Xml.XmlQualifiedName;
44
45 namespace System.Runtime.Serialization
46 {
47 /*
48         XmlFormatter implementation design inference:
49
50         type definitions:
51         - No XML Schema types are directly used. There are some maps from
52           xs:blahType to ms:blahType where the namespaceURI for prefix "ms" is
53           "http://schemas.microsoft.com/2003/10/Serialization/" .
54
55         serializable types:
56         - An object being serialized 1) must be of type System.Object, or
57           2) must be null, or 3) must have either a [DataContract] attribute
58           or a [Serializable] attribute to be serializable.
59         - When the object is either of type System.Object or null, then the
60           XML type is "anyType".
61         - When the object is [Serializable], then the runtime-serialization
62           compatible object graph is written.
63         - Otherwise the serialization is based on contract attributes.
64           ([Serializable] takes precedence).
65
66         type derivation:
67         - For type A to be serializable, the base type B of A must be
68           serializable.
69         - If a type which is [Serializable] and whose base type has a
70           [DataContract], then for base type members [DataContract] is taken.
71         - It is vice versa i.e. if the base type is [Serializable] and the
72           derived type has a [DataContract], then [Serializable] takes place
73           for base members.
74
75         known type collection:
76         - It internally manages mapping store keyed by contract QNames.
77           KnownTypeCollection.Add() checks if the same QName contract already
78           exists (and raises InvalidOperationException if required).
79
80 */
81         internal abstract class SerializationMap
82         {
83                 public const BindingFlags AllInstanceFlags =
84                         BindingFlags.Public | BindingFlags.NonPublic |
85                         BindingFlags.Instance;
86
87                 public readonly KnownTypeCollection KnownTypes;
88                 public readonly Type RuntimeType;
89                 public bool IsReference; // new in 3.5 SP1
90                 public List<DataMemberInfo> Members;
91 #if !NET_2_1
92                 XmlSchemaSet schema_set;
93 #endif
94  
95                 //FIXME FIXME
96                 Dictionary<Type, QName> qname_table = new Dictionary<Type, QName> ();
97
98                 protected SerializationMap (
99                         Type type, QName qname, KnownTypeCollection knownTypes)
100                 {
101                         KnownTypes = knownTypes;
102                         RuntimeType = type;
103                         if (qname.Namespace == String.Empty)
104                                 qname = new QName (qname.Name,
105                                         "http://schemas.datacontract.org/2004/07/" + type.Namespace);
106
107                         XmlName = qname;
108                         Members = new List<DataMemberInfo> ();
109                 }
110
111                 public virtual bool OutputXsiType {
112                         get { return true; }
113                 }
114
115                 public QName XmlName { get; set; }
116
117                 public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
118                 {
119                         object [] atts = type.GetCustomAttributes (
120                                 typeof (CollectionDataContractAttribute), false);
121                         return atts.Length == 0 ? null : (CollectionDataContractAttribute) atts [0];
122                 }
123
124                 public DataMemberAttribute GetDataMemberAttribute (
125                         MemberInfo mi)
126                 {
127                         object [] atts = mi.GetCustomAttributes (
128                                 typeof (DataMemberAttribute), false);
129                         if (atts.Length == 0)
130                                 return null;
131                         return (DataMemberAttribute) atts [0];
132                 }
133
134                 bool IsPrimitive (Type type)
135                 {
136                         return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
137                 }
138
139 #if !NET_2_1
140                 /* Returns the XmlSchemaType AND adds it to @schemas */
141                 public virtual XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
142                 {
143                         if (IsPrimitive (RuntimeType))
144                                 return null;
145
146                         if (generated_schema_types.ContainsKey (XmlName)) // Caching  
147                                 return generated_schema_types [XmlName] as XmlSchemaType;
148
149                         XmlSchemaComplexType complex_type = null;
150
151                         complex_type = new XmlSchemaComplexType ();
152                         complex_type.Name = XmlName.Name;
153                         generated_schema_types [XmlName] = complex_type;
154
155                         if (RuntimeType.BaseType == typeof (object)) {
156                                 complex_type.Particle = GetSequence (schemas, generated_schema_types);
157                         } else {
158                                 //Has a non-System.Object base class
159                                 XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension ();
160                                 XmlSchemaComplexContent content = new XmlSchemaComplexContent ();
161
162                                 complex_type.ContentModel = content;
163                                 content.Content = extension;
164
165                                 KnownTypes.Add (RuntimeType.BaseType);
166                                 SerializationMap map = KnownTypes.FindUserMap (RuntimeType.BaseType);
167                                 //FIXME: map == null ?
168                                 map.GetSchemaType (schemas, generated_schema_types);
169
170                                 extension.Particle = GetSequence (schemas, generated_schema_types);
171                                 extension.BaseTypeName = GetQualifiedName (RuntimeType.BaseType);
172                         }
173                         
174                         XmlSchemaElement schemaElement = GetSchemaElement (XmlName, complex_type);
175                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
176                         schema.Items.Add (complex_type);
177                         schema.Items.Add (schemaElement);
178                         schemas.Reprocess (schema);
179
180                         return complex_type;
181                 }
182
183                 /* Returns the <xs:sequence> for the data members */
184                 XmlSchemaSequence GetSequence (XmlSchemaSet schemas,
185                                 Dictionary<QName, XmlSchemaType> generated_schema_types)
186                 {
187                         List<DataMemberInfo> members = GetMembers ();
188
189                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
190                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
191                         foreach (DataMemberInfo dmi in members) {
192                                 // delegates are not supported.
193                                 if (!dmi.MemberType.IsAbstract && typeof (System.Delegate).IsAssignableFrom (dmi.MemberType))
194                                         continue;
195
196                                 XmlSchemaElement element = new XmlSchemaElement ();
197                                 element.Name = dmi.XmlName;
198
199                                 KnownTypes.Add (dmi.MemberType);
200                                 SerializationMap map = KnownTypes.FindUserMap (dmi.MemberType);
201                                 if (map != null) {
202                                         XmlSchemaType schema_type = map.GetSchemaType (schemas, generated_schema_types);
203                                         if (schema_type is XmlSchemaComplexType)
204                                                 element.IsNillable = true;
205                                 } else {
206                                         //Primitive type
207                                         if (dmi.MemberType == typeof (string))
208                                                 element.IsNillable = true;
209                                 }
210
211                                 element.MinOccurs = 0;
212
213                                 element.SchemaTypeName = GetQualifiedName (dmi.MemberType);
214                                 AddImport (schema, element.SchemaTypeName.Namespace);
215
216                                 sequence.Items.Add (element);
217                         }
218
219                         schemas.Reprocess (schema);
220                         return sequence;
221                 }
222
223                 //FIXME: Replace with a dictionary ?
224                 void AddImport (XmlSchema schema, string ns)
225                 {
226                         if (ns == XmlSchema.Namespace || schema.TargetNamespace == ns)
227                                 return;
228
229                         foreach (XmlSchemaObject o in schema.Includes) {
230                                 XmlSchemaImport import = o as XmlSchemaImport;
231                                 if (import == null)
232                                         continue;
233                                 if (import.Namespace == ns)
234                                         return;
235                         }
236
237                         XmlSchemaImport imp = new XmlSchemaImport ();
238                         imp.Namespace = ns;
239                         schema.Includes.Add (imp);
240                 }
241 #endif
242
243                 //Returns list of data members for this type ONLY
244                 public virtual List<DataMemberInfo> GetMembers ()
245                 {
246                         throw new NotImplementedException (String.Format ("Implement me for {0}", this));
247                 }
248
249 #if !NET_2_1
250                 protected XmlSchemaElement GetSchemaElement (QName qname, XmlSchemaType schemaType)
251                 {
252                         XmlSchemaElement schemaElement = new XmlSchemaElement ();
253                         schemaElement.Name = qname.Name;
254                         schemaElement.SchemaTypeName = qname;
255
256                         if (schemaType is XmlSchemaComplexType)
257                                 schemaElement.IsNillable = true;
258
259                         return schemaElement;
260                 }
261
262                 protected XmlSchema GetSchema (XmlSchemaSet schemas, string ns)
263                 {
264                         ICollection colln = schemas.Schemas (ns);
265                         if (colln.Count > 0) {
266                                 if (colln.Count > 1)
267                                         throw new Exception (String.Format (
268                                                 "More than 1 schema for namespace '{0}' found.", ns));
269                                 foreach (object o in colln)
270                                         //return colln [0]
271                                         return (o as XmlSchema);
272                         }
273
274                         XmlSchema schema = new XmlSchema ();
275                         schema.TargetNamespace = ns;
276                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
277                         schemas.Add (schema);
278
279                         return schema;
280                 }
281
282                 protected XmlQualifiedName GetQualifiedName (Type type)
283                 {
284                         if (qname_table.ContainsKey (type))
285                                 return qname_table [type];
286
287                         QName qname = KnownTypes.GetQName (type);
288                         if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
289                                 qname = new QName (qname.Name, XmlSchema.Namespace);
290
291                         qname_table [type] = qname;
292                         return qname;
293                 }
294 #endif
295
296                 public virtual void Serialize (object graph,
297                         XmlFormatterSerializer serializer)
298                 {
299                         string label = null;
300                         if (IsReference) {
301                                 label = (string) serializer.References [graph];
302                                 if (label != null) {
303                                         serializer.Writer.WriteAttributeString ("z", "Ref", KnownTypeCollection.MSSimpleNamespace, label);
304                                         return;
305                                 }
306                                 label = "i" + (serializer.References.Count + 1);
307                                 serializer.References.Add (graph, label);
308                         }
309                         else if (serializer.SerializingObjects.Contains (graph))
310                                 throw new SerializationException (String.Format ("Circular reference of an object in the object graph was found: '{0}' of type {1}", graph, graph.GetType ()));
311                         serializer.SerializingObjects.Add (graph);
312
313                         if (label != null)
314                                 serializer.Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
315
316                         SerializeNonReference (graph, serializer);
317
318                         serializer.SerializingObjects.Remove (graph);
319                 }
320
321                 public virtual void SerializeNonReference (object graph,
322                         XmlFormatterSerializer serializer)
323                 {
324                         foreach (DataMemberInfo dmi in Members) {
325                                 FieldInfo fi = dmi.Member as FieldInfo;
326                                 PropertyInfo pi = fi == null ?
327                                         (PropertyInfo) dmi.Member : null;
328                                 Type type = fi != null ?
329                                         fi.FieldType : pi.PropertyType;
330                                 object value = fi != null ?
331                                         fi.GetValue (graph) :
332                                         pi.GetValue (graph, null);
333
334                                 serializer.WriteStartElement (dmi.XmlName, dmi.XmlRootNamespace, dmi.XmlNamespace);
335                                 serializer.Serialize (type, value);
336                                 serializer.WriteEndElement ();
337                         }
338                 }
339                 
340                 /* Deserialize non-primitive types */
341
342                 // This is sort of hack. The argument reader already moved ahead of
343                 // the actual empty element.It's just for historical consistency.
344                 public virtual object DeserializeEmptyContent (XmlReader reader,
345                         XmlFormatterDeserializer deserializer)
346                 {
347                         return DeserializeContent (reader, deserializer);
348                 }
349
350                 public virtual object DeserializeContent (XmlReader reader,
351                         XmlFormatterDeserializer deserializer)
352                 {
353                         object instance = FormatterServices.GetUninitializedObject (RuntimeType);
354                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
355                         bool [] filled = new bool [Members.Count];
356                         int memberInd = -1;
357                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
358                                 DataMemberInfo dmi = null;
359                                 int i = 0;
360                                 for (; i < Members.Count; i++) { // unordered
361                                         if (Members [i].Order >= 0)
362                                                 break;
363                                         if (reader.LocalName == Members [i].XmlName &&
364                                                 reader.NamespaceURI == Members [i].XmlRootNamespace) {
365                                                 memberInd = i;
366                                                 dmi = Members [i];
367                                                 break;
368                                         }
369                                 }
370                                 for (; i < Members.Count; i++) { // ordered
371                                         if (dmi != null)
372                                                 break;
373                                         if (reader.LocalName == Members [i].XmlName &&
374                                                 reader.NamespaceURI == Members [i].XmlRootNamespace) {
375                                                 memberInd = i;
376                                                 dmi = Members [i];
377                                                 break;
378                                         }
379                                 }
380
381                                 if (dmi == null) {
382                                         reader.Skip ();
383                                         continue;
384                                 }
385                                 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
386                                 filled [memberInd] = true;
387                         }
388                         for (int i = 0; i < Members.Count; i++)
389                                 if (!filled [i] && Members [i].IsRequired)
390                                         throw MissingRequiredMember (Members [i], reader);
391
392                         return instance;
393                 }
394
395                 // For now it could be private.
396                 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
397                 {
398                         return new ArgumentException (String.Format ("Data contract member {0} is required, but missing in the input XML.", new QName (dmi.XmlName, dmi.XmlNamespace)));
399                 }
400
401                 // For now it could be private.
402                 protected void SetValue (DataMemberInfo dmi, object obj, object value)
403                 {
404                         if (dmi.Member is PropertyInfo)
405                                 ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
406                         else
407                                 ((FieldInfo) dmi.Member).SetValue (obj, value);
408                 }
409
410                 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
411                 {
412                         KnownTypes.Add (type);
413                         QName qname = KnownTypes.GetQName (type);
414                         string rootNamespace = KnownTypes.GetQName (mi.DeclaringType).Namespace;
415                         if (KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null)
416                                 return new DataMemberInfo (mi, dma, rootNamespace, null);
417                         else
418                                 return new DataMemberInfo (mi, dma, rootNamespace, qname.Namespace);
419                 }
420         }
421
422         internal class XmlSerializableMap : SerializationMap
423         {
424                 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
425                         : base (type, qname, knownTypes)
426                 {
427                 }
428
429                 public override void Serialize (object graph, XmlFormatterSerializer serializer)
430                 {
431                         IXmlSerializable ixs = graph as IXmlSerializable;
432                         if (ixs == null)
433                                 //FIXME: Throw what exception here?
434                                 throw new SerializationException ();
435
436                         ixs.WriteXml (serializer.Writer);
437                 }
438
439 #if !NET_2_1
440                 // FIXME: verify return value sanity.
441                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
442                 {
443                         return null;
444                 }
445 #endif
446         }
447
448         internal class SharedContractMap : SerializationMap
449         {
450                 public SharedContractMap (
451                         Type type, QName qname, KnownTypeCollection knownTypes)
452                         : base (type, qname, knownTypes)
453                 {
454                         Type baseType = type;
455                         List <DataMemberInfo> members = new List <DataMemberInfo> ();
456                         object [] atts = type.GetCustomAttributes (
457                                 typeof (DataContractAttribute), false);
458                         IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
459
460                         while (baseType != null) {
461                                 QName bqname = knownTypes.GetQName (baseType);
462                                         
463                                 members = GetMembers (baseType, bqname, true);
464                                 Members.InsertRange (0, members);
465                                 members.Clear ();
466
467                                 baseType = baseType.BaseType;
468                         }
469
470 //                      Members.Sort (delegate (
471 //                              DataMemberInfo d1, DataMemberInfo d2) {
472 //                                      return d1.Order - d2.Order;
473 //                              });
474                 }
475
476                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
477                 {
478                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
479                         BindingFlags flags = AllInstanceFlags;
480                         if (declared_only)
481                                 flags |= BindingFlags.DeclaredOnly;
482
483                         foreach (PropertyInfo pi in type.GetProperties (flags)) {
484                                 DataMemberAttribute dma =
485                                         GetDataMemberAttribute (pi);
486                                 if (dma == null)
487                                         continue;
488                                 KnownTypes.TryRegister (pi.PropertyType);
489                                 var map = KnownTypes.FindUserMap (pi.PropertyType);
490                                 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
491                                         throw new InvalidDataContractException (String.Format (
492                                                         "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
493                                 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType));
494                         }
495
496                         foreach (FieldInfo fi in type.GetFields (flags)) {
497                                 DataMemberAttribute dma =
498                                         GetDataMemberAttribute (fi);
499                                 if (dma == null)
500                                         continue;
501                                 if (fi.IsInitOnly)
502                                         throw new InvalidDataContractException (String.Format (
503                                                         "DataMember field {0} must not be read-only.", fi));
504                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
505                         }
506
507                         data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
508
509                         return data_members;
510                 }
511
512                 public override List<DataMemberInfo> GetMembers ()
513                 {
514                         return GetMembers (RuntimeType, XmlName, true);
515                 }
516         }
517
518         internal class DefaultTypeMap : SerializationMap
519         {
520                 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
521                         : base (type, KnownTypeCollection.GetContractQName (type, null, null), knownTypes)
522                 {
523                         Members.AddRange (GetDefaultMembers ());
524                 }
525
526                 List<DataMemberInfo> GetDefaultMembers ()
527                 {
528                         var l = new List<DataMemberInfo> ();
529                         foreach (var mi in RuntimeType.GetMembers ()) {
530                                 Type mt = null;
531                                 FieldInfo fi = mi as FieldInfo;
532                                 mt = fi == null ? null : fi.FieldType;
533                                 PropertyInfo pi = mi as PropertyInfo;
534                                 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
535                                         mt = pi.PropertyType;
536                                 if (mt == null)
537                                         continue;
538                                 l.Add (new DataMemberInfo (mi, new DataMemberAttribute (), null, null));
539                         }
540                         return l;
541                 }
542         }
543
544         // FIXME: it still needs to consider ItemName/KeyName/ValueName
545         // (especially Dictionary collection is not likely considered yet.)
546         internal class CollectionContractTypeMap : CollectionTypeMap
547         {
548                 public CollectionContractTypeMap (
549                         Type type, CollectionDataContractAttribute a, Type elementType,
550                         QName qname, KnownTypeCollection knownTypes)
551                         : base (type, elementType, qname, knownTypes)
552                 {
553                         IsReference = a.IsReference;
554                 }
555
556                 internal override string CurrentNamespace {
557                         get { return XmlName.Namespace; }
558                 }
559         }
560
561         internal interface ICollectionTypeMap
562         {
563         }
564
565         internal class CollectionTypeMap : SerializationMap, ICollectionTypeMap
566         {
567                 Type element_type;
568                 internal QName element_qname;
569                 MethodInfo add_method;
570
571                 public CollectionTypeMap (
572                         Type type, Type elementType,
573                         QName qname, KnownTypeCollection knownTypes)
574                         : base (type, qname, knownTypes)
575                 {
576                         element_type = elementType;
577                         element_qname = KnownTypes.GetQName (element_type);
578                         var icoll = RuntimeType.GetInterfaces ().FirstOrDefault (
579                                 iface => iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>));
580                         if (icoll != null) {
581                                 if (RuntimeType.IsInterface) {
582                                         add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
583                                 } else {
584                                         var imap = RuntimeType.GetInterfaceMap (icoll);
585                                         for (int i = 0; i < imap.InterfaceMethods.Length; i++)
586                                                 if (imap.InterfaceMethods [i].Name == "Add") {
587                                                         add_method = imap.TargetMethods [i];
588                                                         break;
589                                                 }
590                                         if (add_method == null)
591                                                 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
592                                 }
593                         }
594                 }
595
596                 public override bool OutputXsiType {
597                         get { return false; }
598                 }
599
600                 internal virtual string CurrentNamespace {
601                         get {
602                                 string ns = element_qname.Namespace;
603                                 if (ns == KnownTypeCollection.MSSimpleNamespace)
604                                         ns = KnownTypeCollection.MSArraysNamespace;
605                                 return ns;
606                         }
607                 }
608
609                 public override void SerializeNonReference (object graph,
610                         XmlFormatterSerializer serializer)
611                 {
612
613                         foreach (object o in (IEnumerable) graph) {
614                                 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
615                                 serializer.Serialize (element_type, o);
616                                 serializer.WriteEndElement ();
617                         }
618                 }
619
620                 object CreateInstance ()
621                 {
622                         if (RuntimeType.IsArray)
623                                 return new ArrayList ();
624                         else
625 #if NET_2_1 // FIXME: is it fine?
626                                 return Activator.CreateInstance (RuntimeType);
627 #else
628                                 return Activator.CreateInstance (RuntimeType, true);
629 #endif
630                 }
631
632                 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
633                 {
634                         return CreateInstance ();
635                 }
636
637                 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
638                 {
639                         object instance = CreateInstance ();
640                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
641                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
642                                 object elem = deserializer.Deserialize (element_type, reader);
643                                 if (instance is IList)
644                                         ((IList)instance).Add (elem);
645                                 else if (add_method != null)
646                                         add_method.Invoke (instance, new object [] {elem});
647                                 else
648                                         throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
649                         }
650                         if (RuntimeType.IsArray)
651                                 return ((ArrayList)instance).ToArray (element_type);
652                         return instance;
653                 }
654
655                 public override List<DataMemberInfo> GetMembers ()
656                 {
657                         //Shouldn't come here at all!
658                         throw new NotImplementedException ();
659                 }
660                 
661 #if !NET_2_1
662                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
663                 {
664                         if (generated_schema_types.ContainsKey (XmlName))
665                                 return null;
666
667                         if (generated_schema_types.ContainsKey (XmlName))
668                                 return generated_schema_types [XmlName];
669
670                         QName element_qname = GetQualifiedName (element_type);
671
672                         XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
673                         complex_type.Name = XmlName.Name;
674
675                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
676                         XmlSchemaElement element = new XmlSchemaElement ();
677
678                         element.MinOccurs = 0;
679                         element.MaxOccursString = "unbounded";
680                         element.Name = element_qname.Name;
681
682                         KnownTypes.Add (element_type);
683                         SerializationMap map = KnownTypes.FindUserMap (element_type);
684                         if (map != null) {// non-primitive type
685                                 map.GetSchemaType (schemas, generated_schema_types);
686                                 element.IsNillable = true;
687                         }
688
689                         element.SchemaTypeName = element_qname;
690
691                         sequence.Items.Add (element);
692                         complex_type.Particle = sequence;
693
694                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
695                         schema.Items.Add (complex_type);
696                         schema.Items.Add (GetSchemaElement (XmlName, complex_type));
697                         schemas.Reprocess (schema);
698
699                         generated_schema_types [XmlName] = complex_type;
700
701                         return complex_type;
702                 }
703 #endif
704         }
705
706         internal class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
707         {
708                 Type key_type, value_type;
709                 QName dict_qname, item_qname, key_qname, value_qname;
710                 MethodInfo add_method;
711                 CollectionDataContractAttribute a;
712
713                 public DictionaryTypeMap (
714                         Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
715                         : base (type, QName.Empty, knownTypes)
716                 {
717                         this.a = a;
718
719                         key_type = typeof (object);
720                         value_type = typeof (object);
721
722                         var idic = RuntimeType.GetInterfaces ().FirstOrDefault (
723                                 iface => iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>));
724                         if (idic != null) {
725                                 var imap = RuntimeType.GetInterfaceMap (idic);
726                                 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
727                                         if (imap.InterfaceMethods [i].Name == "Add") {
728                                                 add_method = imap.TargetMethods [i];
729                                                 break;
730                                         }
731                                 var argtypes = idic.GetGenericArguments();
732                                 key_type = argtypes [0];
733                                 value_type = argtypes [1];
734                                 if (add_method == null)
735                                         add_method = type.GetMethod ("Add", argtypes);
736                         }
737
738                         XmlName = GetDictionaryQName ();
739                         item_qname = GetItemQName ();
740                         key_qname = GetKeyQName ();
741                         value_qname = GetValueQName ();
742                 }
743
744                 string ContractNamespace {
745                         get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
746                 }
747
748                 public Type KeyType { get { return key_type; } }
749                 public Type ValueType { get { return value_type; } }
750
751                 static readonly QName kvpair_key_qname = new QName ("Key", KnownTypeCollection.MSArraysNamespace);
752                 static readonly QName kvpair_value_qname = new QName ("Value", KnownTypeCollection.MSArraysNamespace);
753
754                 internal virtual QName GetDictionaryQName ()
755                 {
756                         if (a != null && !String.IsNullOrEmpty (a.Name))
757                                 return new QName (a.Name, ContractNamespace);
758                         return new QName ("ArrayOf" + GetItemQName ().Name, KnownTypeCollection.MSArraysNamespace);
759                 }
760
761                 internal virtual QName GetItemQName ()
762                 {
763                         if (a != null && !String.IsNullOrEmpty (a.ItemName))
764                                 return new QName (a.ItemName, ContractNamespace);
765                         return new QName ("KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name, KnownTypeCollection.MSArraysNamespace);
766                 }
767
768                 internal virtual QName GetKeyQName ()
769                 {
770                         if (a != null && !String.IsNullOrEmpty (a.KeyName))
771                                 return new QName (a.KeyName, ContractNamespace);
772                         return kvpair_key_qname;
773                 }
774
775                 internal virtual QName GetValueQName ()
776                 {
777                         if (a != null && !String.IsNullOrEmpty (a.ValueName))
778                                 return new QName (a.ValueName, ContractNamespace);
779                         return kvpair_value_qname;
780                 }
781
782                 internal virtual string CurrentNamespace {
783                         get {
784                                 string ns = item_qname.Namespace;
785                                 if (ns == KnownTypeCollection.MSSimpleNamespace)
786                                         ns = KnownTypeCollection.MSArraysNamespace;
787                                 return ns;
788                         }
789                 }
790
791                 Type pair_type;
792                 PropertyInfo pair_key_property, pair_value_property;
793
794                 public override void SerializeNonReference (object graph,
795                         XmlFormatterSerializer serializer)
796                 {
797                         if (add_method != null) { // generic
798                                 if (pair_type == null) {
799                                         pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
800                                         pair_key_property = pair_type.GetProperty ("Key");
801                                         pair_value_property = pair_type.GetProperty ("Value");
802                                 }
803                                 foreach (object p in (IEnumerable) graph) {
804                                         serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
805                                         serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
806                                         serializer.Serialize (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
807                                         serializer.WriteEndElement ();
808                                         serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
809                                         serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
810                                         serializer.WriteEndElement ();
811                                         serializer.WriteEndElement ();
812                                 }
813                         } else { // non-generic
814                                 foreach (DictionaryEntry p in (IEnumerable) graph) {
815                                         serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
816                                         serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
817                                         serializer.Serialize (key_type, p.Key);
818                                         serializer.WriteEndElement ();
819                                         serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
820                                         serializer.Serialize (value_type, p.Value);
821                                         serializer.WriteEndElement ();
822                                         serializer.WriteEndElement ();
823                                 }
824                         }
825                 }
826
827                 public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
828                 {
829 #if NET_2_1 // FIXME: is it fine?
830                         object instance = Activator.CreateInstance (RuntimeType);
831 #else
832                         object instance = Activator.CreateInstance (RuntimeType, true);
833 #endif
834                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
835                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
836                                 if (reader.IsEmptyElement)
837                                         throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
838                                 // FIXME: sloppy parsing
839                                 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
840                                 reader.MoveToContent ();
841                                 object key = deserializer.Deserialize (key_type, reader);
842                                 reader.MoveToContent ();
843                                 object val = deserializer.Deserialize (value_type, reader);
844                                 reader.ReadEndElement (); // of pair
845
846                                 if (instance is IDictionary)
847                                         ((IDictionary)instance).Add (key, val);
848                                 else if (add_method != null)
849                                         add_method.Invoke (instance, new object [] {key, val});
850                                 else
851                                         throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
852                         }
853                         return instance;
854                 }
855
856                 public override List<DataMemberInfo> GetMembers ()
857                 {
858                         //Shouldn't come here at all!
859                         throw new NotImplementedException ();
860                 }
861                 
862 #if !NET_2_1
863                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
864                 {
865                         throw new NotImplementedException ();
866                 }
867 #endif
868         }
869
870         internal class SharedTypeMap : SerializationMap
871         {
872                 public SharedTypeMap (
873                         Type type, QName qname, KnownTypeCollection knownTypes)
874                         : base (type, qname, knownTypes)
875                 {
876                         Members = GetMembers (type, XmlName, false);
877                 }
878
879                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
880                 {
881                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
882                         int order = 0;
883                         BindingFlags flags = AllInstanceFlags;
884                         if (declared_only)
885                                 flags |= BindingFlags.DeclaredOnly;
886                         
887                         foreach (FieldInfo fi in type.GetFields (flags)) {
888                                 if (fi.GetCustomAttributes (
889                                         typeof (NonSerializedAttribute),
890                                         false).Length > 0)
891                                         continue;
892
893                                 if (fi.IsInitOnly)
894                                         throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
895                                 DataMemberAttribute dma = new DataMemberAttribute ();
896                                 dma.Order = order++;
897                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
898                         }
899
900                         return data_members;
901                 }
902
903                 // Does this make sense? I doubt.
904                 public override List<DataMemberInfo> GetMembers ()
905                 {
906                         return GetMembers (RuntimeType, XmlName, true);
907                 }
908         }
909
910         internal class EnumMap : SerializationMap
911         {
912                 List<EnumMemberInfo> enum_members;
913
914                 public EnumMap (
915                         Type type, QName qname, KnownTypeCollection knownTypes)
916                         : base (type, qname, knownTypes)
917                 {
918                         bool has_dc = false;
919                         object [] atts = RuntimeType.GetCustomAttributes (
920                                 typeof (DataContractAttribute), false);
921                         if (atts.Length != 0)
922                                 has_dc = true;
923
924                         enum_members = new List<EnumMemberInfo> ();
925                         BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
926                         
927                         foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
928                                 string name = fi.Name;
929                                 if (has_dc) {
930                                         EnumMemberAttribute ema =
931                                                 GetEnumMemberAttribute (fi);
932                                         if (ema == null)
933                                                 continue;
934
935                                         if (ema.Value != null)
936                                                 name = ema.Value;
937                                 }
938
939                                 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
940                         }
941                 }
942
943                 private EnumMemberAttribute GetEnumMemberAttribute (
944                         MemberInfo mi)
945                 {
946                         object [] atts = mi.GetCustomAttributes (
947                                 typeof (EnumMemberAttribute), false);
948                         if (atts.Length == 0)
949                                 return null;
950                         return (EnumMemberAttribute) atts [0];
951                 }
952
953 #if !NET_2_1
954                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
955                 {
956                         if (generated_schema_types.ContainsKey (XmlName))
957                                 return generated_schema_types [XmlName];
958
959                         XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();
960                         simpleType.Name = XmlName.Name;
961
962                         XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();
963                         simpleType.Content = simpleRestriction;
964                         simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", XmlSchema.Namespace);
965
966                         foreach (EnumMemberInfo emi in enum_members) {
967                                 XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();
968                                 e.Value = emi.XmlName;
969                                 simpleRestriction.Facets.Add (e);
970                         }
971
972                         generated_schema_types [XmlName] = simpleType;
973                         
974                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
975                         XmlSchemaElement element = GetSchemaElement (XmlName, simpleType);
976                         element.IsNillable = true;
977
978                         schema.Items.Add (simpleType);
979                         schema.Items.Add (element);
980
981                         return simpleType;
982                 }
983 #endif
984
985                 public override void Serialize (object graph,
986                         XmlFormatterSerializer serializer)
987                 {
988                         foreach (EnumMemberInfo emi in enum_members) {
989                                 if (Enum.Equals (emi.Value, graph)) {
990                                         serializer.Writer.WriteString (emi.XmlName);
991                                         return;
992                                 }
993                         }
994
995                         throw new SerializationException (String.Format (
996                                 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
997                 }
998
999                 public override object DeserializeContent (XmlReader reader,
1000                         XmlFormatterDeserializer deserializer)
1001                 {
1002                         string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1003
1004                         if (value != String.Empty) {
1005                                 foreach (EnumMemberInfo emi in enum_members)
1006                                         if (emi.XmlName == value)
1007                                                 return emi.Value;
1008                         }
1009
1010                         throw new SerializationException (String.Format (
1011                                 "Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1012                 }
1013         }
1014
1015         internal struct EnumMemberInfo
1016         {
1017                 public readonly string XmlName;
1018                 public readonly object Value;
1019
1020                 public EnumMemberInfo (string name, object value)
1021                 {
1022                         XmlName = name;
1023                         Value = value;
1024                 }
1025         }
1026
1027         internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1028         {
1029                 public readonly int Order;
1030                 public readonly bool IsRequired;
1031                 public readonly string XmlName;
1032                 public readonly MemberInfo Member;
1033                 public readonly string XmlNamespace;
1034                 public readonly string XmlRootNamespace;
1035                 public readonly Type MemberType;
1036
1037                 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1038                 {
1039                         if (dma == null)
1040                                 throw new ArgumentNullException ("dma");
1041                         Order = dma.Order;
1042                         Member = member;
1043                         IsRequired = dma.IsRequired;
1044                         XmlName = dma.Name != null ? dma.Name : member.Name;
1045                         XmlNamespace = ns;
1046                         XmlRootNamespace = rootNamespce;
1047                         if (Member is FieldInfo)
1048                                 MemberType = ((FieldInfo) Member).FieldType;
1049                         else
1050                                 MemberType = ((PropertyInfo) Member).PropertyType;
1051                 }
1052
1053                 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1054                         , IComparer // see bug #76361
1055                 {
1056                         public static readonly DataMemberInfoComparer Instance
1057                                 = new DataMemberInfoComparer ();
1058
1059                         private DataMemberInfoComparer () {}
1060
1061                         public int Compare (object o1, object o2)
1062                         {
1063                                 return Compare ((DataMemberInfo) o1,
1064                                         (DataMemberInfo) o2);
1065                         }
1066
1067                         public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1068                         {
1069                                 if (d1.Order == -1 || d2.Order == -1)
1070                                         return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1071
1072                                 return d1.Order - d2.Order;
1073                         }
1074                 }
1075         }
1076 }
1077 #endif