Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.cs
[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                         foreach (var mi in type.GetMethods (AllInstanceFlags)) {
111                                 if (mi.GetCustomAttributes (typeof (OnDeserializingAttribute), false).Length > 0)
112                                         OnDeserializing = mi;
113                                 else if (mi.GetCustomAttributes (typeof (OnDeserializedAttribute), false).Length > 0)
114                                         OnDeserialized = mi;
115                         }
116                 }
117
118                 public MethodInfo OnDeserializing { get; set; }
119                 public MethodInfo OnDeserialized { get; set; }
120
121                 public virtual bool OutputXsiType {
122                         get { return true; }
123                 }
124
125                 public QName XmlName { get; set; }
126
127                 public abstract bool IsContractAllowedType { get; }
128
129                 protected void HandleId (XmlReader reader, XmlFormatterDeserializer deserializer, object instance)
130                 {
131                         HandleId (reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace), deserializer, instance);
132                 }
133                 
134                 protected void HandleId (string id, XmlFormatterDeserializer deserializer, object instance)
135                 {
136                         if (id != null)
137                                 deserializer.References.Add (id, instance);
138                 }
139
140                 public CollectionDataContractAttribute GetCollectionDataContractAttribute (Type type)
141                 {
142                         object [] atts = type.GetCustomAttributes (
143                                 typeof (CollectionDataContractAttribute), false);
144                         return atts.Length == 0 ? null : (CollectionDataContractAttribute) atts [0];
145                 }
146
147                 public DataMemberAttribute GetDataMemberAttribute (
148                         MemberInfo mi)
149                 {
150                         object [] atts = mi.GetCustomAttributes (
151                                 typeof (DataMemberAttribute), false);
152                         if (atts.Length == 0)
153                                 return null;
154                         return (DataMemberAttribute) atts [0];
155                 }
156
157                 bool IsPrimitive (Type type)
158                 {
159                         return (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object));
160                 }
161
162                 //Returns list of data members for this type ONLY
163                 public virtual List<DataMemberInfo> GetMembers ()
164                 {
165                         throw new NotImplementedException (String.Format ("Implement me for {0}", this));
166                 }
167
168 #if !NET_2_1
169                 protected XmlSchemaElement GetSchemaElement (QName qname, XmlSchemaType schemaType)
170                 {
171                         XmlSchemaElement schemaElement = new XmlSchemaElement ();
172                         schemaElement.Name = qname.Name;
173                         schemaElement.SchemaTypeName = qname;
174
175                         if (schemaType is XmlSchemaComplexType)
176                                 schemaElement.IsNillable = true;
177
178                         return schemaElement;
179                 }
180
181                 protected XmlSchema GetSchema (XmlSchemaSet schemas, string ns)
182                 {
183                         ICollection colln = schemas.Schemas (ns);
184                         if (colln.Count > 0) {
185                                 if (colln.Count > 1)
186                                         throw new Exception (String.Format (
187                                                 "More than 1 schema for namespace '{0}' found.", ns));
188                                 foreach (object o in colln)
189                                         //return colln [0]
190                                         return (o as XmlSchema);
191                         }
192
193                         XmlSchema schema = new XmlSchema ();
194                         schema.TargetNamespace = ns;
195                         schema.ElementFormDefault = XmlSchemaForm.Qualified;
196                         schemas.Add (schema);
197
198                         return schema;
199                 }
200
201                 protected XmlQualifiedName GetQualifiedName (Type type)
202                 {
203                         if (qname_table.ContainsKey (type))
204                                 return qname_table [type];
205
206                         QName qname = KnownTypes.GetQName (type);
207                         if (qname.Namespace == KnownTypeCollection.MSSimpleNamespace)
208                                 qname = new QName (qname.Name, XmlSchema.Namespace);
209
210                         qname_table [type] = qname;
211                         return qname;
212                 }
213 #endif
214
215                 public virtual void Serialize (object graph,
216                         XmlFormatterSerializer serializer)
217                 {
218                         string label;
219                         if (serializer.TrySerializeAsReference (IsReference, graph, out label))
220                                 return;
221                         else if (serializer.SerializingObjects.Contains (graph))
222                                 throw new SerializationException (String.Format ("Circular reference of an object in the object graph was found: '{0}' of type {1}", graph, graph.GetType ()));
223                         serializer.SerializingObjects.Add (graph);
224
225                         if (label != null)
226                                 serializer.Writer.WriteAttributeString ("z", "Id", KnownTypeCollection.MSSimpleNamespace, label);
227
228                         SerializeNonReference (graph, serializer);
229
230                         serializer.SerializingObjects.Remove (graph);
231                 }
232
233                 public virtual void SerializeNonReference (object graph,
234                         XmlFormatterSerializer serializer)
235                 {
236                         foreach (DataMemberInfo dmi in Members) {
237                                 FieldInfo fi = dmi.Member as FieldInfo;
238                                 PropertyInfo pi = fi == null ?
239                                         (PropertyInfo) dmi.Member : null;
240                                 Type type = fi != null ?
241                                         fi.FieldType : pi.PropertyType;
242                                 object value = fi != null ?
243                                         fi.GetValue (graph) :
244                                         pi.GetValue (graph, null);
245
246                                 serializer.WriteStartElement (dmi.XmlName, dmi.XmlRootNamespace, dmi.XmlNamespace);
247                                 serializer.Serialize (type, value);
248                                 serializer.WriteEndElement ();
249                         }
250                 }
251
252                 public virtual object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
253                 {
254                         bool isEmpty = reader.IsEmptyElement;
255                         string id = reader.GetAttribute ("Id", KnownTypeCollection.MSSimpleNamespace);
256                         reader.ReadStartElement ();
257                         reader.MoveToContent ();
258
259                         object res;
260
261                         if (isEmpty)
262                                 res = DeserializeEmptyContent (reader, deserializer, id);
263                         else
264                                 res = DeserializeContent (reader, deserializer, id);
265
266                         reader.MoveToContent ();
267                         if (!isEmpty && reader.NodeType == XmlNodeType.EndElement)
268                                 reader.ReadEndElement ();
269                         else if (!isEmpty && !reader.EOF && reader.NodeType != XmlNodeType.EndElement) {
270                                 var li = reader as IXmlLineInfo;
271                                 throw new SerializationException (String.Format ("Deserializing type '{3}'. Expecting state 'EndElement'. Encountered state '{0}' with name '{1}' with namespace '{2}'.{4}",
272                                         reader.NodeType,
273                                         reader.Name,
274                                         reader.NamespaceURI,
275                                         RuntimeType.FullName,
276                                         li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : String.Empty));
277                         }
278                         return res;
279                 }
280
281                 // This is sort of hack. The argument reader already moved ahead of
282                 // the actual empty element.It's just for historical consistency.
283                 public virtual object DeserializeEmptyContent (XmlReader reader,
284                         XmlFormatterDeserializer deserializer, string id)
285                 {
286                         return DeserializeContent (reader, deserializer, id, true);
287                 }
288
289                 public virtual object DeserializeContent (XmlReader reader,
290                         XmlFormatterDeserializer deserializer, string id)
291                 {
292                         return DeserializeContent (reader, deserializer, id, false);
293                 }
294
295                 object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id, bool empty)
296                 {
297                         object instance = FormatterServices.GetUninitializedObject (RuntimeType);
298                         HandleId (id, deserializer, instance);
299
300                         if (OnDeserializing != null)
301                                 OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
302
303                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
304                         bool [] filled = new bool [Members.Count];
305                         bool [] nsmatched = new bool [Members.Count];
306                         int memberInd = -1, ordered = -1;
307                         while (!empty && reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
308                                 DataMemberInfo dmi = null;
309                                 int i = 0;
310                                 bool nsmatchedOne = false;
311                                 for (; i < Members.Count; i++) { // unordered
312                                         if (Members [i].Order >= 0)
313                                                 break;
314                                         if (reader.LocalName == Members [i].XmlName) {
315                                                 memberInd = i;
316                                                 dmi = Members [i];
317                                                 nsmatchedOne = (dmi.XmlRootNamespace == null || reader.NamespaceURI == dmi.XmlRootNamespace);
318                                                 if (nsmatchedOne)
319                                                         break;
320                                         }
321                                 }
322                                 for (i = Math.Max (i, ordered); i < Members.Count; i++) { // ordered
323                                         if (dmi != null)
324                                                 break;
325                                         if (reader.LocalName == Members [i].XmlName) {
326                                                 ordered = i;
327                                                 memberInd = i;
328                                                 dmi = Members [i];
329                                                 nsmatchedOne = (dmi.XmlRootNamespace == null || reader.NamespaceURI == dmi.XmlRootNamespace);
330                                                 if (nsmatchedOne)
331                                                         break;
332                                         }
333                                 }
334                                 
335                                 if (dmi == null) {
336                                         reader.Skip ();
337                                         reader.MoveToContent ();
338                                         continue;
339                                 }
340                                 if (filled [memberInd] && nsmatched [memberInd]) {
341                                         // The strictly-corresponding member (i.e. that matches namespace URI too, not only local name) already exists, so skip this element.
342                                         reader.Skip ();
343                                         reader.MoveToContent ();
344                                         continue;
345                                 }
346                                 nsmatched [memberInd] = nsmatchedOne;
347                                 SetValue (dmi, instance, deserializer.Deserialize (dmi.MemberType, reader));
348                                 filled [memberInd] = true;
349                                 reader.MoveToContent ();
350                         }
351                         for (int i = 0; i < Members.Count; i++)
352                                 if (!filled [i] && Members [i].IsRequired)
353                                         throw MissingRequiredMember (Members [i], reader);
354
355                         if (OnDeserialized != null)
356                                 OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
357
358                         return instance;
359                 }
360
361                 // For now it could be private.
362                 protected Exception MissingRequiredMember (DataMemberInfo dmi, XmlReader reader)
363                 {
364                         var li = reader as IXmlLineInfo;
365                         return new ArgumentException (String.Format ("Data contract member {0} for the type {1} is required, but missing in the input XML.{2}",
366                                 new QName (dmi.XmlName, dmi.XmlNamespace),
367                                 RuntimeType,
368                                 li != null && li.HasLineInfo () ? String.Format (" {0}({1},{2})", reader.BaseURI, li.LineNumber, li.LinePosition) : null));
369                 }
370
371                 // For now it could be private.
372                 protected void SetValue (DataMemberInfo dmi, object obj, object value)
373                 {
374                         try {
375                                 if (dmi.Member is PropertyInfo)
376                                         ((PropertyInfo) dmi.Member).SetValue (obj, value, null);
377                                 else
378                                         ((FieldInfo) dmi.Member).SetValue (obj, value);
379                         } catch (Exception ex) {
380                                 throw new InvalidOperationException (String.Format ("Failed to set value of type {0} for property {1}", value != null ? value.GetType () : null, dmi.Member), ex);
381                         }
382                 }
383
384                 protected DataMemberInfo CreateDataMemberInfo (DataMemberAttribute dma, MemberInfo mi, Type memberType, string ownerNamespace)
385                 {
386                         KnownTypes.Add (memberType);
387                         QName qname = KnownTypes.GetQName (memberType);
388                         
389                         if (KnownTypeCollection.GetPrimitiveTypeFromName (qname) != null)
390                                 return new DataMemberInfo (mi, dma, ownerNamespace, null);
391                         else
392                                 return new DataMemberInfo (mi, dma, ownerNamespace, qname.Namespace);
393                 }
394         }
395
396         internal partial class XmlSerializableMap : SerializationMap
397         {
398                 public override bool IsContractAllowedType { get { return true; } }
399
400                 public XmlSerializableMap (Type type, QName qname, KnownTypeCollection knownTypes)
401                         : base (type, qname, knownTypes)
402                 {
403                 }
404
405                 public override void Serialize (object graph, XmlFormatterSerializer serializer)
406                 {
407                         IXmlSerializable ixs = graph as IXmlSerializable;
408                         if (ixs == null)
409                                 //FIXME: Throw what exception here?
410                                 throw new SerializationException ();
411
412                         ixs.WriteXml (serializer.Writer);
413                 }
414
415                 public override object DeserializeObject (XmlReader reader, XmlFormatterDeserializer deserializer)
416                 {
417 #if NET_2_1
418                         IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType);
419 #else
420                         IXmlSerializable ixs = (IXmlSerializable) Activator.CreateInstance (RuntimeType, true);
421 #endif
422
423                         HandleId (reader, deserializer, ixs);
424
425                         ixs.ReadXml (reader);
426                         return ixs;
427                 }
428         }
429
430         internal partial class SharedContractMap : SerializationMap
431         {
432                 public SharedContractMap (
433                         Type type, QName qname, KnownTypeCollection knownTypes)
434                         : base (type, qname, knownTypes)
435                 {
436                 }
437
438                 public override bool IsContractAllowedType { get { return true; } }
439
440                 internal void Initialize ()
441                 {
442                         Type type = RuntimeType;
443                         List <DataMemberInfo> members;
444                         object [] atts = type.GetCustomAttributes (
445                                 typeof (DataContractAttribute), false);
446                         IsReference = atts.Length > 0 ? (((DataContractAttribute) atts [0]).IsReference) : false;
447
448                         while (type != null) {
449                                 members = GetMembers (type);
450                                 members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
451                                 Members.InsertRange (0, members);
452                                 members.Clear ();
453
454                                 type = type.BaseType;
455                         }
456                 }
457
458                 List<DataMemberInfo> GetMembers (Type type)
459                 {
460                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
461                         BindingFlags flags = AllInstanceFlags | BindingFlags.DeclaredOnly;
462
463                         foreach (PropertyInfo pi in type.GetProperties (flags)) {
464                                 DataMemberAttribute dma =
465                                         GetDataMemberAttribute (pi);
466                                 if (dma == null)
467                                         continue;
468                                 KnownTypes.Add (pi.PropertyType);
469                                 var map = KnownTypes.FindUserMap (pi.PropertyType);
470                                 if (!pi.CanRead || (!pi.CanWrite && !(map is ICollectionTypeMap)))
471                                         throw new InvalidDataContractException (String.Format (
472                                                         "DataMember property '{0}' on type '{1}' must have both getter and setter.", pi, pi.DeclaringType));
473                                 data_members.Add (CreateDataMemberInfo (dma, pi, pi.PropertyType, KnownTypeCollection.GetStaticQName (pi.DeclaringType).Namespace));
474                         }
475
476                         foreach (FieldInfo fi in type.GetFields (flags)) {
477                                 DataMemberAttribute dma =
478                                         GetDataMemberAttribute (fi);
479                                 if (dma == null)
480                                         continue;
481                                 data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType, KnownTypeCollection.GetStaticQName (fi.DeclaringType).Namespace));
482                         }
483
484                         return data_members;
485                 }
486
487                 public override List<DataMemberInfo> GetMembers ()
488                 {
489                         return Members;
490                 }
491         }
492
493         internal partial class DefaultTypeMap : SerializationMap
494         {
495                 public DefaultTypeMap (Type type, KnownTypeCollection knownTypes)
496                         : base (type, KnownTypeCollection.GetStaticQName (type), knownTypes)
497                 {
498                 }
499
500                 public override bool IsContractAllowedType { get { return false; } }
501
502                 internal void Initialize ()
503                 {
504                         Members.AddRange (GetDefaultMembers ());
505                 }
506
507                 List<DataMemberInfo> GetDefaultMembers ()
508                 {
509                         var l = new List<DataMemberInfo> ();
510                         foreach (var mi in RuntimeType.GetMembers ()) {
511                                 Type mt = null;
512                                 FieldInfo fi = mi as FieldInfo;
513                                 mt = fi == null ? null : fi.FieldType;
514                                 PropertyInfo pi = mi as PropertyInfo;
515                                 if (pi != null && pi.CanRead && pi.CanWrite && pi.GetIndexParameters ().Length == 0)
516                                         mt = pi.PropertyType;
517                                 if (mt == null)
518                                         continue;
519                                 if (mi.GetCustomAttributes (typeof (IgnoreDataMemberAttribute), false).Length != 0)
520                                         continue;
521                                 string ns = KnownTypeCollection.GetStaticQName (mi.DeclaringType).Namespace;
522                                 l.Add (CreateDataMemberInfo (new DataMemberAttribute (), mi, mt, ns));
523                         }
524                         l.Sort (DataMemberInfo.DataMemberInfoComparer.Instance);
525                         return l;
526                 }
527         }
528
529         // FIXME: it still needs to consider KeyName/ValueName
530         // (especially Dictionary collection is not likely considered yet.)
531         internal partial class CollectionContractTypeMap : CollectionTypeMap
532         {
533                 CollectionDataContractAttribute a;
534
535                 public CollectionContractTypeMap (
536                         Type type, CollectionDataContractAttribute a, Type elementType,
537                         QName qname, KnownTypeCollection knownTypes)
538                         : base (type, elementType, qname, knownTypes)
539                 {
540                         this.a = a;
541                         IsReference = a.IsReference;
542                         if (!String.IsNullOrEmpty (a.ItemName))
543                                 element_qname = new XmlQualifiedName (a.ItemName, a.Namespace ?? CurrentNamespace);
544                 }
545
546                 internal override string CurrentNamespace {
547                         get { return XmlName.Namespace; }
548                 }
549
550                 public override bool IsContractAllowedType { get { return true; } }
551         }
552
553         internal interface ICollectionTypeMap
554         {
555         }
556
557         internal partial class CollectionTypeMap : SerializationMap, ICollectionTypeMap
558         {
559                 Type element_type;
560                 internal QName element_qname;
561                 MethodInfo add_method;
562
563                 public CollectionTypeMap (
564                         Type type, Type elementType,
565                         QName qname, KnownTypeCollection knownTypes)
566                         : base (type, qname, knownTypes)
567                 {
568                         element_type = elementType;
569                         element_qname = KnownTypes.GetQName (element_type);
570                         var icoll = GetGenericCollectionInterface (RuntimeType);
571                         if (icoll != null) {
572                                 if (RuntimeType.IsInterface) {
573                                         add_method = RuntimeType.GetMethod ("Add", icoll.GetGenericArguments ());
574                                 } else {
575                                         var imap = RuntimeType.GetInterfaceMap (icoll);
576                                         for (int i = 0; i < imap.InterfaceMethods.Length; i++)
577                                                 if (imap.InterfaceMethods [i].Name == "Add") {
578                                                         add_method = imap.TargetMethods [i];
579                                                         break;
580                                                 }
581                                         if (add_method == null)
582                                                 add_method = type.GetMethod ("Add", icoll.GetGenericArguments ());
583                                 }
584                         }
585                 }
586
587                 static Type GetGenericCollectionInterface (Type type)
588                 {
589                         foreach (var iface in type.GetInterfacesOrSelfInterface ())
590                                 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IEnumerable<>))
591                                         return iface;
592
593                         return null;
594                 }
595
596                 public override bool IsContractAllowedType { get { return false; } }
597
598                 public override bool OutputXsiType {
599                         get { return false; }
600                 }
601
602                 internal virtual string CurrentNamespace {
603                         get {
604                                 string ns = element_qname.Namespace;
605                                 if (ns == KnownTypeCollection.MSSimpleNamespace)
606                                         ns = KnownTypeCollection.MSArraysNamespace;
607                                 return ns;
608                         }
609                 }
610
611                 public override void SerializeNonReference (object graph,
612                         XmlFormatterSerializer serializer)
613                 {
614                         // output item xmlns in advance so that it does not result in excessive xmlns overwrites.
615                         if (XmlName.Namespace != element_qname.Namespace && element_qname.Namespace != KnownTypeCollection.MSSimpleNamespace)
616                                 serializer.Writer.WriteXmlnsAttribute (null, element_qname.Namespace);
617
618                         foreach (object o in (IEnumerable) graph) {
619                                 serializer.WriteStartElement (element_qname.Name, XmlName.Namespace, CurrentNamespace);
620                                 serializer.Serialize (element_type, o);
621                                 serializer.WriteEndElement ();
622                         }
623                 }
624
625                 object CreateInstance ()
626                 {
627                         if (RuntimeType.IsArray)
628                                 return new ArrayList ();
629                         if (RuntimeType.IsInterface) {
630                                 var icoll = GetGenericCollectionInterface (RuntimeType);
631                                 if (icoll != null)
632                                         return Activator.CreateInstance (typeof (List<>).MakeGenericType (RuntimeType.GetGenericArguments () [0])); // List<T>
633                                 else // non-generic
634                                         return new ArrayList ();
635                         }
636 #if NET_2_1 // FIXME: is it fine?
637                         return Activator.CreateInstance (RuntimeType);
638 #else
639                         return Activator.CreateInstance (RuntimeType, true);
640 #endif
641                 }
642
643                 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
644                 {
645                         var instance = CreateInstance ();
646                         HandleId (id, deserializer, instance);
647                         if (OnDeserializing != null)
648                                 OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
649                         try {
650                                 if (RuntimeType.IsArray)
651                                         return ((ArrayList)instance).ToArray (element_type);
652                                 else
653                                         return instance;
654                         } finally {
655                                 if (OnDeserialized != null)
656                                         OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
657                         }
658                 }
659
660                 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
661                 {
662                         object instance = CreateInstance ();
663                         HandleId (id, deserializer, instance);
664                         if (OnDeserializing != null)
665                                 OnDeserializing.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
666                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
667                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
668                                 object elem = deserializer.Deserialize (element_type, reader);
669                                 if (instance is IList)
670                                         ((IList)instance).Add (elem);
671                                 else if (add_method != null)
672                                         add_method.Invoke (instance, new object [] {elem});
673                                 else
674                                         throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
675                                 reader.MoveToContent ();
676                         }
677                         try {
678                                 if (RuntimeType.IsArray)
679                                         return ((ArrayList)instance).ToArray (element_type);
680                                 return instance;
681                         } finally {
682                                 if (OnDeserialized != null)
683                                         OnDeserialized.Invoke (instance, new object [] {new StreamingContext (StreamingContextStates.All)});
684                         }
685                 }
686
687                 public override List<DataMemberInfo> GetMembers ()
688                 {
689                         //Shouldn't come here at all!
690                         throw new NotImplementedException ();
691                 }
692         }
693
694         internal partial class DictionaryTypeMap : SerializationMap, ICollectionTypeMap
695         {
696                 Type key_type, value_type;
697                 QName item_qname, key_qname, value_qname;
698                 MethodInfo add_method;
699                 CollectionDataContractAttribute a;
700
701                 public DictionaryTypeMap (
702                         Type type, CollectionDataContractAttribute a, KnownTypeCollection knownTypes)
703                         : base (type, QName.Empty, knownTypes)
704                 {
705                         this.a = a;
706
707                         key_type = typeof (object);
708                         value_type = typeof (object);
709
710                         var idic = GetGenericDictionaryInterface (RuntimeType);
711                         if (idic != null) {
712                                 var imap = RuntimeType.GetInterfaceMap (idic);
713                                 for (int i = 0; i < imap.InterfaceMethods.Length; i++)
714                                         if (imap.InterfaceMethods [i].Name == "Add") {
715                                                 add_method = imap.TargetMethods [i];
716                                                 break;
717                                         }
718                                 var argtypes = idic.GetGenericArguments();
719                                 key_type = argtypes [0];
720                                 value_type = argtypes [1];
721                                 if (add_method == null)
722                                         add_method = type.GetMethod ("Add", argtypes);
723                         }
724
725                         XmlName = GetDictionaryQName ();
726                         item_qname = GetItemQName ();
727                         key_qname = GetKeyQName ();
728                         value_qname = GetValueQName ();
729                 }
730
731                 static Type GetGenericDictionaryInterface (Type type)
732                 {
733                         foreach (var iface in type.GetInterfacesOrSelfInterface ())
734                                 if (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>))
735                                         return iface;
736
737                         return null;
738                 }
739
740                 string ContractNamespace {
741                         get { return a != null && !String.IsNullOrEmpty (a.Namespace) ? a.Namespace : KnownTypeCollection.MSArraysNamespace; }
742                 }
743
744                 public override bool IsContractAllowedType { get { return a != null; } }
745
746                 public Type KeyType { get { return key_type; } }
747                 public Type ValueType { get { return value_type; } }
748
749                 internal virtual QName GetDictionaryQName ()
750                 {
751                         string name = a != null ? a.Name : null;
752                         string ns = a != null ? a.Namespace : null;
753                         if (RuntimeType.IsGenericType && RuntimeType.GetGenericTypeDefinition () != typeof (Dictionary<,>))
754                                 name = name ?? KnownTypeCollection.GetDefaultName (RuntimeType);
755                         else
756                                 name = "ArrayOf" + GetItemQName ().Name;
757                         ns = ns ?? KnownTypeCollection.MSArraysNamespace;
758
759                         return new QName (name, ns);
760                 }
761
762                 internal virtual QName GetItemQName ()
763                 {
764                         string name = a != null ? a.ItemName : null;
765                         string ns = a != null ? a.Namespace : null;
766
767                         name = name ?? "KeyValueOf" + KnownTypes.GetQName (key_type).Name + KnownTypes.GetQName (value_type).Name;
768                         ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
769
770                         return new QName (name, ns);
771                 }
772
773                 internal virtual QName GetKeyQName ()
774                 {
775                         string name = a != null ? a.KeyName : null;
776                         string ns = a != null ? a.Namespace : null;
777
778                         name = name ?? "Key";
779                         ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
780                         return new QName (name, ns);
781                 }
782
783                 internal virtual QName GetValueQName ()
784                 {
785                         string name = a != null ? a.ValueName : null;
786                         string ns = a != null ? a.Namespace : null;
787
788                         name = name ?? "Value";
789                         ns = ns ?? (a != null ? ContractNamespace : KnownTypeCollection.MSArraysNamespace);
790                         return new QName (name, ns);
791                 }
792
793                 internal virtual string CurrentNamespace {
794                         get {
795                                 string ns = item_qname.Namespace;
796                                 if (ns == KnownTypeCollection.MSSimpleNamespace)
797                                         ns = KnownTypeCollection.MSArraysNamespace;
798                                 return ns;
799                         }
800                 }
801
802                 Type pair_type;
803                 PropertyInfo pair_key_property, pair_value_property;
804
805                 public override void SerializeNonReference (object graph,
806                         XmlFormatterSerializer serializer)
807                 {
808                         if (add_method != null) { // generic
809                                 if (pair_type == null) {
810                                         pair_type = typeof (KeyValuePair<,>).MakeGenericType (add_method.DeclaringType.GetGenericArguments ());
811                                         pair_key_property = pair_type.GetProperty ("Key");
812                                         pair_value_property = pair_type.GetProperty ("Value");
813                                 }
814                                 foreach (object 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 (pair_key_property.PropertyType, pair_key_property.GetValue (p, null));
818                                         serializer.WriteEndElement ();
819                                         serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
820                                         serializer.Serialize (pair_value_property.PropertyType, pair_value_property.GetValue (p, null));
821                                         serializer.WriteEndElement ();
822                                         serializer.WriteEndElement ();
823                                 }
824                         } else { // non-generic
825                                 foreach (DictionaryEntry p in (IEnumerable) graph) {
826                                         serializer.WriteStartElement (item_qname.Name, item_qname.Namespace, CurrentNamespace);
827                                         serializer.WriteStartElement (key_qname.Name, key_qname.Namespace, CurrentNamespace);
828                                         serializer.Serialize (key_type, p.Key);
829                                         serializer.WriteEndElement ();
830                                         serializer.WriteStartElement (value_qname.Name, value_qname.Namespace, CurrentNamespace);
831                                         serializer.Serialize (value_type, p.Value);
832                                         serializer.WriteEndElement ();
833                                         serializer.WriteEndElement ();
834                                 }
835                         }
836                 }
837
838                 object CreateInstance ()
839                 {
840                         if (RuntimeType.IsInterface) {
841                                 if (RuntimeType.IsGenericType && Array.IndexOf (RuntimeType.GetGenericTypeDefinition ().GetInterfaces (), typeof (IDictionary<,>)) >= 0) {
842                                         var gargs = RuntimeType.GetGenericArguments ();
843                                         return Activator.CreateInstance (typeof (Dictionary<,>).MakeGenericType (gargs [0], gargs [1])); // Dictionary<T>
844                                 }
845                                 else // non-generic
846                                         return new Hashtable ();
847                         }
848 #if NET_2_1 // FIXME: is it fine?
849                         return Activator.CreateInstance (RuntimeType);
850 #else
851                         return Activator.CreateInstance (RuntimeType, true);
852 #endif
853                 }
854
855                 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
856                 {
857                         return DeserializeContent (reader, deserializer, id);
858                 }
859
860                 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
861                 {
862                         object instance = CreateInstance ();
863                         HandleId (id, deserializer, instance);
864                         int depth = reader.NodeType == XmlNodeType.None ? reader.Depth : reader.Depth - 1;
865                         while (reader.NodeType == XmlNodeType.Element && reader.Depth > depth) {
866                                 if (reader.IsEmptyElement)
867                                         throw new XmlException (String.Format ("Unexpected empty element for dictionary entry: name {0}", reader.Name));
868                                 // FIXME: sloppy parsing
869                                 reader.ReadStartElement ();// item_qname.Name, item_qname.Namespace);
870                                 reader.MoveToContent ();
871                                 object key = deserializer.Deserialize (key_type, reader);
872                                 reader.MoveToContent ();
873                                 object val = deserializer.Deserialize (value_type, reader);
874                                 reader.MoveToContent ();
875                                 reader.ReadEndElement (); // of pair
876                                 reader.MoveToContent ();
877
878                                 if (instance is IDictionary)
879                                         ((IDictionary)instance).Add (key, val);
880                                 else if (add_method != null)
881                                         add_method.Invoke (instance, new object [] {key, val});
882                                 else
883                                         throw new NotImplementedException (String.Format ("Type {0} is not supported", RuntimeType));
884                         }
885                         return instance;
886                 }
887
888                 public override List<DataMemberInfo> GetMembers ()
889                 {
890                         //Shouldn't come here at all!
891                         throw new NotImplementedException ();
892                 }
893         }
894
895         internal partial class SharedTypeMap : SerializationMap
896         {
897                 public SharedTypeMap (
898                         Type type, QName qname, KnownTypeCollection knownTypes)
899                         : base (type, qname, knownTypes)
900                 {
901                 }
902
903                 public override bool IsContractAllowedType { get { return true; } }
904
905                 public void Initialize ()
906                 {
907                         Members = GetMembers (RuntimeType, XmlName, false);
908                 }
909
910                 List<DataMemberInfo> GetMembers (Type type, QName qname, bool declared_only)
911                 {
912                         List<DataMemberInfo> data_members = new List<DataMemberInfo> ();
913                         BindingFlags flags = AllInstanceFlags | BindingFlags.DeclaredOnly;
914                         
915                         for (Type t = type; t != null; t = t.BaseType) {
916                                 foreach (FieldInfo fi in t.GetFields (flags)) {
917                                         if (fi.GetCustomAttributes (
918                                                 typeof (NonSerializedAttribute),
919                                                 false).Length > 0)
920                                                 continue;
921
922                                         if (fi.IsInitOnly)
923                                                 throw new InvalidDataContractException (String.Format ("DataMember field {0} must not be read-only.", fi));
924                                         DataMemberAttribute dma = new DataMemberAttribute ();
925                                         data_members.Add (CreateDataMemberInfo (dma, fi, fi.FieldType, KnownTypeCollection.GetStaticQName (fi.DeclaringType).Namespace));
926                                 }
927                         }
928
929                         data_members.Sort (DataMemberInfo.DataMemberInfoComparer.Instance); // alphabetic order.
930
931                         return data_members;
932                 }
933
934                 // Does this make sense? I doubt.
935                 public override List<DataMemberInfo> GetMembers ()
936                 {
937                         return Members;
938                         //return GetMembers (RuntimeType, XmlName, true);
939                 }
940         }
941
942         internal partial class EnumMap : SerializationMap
943         {
944                 List<EnumMemberInfo> enum_members;
945                 bool flag_attr;
946
947                 public EnumMap (
948                         Type type, QName qname, KnownTypeCollection knownTypes)
949                         : base (type, qname, knownTypes)
950                 {
951                         bool has_dc = false;
952                         object [] atts = RuntimeType.GetCustomAttributes (
953                                 typeof (DataContractAttribute), false);
954                         if (atts.Length != 0)
955                                 has_dc = true;
956                         flag_attr = type.GetCustomAttributes (typeof (FlagsAttribute), false).Length > 0;
957
958                         enum_members = new List<EnumMemberInfo> ();
959                         BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
960                         
961                         foreach (FieldInfo fi in RuntimeType.GetFields (flags)) {
962                                 string name = fi.Name;
963                                 if (has_dc) {
964                                         EnumMemberAttribute ema =
965                                                 GetEnumMemberAttribute (fi);
966                                         if (ema == null)
967                                                 continue;
968
969                                         if (ema.Value != null)
970                                                 name = ema.Value;
971                                 }
972
973                                 enum_members.Add (new EnumMemberInfo (name, fi.GetValue (null)));
974                         }
975                 }
976
977                 public override bool IsContractAllowedType { get { return false; } }
978
979                 private EnumMemberAttribute GetEnumMemberAttribute (
980                         MemberInfo mi)
981                 {
982                         object [] atts = mi.GetCustomAttributes (
983                                 typeof (EnumMemberAttribute), false);
984                         if (atts.Length == 0)
985                                 return null;
986                         return (EnumMemberAttribute) atts [0];
987                 }
988
989                 public override void Serialize (object graph,
990                         XmlFormatterSerializer serializer)
991                 {
992                         if (flag_attr) {
993                                 long val = Convert.ToInt64 (graph);
994                                 string s = null;
995                                 foreach (EnumMemberInfo emi in enum_members) {
996                                         long f = Convert.ToInt64 (emi.Value);
997                                         if ((f & val) == f)
998                                                 s += (s != null ? " " : String.Empty) + emi.XmlName;
999                                 }
1000                                 if (s != null)
1001                                         serializer.Writer.WriteString (s);
1002                                 return;
1003                         } else {
1004                                 foreach (EnumMemberInfo emi in enum_members) {
1005                                         if (Enum.Equals (emi.Value, graph)) {
1006                                                 serializer.Writer.WriteString (emi.XmlName);
1007                                                 return;
1008                                         }
1009                                 }
1010                         }
1011
1012                         throw new SerializationException (String.Format (
1013                                 "Enum value '{0}' is invalid for type '{1}' and cannot be serialized.", graph, RuntimeType));
1014                 }
1015
1016                 public override object DeserializeEmptyContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
1017                 {
1018                         if (!flag_attr)
1019                                 throw new SerializationException (String.Format ("Enum value '' is invalid for type '{0}' and cannot be deserialized.", RuntimeType));
1020                         var instance = Enum.ToObject (RuntimeType, 0);
1021                         HandleId (id, deserializer, instance);
1022                         return instance;
1023                 }
1024
1025                 public override object DeserializeContent (XmlReader reader, XmlFormatterDeserializer deserializer, string id)
1026                 {
1027                         string value = reader.NodeType != XmlNodeType.Text ? String.Empty : reader.ReadContentAsString ();
1028                         HandleId (id, deserializer, value);
1029
1030                         if (value != String.Empty) {
1031                                 if (flag_attr && value.IndexOf (' ') != -1) {
1032                                         long flags = 0L;
1033                                         foreach (string flag in value.Split (' ')) {
1034                                                 foreach (EnumMemberInfo emi in enum_members) {
1035                                                         if (emi.XmlName == flag) {
1036                                                                 flags |= Convert.ToInt64 (emi.Value);
1037                                                                 break;
1038                                                         }
1039                                                 }
1040                                         }
1041                                         return Enum.ToObject (RuntimeType, flags);
1042                                 }
1043                                 else {
1044                                         foreach (EnumMemberInfo emi in enum_members)
1045                                                 if (emi.XmlName == value)
1046                                                         return emi.Value;
1047                                 }
1048                         }
1049
1050                         if (!flag_attr)
1051                                 throw new SerializationException (String.Format ("Enum value '{0}' is invalid for type '{1}' and cannot be deserialized.", value, RuntimeType));
1052                         return Enum.ToObject (RuntimeType, 0);
1053                 }
1054         }
1055
1056         internal struct EnumMemberInfo
1057         {
1058                 public readonly string XmlName;
1059                 public readonly object Value;
1060
1061                 public EnumMemberInfo (string name, object value)
1062                 {
1063                         XmlName = name;
1064                         Value = value;
1065                 }
1066         }
1067
1068         internal class DataMemberInfo //: KeyValuePair<int, MemberInfo>
1069         {
1070                 public readonly int Order;
1071                 public readonly bool IsRequired;
1072                 public readonly string XmlName;
1073                 public readonly MemberInfo Member;
1074                 public readonly string XmlNamespace;
1075                 public readonly string XmlRootNamespace;
1076                 public readonly Type MemberType;
1077
1078                 public DataMemberInfo (MemberInfo member, DataMemberAttribute dma, string rootNamespce, string ns)
1079                 {
1080                         if (dma == null)
1081                                 throw new ArgumentNullException ("dma");
1082                         Order = dma.Order;
1083                         Member = member;
1084                         IsRequired = dma.IsRequired;
1085                         XmlName = XmlConvert.EncodeLocalName (dma.Name != null ? dma.Name : member.Name);
1086                         XmlNamespace = ns;
1087                         XmlRootNamespace = rootNamespce;
1088                         if (Member is FieldInfo)
1089                                 MemberType = ((FieldInfo) Member).FieldType;
1090                         else
1091                                 MemberType = ((PropertyInfo) Member).PropertyType;
1092                 }
1093
1094                 public class DataMemberInfoComparer : IComparer<DataMemberInfo>
1095                         , IComparer // see bug #76361
1096                 {
1097                         public static readonly DataMemberInfoComparer Instance
1098                                 = new DataMemberInfoComparer ();
1099
1100                         private DataMemberInfoComparer () {}
1101
1102                         public int Compare (object o1, object o2)
1103                         {
1104                                 return Compare ((DataMemberInfo) o1,
1105                                         (DataMemberInfo) o2);
1106                         }
1107
1108                         public int Compare (DataMemberInfo d1, DataMemberInfo d2)
1109                         {
1110                                 if (d1.Order == d2.Order)
1111                                         return String.CompareOrdinal (d1.XmlName, d2.XmlName);
1112
1113                                 return d1.Order - d2.Order;
1114                         }
1115                 }
1116         }
1117 }
1118 #endif