New tests.
[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 partial 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 == null)
104                                 qname = new QName (qname.Name,
105                                         KnownTypeCollection.DefaultClrNamespaceBase + 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                 public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
341                 {
342                         bool isEmpty = reader.IsEmptyElement;
343                         reader.ReadStartElement ();
344                         reader.MoveToContent ();
345
346                         object res;
347
348                         if (isEmpty)
349                                 res = DeserializeEmptyContent (reader, deserializer);
350                         else
351                                 res = DeserializeContent (reader, deserializer);
352
353                         reader.MoveToContent ();
354                         if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
355                                 reader.ReadEndElement ();
356                         else if (!isEmpty && reader.NodeType != XmlNodeType.None) {
357                                 var li = reader as IXmlLineInfo;
358                                 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.{4}",
359                                         reader.NodeType,
360                                         reader.Name,
361                                         reader.NamespaceURI,
362                                         RuntimeType.FullName,
363                                         li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : String.Empty));
364                         }
365                         return res;
366                 }
367
368                 // This is sort of hack. The argument reader already moved ahead of
369                 // the actual empty element.It's just for historical consistency.
370                 public virtual object DeserializeEmptyContent (XmlReader reader,
371                         XmlFormatterDeserializer deserializer)
372                 {
373                         return DeserializeContent (reader, deserializer, true);
374                 }
375
376                 public virtual object DeserializeContent (XmlReader reader,
377                         XmlFormatterDeserializer deserializer)
378                 {
379                         return DeserializeContent (reader, deserializer, false);
380                 }
381
382                 object DeserializeContent (XmlReader reader,
383                         XmlFormatterDeserializer deserializer, bool empty)
384                 {
385                         object instance = FormatterServices.GetUninitializedObject (RuntimeType);
386                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
387                         bool [] filled = new bool [Members.Count];
388                         int memberInd = -1, ordered = -1;
389                         while (!empty && reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
390                                 DataMemberInfo dmi = null;
391                                 int i = 0;
392                                 for (; i < Members.Count; i++) { // unordered
393                                         if (Members [i].Order >= 0)
394                                                 break;
395                                         if (reader.LocalName == Members [i].XmlName &&
396                                                 reader.NamespaceURI == Members [i].XmlRootNamespace) {
397                                                 memberInd = i;
398                                                 dmi = Members [i];
399                                                 break;
400                                         }
401                                 }
402                                 for (i = Math.Max (i, ordered); i < Members.Count; i++) { // ordered
403                                         if (dmi != null)
404                                                 break;
405                                         if (reader.LocalName == Members [i].XmlName &&
406                                                 reader.NamespaceURI == Members [i].XmlRootNamespace) {
407                                                 memberInd = i;
408                                                 ordered = i;
409                                                 dmi = Members [i];
410                                                 break;
411                                         }
412                                 }
413
414                                 if (dmi == null) {
415                                         reader.Skip ();
416                                         continue;
417                                 }
418                                 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
419                                 filled [memberInd] = true;
420                                 reader.MoveToContent ();
421                         }
422                         for (int i = 0; i < Members.Count; i++)
423                                 if (!filled [i] && Members [i].IsRequired)
424                                         throw MissingRequiredMember (Members [i], reader);
425
426                         return instance;
427                 }
428
429                 // For now it could be private.
430                 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
431                 {
432                         var li = reader as IXmlLineInfo;
433                         return new ArgumentException (String.Format ("Data contract member {0} for the type {1} is required, but missing in the input XML.{2}",
434                                 new QName (dmi.XmlName, dmi.XmlNamespace),
435                                 RuntimeType,
436                                 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : null));
437                 }
438
439                 // For now it could be private.
440                 protected void SetValue (DataMemberInfo dmi, object obj, object value)
441                 {
442                         try {
443                                 if (dmi.Member is PropertyInfo)
444                                         ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
445                                 else
446                                         ((FieldInfo) dmi.Member).SetValue (obj, value);
447                         } catch (Exception ex) {
448                                 throw new InvalidOperationException (String.Format ("Failed to set value of type {0} for property {1}", value != null ? value.GetType () : null, dmi.Member), ex);
449                         }
450                 }
451
452                 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type type)
453                 {
454                         KnownTypes.Add (type);
455                         QName qname = KnownTypes.GetQName (type);
456                         string rootNamespace = KnownTypes.GetQName (mi.DeclaringType).Namespace;
457                         if (KnownTypeCollection.GetPrimitiveTypeFromName (qname.Name) != null)
458                                 return new DataMemberInfo (mi, dma, rootNamespace, null);
459                         else
460                                 return new DataMemberInfo (mi, dma, rootNamespace, qname.Namespace);
461                 }
462         }
463
464         internal partial class XmlSerializableMap : SerializationMap
465         {
466                 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
467                         : base (type, qname, knownTypes)
468                 {
469                 }
470
471                 public override void Serialize (object graph, XmlFormatterSerializer serializer)
472                 {
473                         IXmlSerializable ixs = graph as IXmlSerializable;
474                         if (ixs == null)
475                                 //FIXME: Throw what exception here?
476                                 throw new SerializationException ();
477
478                         ixs.WriteXml (serializer.Writer);
479                 }
480
481                 public override object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
482                 {
483                         IXmlSerializable ixs = (IXmlSerializable) FormatterServices.GetUninitializedObject (RuntimeType);
484                         ixs.ReadXml (reader);
485                         return ixs;
486                 }
487
488 #if !NET_2_1
489                 // FIXME: verify return value sanity.
490                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
491                 {
492                         return null;
493                 }
494 #endif
495         }
496
497         internal partial class SharedContractMap : SerializationMap
498         {
499                 public SharedContractMap (
500                         Type type, QName qname, KnownTypeCollection knownTypes)
501                         : base (type, qname, knownTypes)
502                 {
503                 }
504
505                 internal void Initialize ()
506                 {
507                         Type baseType = RuntimeType;
508                         List <DataMemberInfo> members = new List <DataMemberInfo> ();
509                         object [] atts = baseType.GetCustomAttributes (
510                                 typeof (DataContractAttribute), false);
511                         IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
512
513                         while (baseType != null) {
514                                 QName bqname = KnownTypes.GetQName (baseType);
515                                         
516                                 members = GetMembers (baseType, bqname, true);
517                                 members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
518                                 Members.InsertRange (0, members);
519                                 members.Clear ();
520
521                                 baseType = baseType.BaseType;
522                         }
523                 }
524
525                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
526                 {
527                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
528                         BindingFlags flags = AllInstanceFlags;
529                         if (declared_only)
530                                 flags |= BindingFlags.DeclaredOnly;
531
532                         foreach (PropertyInfo pi in type.GetProperties (flags)) {
533                                 DataMemberAttribute dma =
534                                         GetDataMemberAttribute (pi);
535                                 if (dma == null)
536                                         continue;
537                                 KnownTypes.TryRegister (pi.PropertyType);
538                                 var map = KnownTypes.FindUserMap (pi.PropertyType);
539                                 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
540                                         throw new InvalidDataContractException (String.Format (
541                                                         "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
542                                 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType));
543                         }
544
545                         foreach (FieldInfo fi in type.GetFields (flags)) {
546                                 DataMemberAttribute dma =
547                                         GetDataMemberAttribute (fi);
548                                 if (dma == null)
549                                         continue;
550                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
551                         }
552
553                         return data_members;
554                 }
555
556                 public override List<DataMemberInfo> GetMembers ()
557                 {
558                         return Members;
559                 }
560         }
561
562         internal partial class DefaultTypeMap : SerializationMap
563         {
564                 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
565                         : base (type, KnownTypeCollection.GetStaticQName (type), knownTypes)
566                 {
567                         Members.AddRange (GetDefaultMembers ());
568                 }
569
570                 List<DataMemberInfo> GetDefaultMembers ()
571                 {
572                         var l = new List<DataMemberInfo> ();
573                         foreach (var mi in RuntimeType.GetMembers ()) {
574                                 Type mt = null;
575                                 FieldInfo fi = mi as FieldInfo;
576                                 mt = fi == null ? null : fi.FieldType;
577                                 PropertyInfo pi = mi as PropertyInfo;
578                                 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
579                                         mt = pi.PropertyType;
580                                 if (mt == null)
581                                         continue;
582                                 if (mi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length != 0)
583                                         continue;
584                                 l.Add (new DataMemberInfo (mi, new DataMemberAttribute (), null, null));
585                         }
586                         l.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
587                         return l;
588                 }
589         }
590
591         // FIXME: it still needs to consider ItemName/KeyName/ValueName
592         // (especially Dictionary collection is not likely considered yet.)
593         internal partial class CollectionContractTypeMap : CollectionTypeMap
594         {
595                 CollectionDataContractAttribute a;
596
597                 public CollectionContractTypeMap (
598                         Type type, CollectionDataContractAttribute a, Type elementType,
599                         QName qname, KnownTypeCollection knownTypes)
600                         : base (type, elementType, qname, knownTypes)
601                 {
602                         this.a = a;
603                         IsReference = a.IsReference;
604                 }
605
606                 internal override string CurrentNamespace {
607                         get { return XmlName.Namespace; }
608                 }
609         }
610
611         internal interface ICollectionTypeMap
612         {
613         }
614
615         internal partial class CollectionTypeMap : SerializationMap, ICollectionTypeMap
616         {
617                 Type element_type;
618                 internal QName element_qname;
619                 MethodInfo add_method;
620
621                 public CollectionTypeMap (
622                         Type type, Type elementType,
623                         QName qname, KnownTypeCollection knownTypes)
624                         : base (type, qname, knownTypes)
625                 {
626                         element_type = elementType;
627                         element_qname = KnownTypes.GetQName (element_type);
628                         var icoll = GetGenericCollectionInterface (RuntimeType);
629                         if (icoll != null) {
630                                 if (RuntimeType.IsInterface) {
631                                         add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
632                                 } else {
633                                         var imap = RuntimeType.GetInterfaceMap (icoll);
634                                         for (int i = 0; i < imap.InterfaceMethods.Length; i++)
635                                                 if (imap.InterfaceMethods [i].Name == "Add") {
636                                                         add_method = imap.TargetMethods [i];
637                                                         break;
638                                                 }
639                                         if (add_method == null)
640                                                 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
641                                 }
642                         }
643                 }
644
645                 static Type GetGenericCollectionInterface (Type type)
646                 {
647                         foreach (var iface in type.GetInterfaces ())
648                                 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (ICollection<>))
649                                         return iface;
650
651                         return null;
652                 }
653
654                 public override bool OutputXsiType {
655                         get { return false; }
656                 }
657
658                 internal virtual string CurrentNamespace {
659                         get {
660                                 string ns = element_qname.Namespace;
661                                 if (ns == KnownTypeCollection.MSSimpleNamespace)
662                                         ns = KnownTypeCollection.MSArraysNamespace;
663                                 return ns;
664                         }
665                 }
666
667                 public override void SerializeNonReference (object graph,
668                         XmlFormatterSerializer serializer)
669                 {
670
671                         foreach (object o in (IEnumerable) graph) {
672                                 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
673                                 serializer.Serialize (element_type, o);
674                                 serializer.WriteEndElement ();
675                         }
676                 }
677
678                 object CreateInstance ()
679                 {
680                         if (RuntimeType.IsArray)
681                                 return new ArrayList ();
682                         if (RuntimeType.IsInterface) {
683                                 var icoll = GetGenericCollectionInterface (RuntimeType);
684                                 if (icoll != null)
685                                         return Activator.CreateInstance (typeof (List<>).MakeGenericType (RuntimeType.GetGenericArguments () [0])); // List<T>
686                                 else // non-generic
687                                         return new ArrayList ();
688                         }
689 #if NET_2_1 // FIXME: is it fine?
690                         return Activator.CreateInstance (RuntimeType);
691 #else
692                         return Activator.CreateInstance (RuntimeType, true);
693 #endif
694                 }
695
696                 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
697                 {
698                         var instance = CreateInstance ();
699                         if (RuntimeType.IsArray)
700                                 return ((ArrayList)instance).ToArray (element_type);
701                         else
702                                 return instance;
703                 }
704
705                 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer)
706                 {
707                         object instance = CreateInstance ();
708                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
709                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
710                                 object elem = deserializer.Deserialize (element_type, reader);
711                                 if (instance is IList)
712                                         ((IList)instance).Add (elem);
713                                 else if (add_method != null)
714                                         add_method.Invoke (instance, new object [] {elem});
715                                 else
716                                         throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
717                                 reader.MoveToContent ();
718                         }
719                         if (RuntimeType.IsArray)
720                                 return ((ArrayList)instance).ToArray (element_type);
721                         return instance;
722                 }
723
724                 public override List<DataMemberInfo> GetMembers ()
725                 {
726                         //Shouldn't come here at all!
727                         throw new NotImplementedException ();
728                 }
729                 
730 #if !NET_2_1
731                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
732                 {
733                         if (generated_schema_types.ContainsKey (XmlName))
734                                 return null;
735
736                         if (generated_schema_types.ContainsKey (XmlName))
737                                 return generated_schema_types [XmlName];
738
739                         QName element_qname = GetQualifiedName (element_type);
740
741                         XmlSchemaComplexType complex_type = new XmlSchemaComplexType ();
742                         complex_type.Name = XmlName.Name;
743
744                         XmlSchemaSequence sequence = new XmlSchemaSequence ();
745                         XmlSchemaElement element = new XmlSchemaElement ();
746
747                         element.MinOccurs = 0;
748                         element.MaxOccursString = "unbounded";
749                         element.Name = element_qname.Name;
750
751                         KnownTypes.Add (element_type);
752                         SerializationMap map = KnownTypes.FindUserMap (element_type);
753                         if (map != null) {// non-primitive type
754                                 map.GetSchemaType (schemas, generated_schema_types);
755                                 element.IsNillable = true;
756                         }
757
758                         element.SchemaTypeName = element_qname;
759
760                         sequence.Items.Add (element);
761                         complex_type.Particle = sequence;
762
763                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
764                         schema.Items.Add (complex_type);
765                         schema.Items.Add (GetSchemaElement (XmlName, complex_type));
766                         schemas.Reprocess (schema);
767
768                         generated_schema_types [XmlName] = complex_type;
769
770                         return complex_type;
771                 }
772 #endif
773         }
774
775         internal partial class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
776         {
777                 Type key_type, value_type;
778                 QName dict_qname, item_qname, key_qname, value_qname;
779                 MethodInfo add_method;
780                 CollectionDataContractAttribute a;
781
782                 public DictionaryTypeMap (
783                         Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
784                         : base (type, QName.Empty, knownTypes)
785                 {
786                         this.a = a;
787
788                         key_type = typeof (object);
789                         value_type = typeof (object);
790
791                         var idic = GetGenericDictionaryInterface (RuntimeType);
792                         if (idic != null) {
793                                 var imap = RuntimeType.GetInterfaceMap (idic);
794                                 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
795                                         if (imap.InterfaceMethods [i].Name == "Add") {
796                                                 add_method = imap.TargetMethods [i];
797                                                 break;
798                                         }
799                                 var argtypes = idic.GetGenericArguments();
800                                 key_type = argtypes [0];
801                                 value_type = argtypes [1];
802                                 if (add_method == null)
803                                         add_method = type.GetMethod ("Add", argtypes);
804                         }
805
806                         XmlName = GetDictionaryQName ();
807                         item_qname = GetItemQName ();
808                         key_qname = GetKeyQName ();
809                         value_qname = GetValueQName ();
810                 }
811
812                 static Type GetGenericDictionaryInterface (Type type)
813                 {
814                         foreach (var iface in type.GetInterfaces ())
815                                 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>))
816                                         return iface;
817
818                         return null;
819                 }
820
821                 string ContractNamespace {
822                         get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
823                 }
824
825                 public Type KeyType { get { return key_type; } }
826                 public Type ValueType { get { return value_type; } }
827
828                 static readonly QName kvpair_key_qname = new QName ("Key", KnownTypeCollection.MSArraysNamespace);
829                 static readonly QName kvpair_value_qname = new QName ("Value", KnownTypeCollection.MSArraysNamespace);
830
831                 internal virtual QName GetDictionaryQName ()
832                 {
833                         if (a != null && !String.IsNullOrEmpty (a.Name))
834                                 return new QName (a.Name, ContractNamespace);
835                         return new QName ("ArrayOf" + GetItemQName ().Name, KnownTypeCollection.MSArraysNamespace);
836                 }
837
838                 internal virtual QName GetItemQName ()
839                 {
840                         if (a != null && !String.IsNullOrEmpty (a.ItemName))
841                                 return new QName (a.ItemName, ContractNamespace);
842                         return new QName ("KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name, KnownTypeCollection.MSArraysNamespace);
843                 }
844
845                 internal virtual QName GetKeyQName ()
846                 {
847                         if (a != null && !String.IsNullOrEmpty (a.KeyName))
848                                 return new QName (a.KeyName, ContractNamespace);
849                         return kvpair_key_qname;
850                 }
851
852                 internal virtual QName GetValueQName ()
853                 {
854                         if (a != null && !String.IsNullOrEmpty (a.ValueName))
855                                 return new QName (a.ValueName, ContractNamespace);
856                         return kvpair_value_qname;
857                 }
858
859                 internal virtual string CurrentNamespace {
860                         get {
861                                 string ns = item_qname.Namespace;
862                                 if (ns == KnownTypeCollection.MSSimpleNamespace)
863                                         ns = KnownTypeCollection.MSArraysNamespace;
864                                 return ns;
865                         }
866                 }
867
868                 Type pair_type;
869                 PropertyInfo pair_key_property, pair_value_property;
870
871                 public override void SerializeNonReference (object graph,
872                         XmlFormatterSerializer serializer)
873                 {
874                         if (add_method != null) { // generic
875                                 if (pair_type == null) {
876                                         pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
877                                         pair_key_property = pair_type.GetProperty ("Key");
878                                         pair_value_property = pair_type.GetProperty ("Value");
879                                 }
880                                 foreach (object p in (IEnumerable) graph) {
881                                         serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
882                                         serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
883                                         serializer.Serialize (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
884                                         serializer.WriteEndElement ();
885                                         serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
886                                         serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
887                                         serializer.WriteEndElement ();
888                                         serializer.WriteEndElement ();
889                                 }
890                         } else { // non-generic
891                                 foreach (DictionaryEntry p in (IEnumerable) graph) {
892                                         serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
893                                         serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
894                                         serializer.Serialize (key_type, p.Key);
895                                         serializer.WriteEndElement ();
896                                         serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
897                                         serializer.Serialize (value_type, p.Value);
898                                         serializer.WriteEndElement ();
899                                         serializer.WriteEndElement ();
900                                 }
901                         }
902                 }
903
904                 object CreateInstance ()
905                 {
906                         if (RuntimeType.IsInterface) {
907                                 if (RuntimeType.IsGenericType && Array.IndexOf (RuntimeType.GetGenericTypeDefinition ().GetInterfaces (), typeof (IDictionary<,>)) >= 0) {
908                                         var gargs = RuntimeType.GetGenericArguments ();
909                                         return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (gargs [0], gargs [1])); // Dictionary<T>
910                                 }
911                                 else // non-generic
912                                         return new Hashtable ();
913                         }
914 #if NET_2_1 // FIXME: is it fine?
915                         return Activator.CreateInstance (RuntimeType);
916 #else
917                         return Activator.CreateInstance (RuntimeType, true);
918 #endif
919                 }
920
921                 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer)
922                 {
923                         return DeserializeContent (reader, deserializer);
924                 }
925
926                 public override object DeserializeContent(XmlReader reader, XmlFormatterDeserializer deserializer)
927                 {
928                         object instance = CreateInstance ();
929                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
930                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
931                                 if (reader.IsEmptyElement)
932                                         throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
933                                 // FIXME: sloppy parsing
934                                 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
935                                 reader.MoveToContent ();
936                                 object key = deserializer.Deserialize (key_type, reader);
937                                 reader.MoveToContent ();
938                                 object val = deserializer.Deserialize (value_type, reader);
939                                 reader.ReadEndElement (); // of pair
940
941                                 if (instance is IDictionary)
942                                         ((IDictionary)instance).Add (key, val);
943                                 else if (add_method != null)
944                                         add_method.Invoke (instance, new object [] {key, val});
945                                 else
946                                         throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
947                         }
948                         return instance;
949                 }
950
951                 public override List<DataMemberInfo> GetMembers ()
952                 {
953                         //Shouldn't come here at all!
954                         throw new NotImplementedException ();
955                 }
956                 
957 #if !NET_2_1
958                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
959                 {
960                         throw new NotImplementedException ();
961                 }
962 #endif
963         }
964
965         internal partial class SharedTypeMap : SerializationMap
966         {
967                 public SharedTypeMap (
968                         Type type, QName qname, KnownTypeCollection knownTypes)
969                         : base (type, qname, knownTypes)
970                 {
971                 }
972
973                 public void Initialize ()
974                 {
975                         Members = GetMembers (RuntimeType, XmlName, false);
976                 }
977
978                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
979                 {
980                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
981                         BindingFlags flags = AllInstanceFlags;
982                         if (declared_only)
983                                 flags |= BindingFlags.DeclaredOnly;
984                         
985                         foreach (FieldInfo fi in type.GetFields (flags)) {
986                                 if (fi.GetCustomAttributes (
987                                         typeof (NonSerializedAttribute),
988                                         false).Length > 0)
989                                         continue;
990
991                                 if (fi.IsInitOnly)
992                                         throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
993                                 DataMemberAttribute dma = new DataMemberAttribute ();
994                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType));
995                         }
996
997                         data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance); // alphabetic order.
998
999                         return data_members;
1000                 }
1001
1002                 // Does this make sense? I doubt.
1003                 public override List<DataMemberInfo> GetMembers ()
1004                 {
1005                         return Members;
1006                         //return GetMembers (RuntimeType, XmlName, true);
1007                 }
1008         }
1009
1010         internal partial class EnumMap : SerializationMap
1011         {
1012                 List<EnumMemberInfo> enum_members;
1013                 bool flag_attr;
1014
1015                 public EnumMap (
1016                         Type type, QName qname, KnownTypeCollection knownTypes)
1017                         : base (type, qname, knownTypes)
1018                 {
1019                         bool has_dc = false;
1020                         object [] atts = RuntimeType.GetCustomAttributes (
1021                                 typeof (DataContractAttribute), false);
1022                         if (atts.Length != 0)
1023                                 has_dc = true;
1024                         flag_attr = type.GetCustomAttributes (typeof (FlagsAttribute), false).Length > 0;
1025
1026                         enum_members = new List<EnumMemberInfo> ();
1027                         BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
1028                         
1029                         foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
1030                                 string name = fi.Name;
1031                                 if (has_dc) {
1032                                         EnumMemberAttribute ema =
1033                                                 GetEnumMemberAttribute (fi);
1034                                         if (ema == null)
1035                                                 continue;
1036
1037                                         if (ema.Value != null)
1038                                                 name = ema.Value;
1039                                 }
1040
1041                                 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
1042                         }
1043                 }
1044
1045                 private EnumMemberAttribute GetEnumMemberAttribute (
1046                         MemberInfo mi)
1047                 {
1048                         object [] atts = mi.GetCustomAttributes (
1049                                 typeof (EnumMemberAttribute), false);
1050                         if (atts.Length == 0)
1051                                 return null;
1052                         return (EnumMemberAttribute) atts [0];
1053                 }
1054
1055 #if !NET_2_1
1056                 public override XmlSchemaType GetSchemaType (XmlSchemaSet schemas, Dictionary<QName, XmlSchemaType> generated_schema_types)
1057                 {
1058                         if (generated_schema_types.ContainsKey (XmlName))
1059                                 return generated_schema_types [XmlName];
1060
1061                         XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();
1062                         simpleType.Name = XmlName.Name;
1063
1064                         XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();
1065                         simpleType.Content = simpleRestriction;
1066                         simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", XmlSchema.Namespace);
1067
1068                         foreach (EnumMemberInfo emi in enum_members) {
1069                                 XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();
1070                                 e.Value = emi.XmlName;
1071                                 simpleRestriction.Facets.Add (e);
1072                         }
1073
1074                         generated_schema_types [XmlName] = simpleType;
1075                         
1076                         XmlSchema schema = GetSchema (schemas, XmlName.Namespace);
1077                         XmlSchemaElement element = GetSchemaElement (XmlName, simpleType);
1078                         element.IsNillable = true;
1079
1080                         schema.Items.Add (simpleType);
1081                         schema.Items.Add (element);
1082
1083                         return simpleType;
1084                 }
1085 #endif
1086
1087                 public override void Serialize (object graph,
1088                         XmlFormatterSerializer serializer)
1089                 {
1090                         foreach (EnumMemberInfo emi in enum_members) {
1091                                 if (Enum.Equals (emi.Value, graph)) {
1092                                         serializer.Writer.WriteString (emi.XmlName);
1093                                         return;
1094                                 }
1095                         }
1096
1097                         throw new SerializationException (String.Format (
1098                                 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
1099                 }
1100
1101                 public override object DeserializeEmptyContent (XmlReader reader,
1102                         XmlFormatterDeserializer deserializer)
1103                 {
1104                         if (!flag_attr)
1105                                 throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
1106                         return Enum.ToObject (RuntimeType, 0);
1107                 }
1108
1109                 public override object DeserializeContent (XmlReader reader,
1110                         XmlFormatterDeserializer deserializer)
1111                 {
1112                         string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1113
1114                         if (value != String.Empty) {
1115                                 foreach (EnumMemberInfo emi in enum_members)
1116                                         if (emi.XmlName == value)
1117                                                 return emi.Value;
1118                         }
1119
1120                         if (!flag_attr)
1121                                 throw new SerializationException (String.Format ("Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1122                         return Enum.ToObject (RuntimeType, 0);
1123                 }
1124         }
1125
1126         internal struct EnumMemberInfo
1127         {
1128                 public readonly string XmlName;
1129                 public readonly object Value;
1130
1131                 public EnumMemberInfo (string name, object value)
1132                 {
1133                         XmlName = name;
1134                         Value = value;
1135                 }
1136         }
1137
1138         internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1139         {
1140                 public readonly int Order;
1141                 public readonly bool IsRequired;
1142                 public readonly string XmlName;
1143                 public readonly MemberInfo Member;
1144                 public readonly string XmlNamespace;
1145                 public readonly string XmlRootNamespace;
1146                 public readonly Type MemberType;
1147
1148                 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1149                 {
1150                         if (dma == null)
1151                                 throw new ArgumentNullException ("dma");
1152                         Order = dma.Order;
1153                         Member = member;
1154                         IsRequired = dma.IsRequired;
1155                         XmlName = dma.Name != null ? dma.Name : member.Name;
1156                         XmlNamespace = ns;
1157                         XmlRootNamespace = rootNamespce;
1158                         if (Member is FieldInfo)
1159                                 MemberType = ((FieldInfo) Member).FieldType;
1160                         else
1161                                 MemberType = ((PropertyInfo) Member).PropertyType;
1162                 }
1163
1164                 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1165                         , IComparer // see bug #76361
1166                 {
1167                         public static readonly DataMemberInfoComparer Instance
1168                                 = new DataMemberInfoComparer ();
1169
1170                         private DataMemberInfoComparer () {}
1171
1172                         public int Compare (object o1, object o2)
1173                         {
1174                                 return Compare ((DataMemberInfo) o1,
1175                                         (DataMemberInfo) o2);
1176                         }
1177
1178                         public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1179                         {
1180                                 if (d1.Order == d2.Order)
1181                                         return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1182
1183                                 return d1.Order - d2.Order;
1184                         }
1185                 }
1186         }
1187 }
1188 #endif