KnownTypeCollection; recursively registered type was not added to the collection...
[mono.git] / mcs / class / System.Runtime.Serialization / System.Runtime.Serialization / KnownTypeCollection.cs
1 //
2 // KnownTypeCollection.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 #if NET_2_0
29 using System;
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
33 using System.Linq;
34 using System.Reflection;
35 using System.Xml;
36 using System.Xml.Schema;
37
38 using QName = System.Xml.XmlQualifiedName;
39 using System.Xml.Serialization;
40
41 namespace System.Runtime.Serialization
42 {
43 /*
44         XmlFormatter implementation design inference:
45
46         type definitions:
47         - No XML Schema types are directly used. There are some maps from
48           xs:blahType to ms:blahType where the namespaceURI for prefix "ms" is
49           "http://schemas.microsoft.com/2003/10/Serialization/" .
50
51         serializable types:
52         - An object being serialized 1) must be of type System.Object, or
53           2) must be null, or 3) must have either a [DataContract] attribute
54           or a [Serializable] attribute to be serializable.
55         - When the object is either of type System.Object or null, then the
56           XML type is "anyType".
57         - When the object is [Serializable], then the runtime-serialization
58           compatible object graph is written.
59         - Otherwise the serialization is based on contract attributes.
60           ([Serializable] takes precedence).
61
62         type derivation:
63         - For type A to be serializable, the base type B of A must be
64           serializable.
65         - If a type which is [Serializable] and whose base type has a
66           [DataContract], then for base type members [DataContract] is taken.
67         - It is vice versa i.e. if the base type is [Serializable] and the
68           derived type has a [DataContract], then [Serializable] takes place
69           for base members.
70
71         known type collection:
72         - It internally manages mapping store keyed by contract QNames.
73           KnownTypeCollection.Add() checks if the same QName contract already
74           exists (and raises InvalidOperationException if required).
75
76 */
77         internal static class TypeExtensions
78         {
79                 public static T GetCustomAttribute<T> (this MemberInfo type, bool inherit)
80                 {
81                         var arr = type.GetCustomAttributes (typeof (T), inherit);
82                         return arr != null && arr.Length == 1 ? (T) arr [0] : default (T);
83                 }
84         }
85
86         internal sealed class KnownTypeCollection : Collection<Type>
87         {
88                 internal const string MSSimpleNamespace =
89                         "http://schemas.microsoft.com/2003/10/Serialization/";
90                 internal const string MSArraysNamespace =
91                         "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
92                 internal const string DefaultClrNamespaceBase =
93                         "http://schemas.datacontract.org/2004/07/";
94
95                 static QName any_type, bool_type,
96                         byte_type, date_type, decimal_type, double_type,
97                         float_type, string_type,
98                         short_type, int_type, long_type,
99                         ubyte_type, ushort_type, uint_type, ulong_type,
100                         // non-TypeCode
101                         any_uri_type, base64_type, duration_type, qname_type,
102                         // custom in ms nsURI schema
103                         char_type, guid_type,
104                         // not in ms nsURI schema
105                         dbnull_type;
106
107                 static KnownTypeCollection ()
108                 {
109                         //any_type, bool_type,  byte_type, date_type, decimal_type, double_type,        float_type, string_type,
110                         // short_type, int_type, long_type,     ubyte_type, ushort_type, uint_type, ulong_type,
111                         //      any_uri_type, base64_type, duration_type, qname_type,
112                         //      char_type, guid_type,   dbnull_type;
113                         string s = MSSimpleNamespace;
114                         any_type = new QName ("anyType", s);
115                         any_uri_type = new QName ("anyURI", s);
116                         bool_type = new QName ("boolean", s);
117                         base64_type = new QName ("base64Binary", s);
118                         date_type = new QName ("dateTime", s);
119                         duration_type = new QName ("duration", s);
120                         qname_type = new QName ("QName", s);
121                         decimal_type = new QName ("decimal", s);
122                         double_type = new QName ("double", s);
123                         float_type = new QName ("float", s);
124                         byte_type = new QName ("byte", s);
125                         short_type = new QName ("short", s);
126                         int_type = new QName ("int", s);
127                         long_type = new QName ("long", s);
128                         ubyte_type = new QName ("unsignedByte", s);
129                         ushort_type = new QName ("unsignedShort", s);
130                         uint_type = new QName ("unsignedInt", s);
131                         ulong_type = new QName ("unsignedLong", s);
132                         string_type = new QName ("string", s);
133                         guid_type = new QName ("guid", s);
134                         char_type = new QName ("char", s);
135
136                         dbnull_type = new QName ("DBNull", MSSimpleNamespace + "System");
137                 }
138
139                 // FIXME: find out how QName and guid are processed
140
141                 internal QName GetXmlName (Type type)
142                 {
143                         SerializationMap map = FindUserMap (type);
144                         if (map != null)
145                                 return map.XmlName;
146                         return GetPredefinedTypeName (type);
147                 }
148
149                 internal static QName GetPredefinedTypeName (Type type)
150                 {
151                         QName name = GetPrimitiveTypeName (type);
152                         if (name != QName.Empty)
153                                 return name;
154                         if (type == typeof (DBNull))
155                                 return dbnull_type;
156                         return QName.Empty;
157                 }
158
159                 internal static QName GetPrimitiveTypeName (Type type)
160                 {
161                         if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
162                                 return GetPrimitiveTypeName (type.GetGenericArguments () [0]);
163
164                         if (type.IsEnum)
165                                 return QName.Empty;
166
167                         switch (Type.GetTypeCode (type)) {
168                         case TypeCode.Object: // other than System.Object
169                         case TypeCode.DBNull: // it is natively mapped, but not in ms serialization namespace.
170                         case TypeCode.Empty:
171                         default:
172                                 if (type == typeof (object))
173                                         return any_type;
174                                 if (type == typeof (Guid))
175                                         return guid_type;
176                                 if (type == typeof (TimeSpan))
177                                         return duration_type;
178                                 if (type == typeof (byte []))
179                                         return base64_type;
180                                 if (type == typeof (Uri))
181                                         return any_uri_type;
182                                 return QName.Empty;
183                         case TypeCode.Boolean:
184                                 return bool_type;
185                         case TypeCode.Byte:
186                                 return ubyte_type;
187                         case TypeCode.Char:
188                                 return char_type;
189                         case TypeCode.DateTime:
190                                 return date_type;
191                         case TypeCode.Decimal:
192                                 return decimal_type;
193                         case TypeCode.Double:
194                                 return double_type;
195                         case TypeCode.Int16:
196                                 return short_type;
197                         case TypeCode.Int32:
198                                 return int_type;
199                         case TypeCode.Int64:
200                                 return long_type;
201                         case TypeCode.SByte:
202                                 return byte_type;
203                         case TypeCode.Single:
204                                 return float_type;
205                         case TypeCode.String:
206                                 return string_type;
207                         case TypeCode.UInt16:
208                                 return ushort_type;
209                         case TypeCode.UInt32:
210                                 return uint_type;
211                         case TypeCode.UInt64:
212                                 return ulong_type;
213                         }
214                 }
215
216                 internal static string PredefinedTypeObjectToString (object obj)
217                 {
218                         Type type = obj.GetType ();
219                         switch (Type.GetTypeCode (type)) {
220                         case TypeCode.Object: // other than System.Object
221                         case TypeCode.Empty:
222                         default:
223                                 if (type == typeof (object))
224                                         return String.Empty;
225                                 if (type == typeof (Guid))
226                                         return XmlConvert.ToString ((Guid) obj);
227                                 if (type == typeof (TimeSpan))
228                                         return XmlConvert.ToString ((TimeSpan) obj);
229                                 if (type == typeof (byte []))
230                                         return Convert.ToBase64String ((byte []) obj);
231                                 if (type == typeof (Uri))
232                                         return ((Uri) obj).ToString ();
233                                 throw new Exception ("Internal error: missing predefined type serialization for type " + type.FullName);
234                         case TypeCode.DBNull: // predefined, but not primitive
235                                 return String.Empty;
236                         case TypeCode.Boolean:
237                                 return XmlConvert.ToString ((bool) obj);
238                         case TypeCode.Byte:
239                                 return XmlConvert.ToString ((int)((byte) obj));
240                         case TypeCode.Char:
241                                 return XmlConvert.ToString ((uint) (char) obj);
242                         case TypeCode.DateTime:
243                                 return XmlConvert.ToString ((DateTime) obj, XmlDateTimeSerializationMode.RoundtripKind);
244                         case TypeCode.Decimal:
245                                 return XmlConvert.ToString ((decimal) obj);
246                         case TypeCode.Double:
247                                 return XmlConvert.ToString ((double) obj);
248                         case TypeCode.Int16:
249                                 return XmlConvert.ToString ((short) obj);
250                         case TypeCode.Int32:
251                                 return XmlConvert.ToString ((int) obj);
252                         case TypeCode.Int64:
253                                 return XmlConvert.ToString ((long) obj);
254                         case TypeCode.SByte:
255                                 return XmlConvert.ToString ((sbyte) obj);
256                         case TypeCode.Single:
257                                 return XmlConvert.ToString ((float) obj);
258                         case TypeCode.String:
259                                 return (string) obj;
260                         case TypeCode.UInt16:
261                                 return XmlConvert.ToString ((int) (ushort) obj);
262                         case TypeCode.UInt32:
263                                 return XmlConvert.ToString ((uint) obj);
264                         case TypeCode.UInt64:
265                                 return XmlConvert.ToString ((ulong) obj);
266                         }
267                 }
268
269                 // FIXME: xsd types and ms serialization types should be differentiated.
270                 internal static Type GetPrimitiveTypeFromName (string name)
271                 {
272                         switch (name) {
273                         case "anyURI":
274                                 return typeof (Uri);
275                         case "boolean":
276                                 return typeof (bool);
277                         case "base64Binary":
278                                 return typeof (byte []);
279                         case "dateTime":
280                                 return typeof (DateTime);
281                         case "duration":
282                                 return typeof (TimeSpan);
283                         case "QName":
284                                 return typeof (QName);
285                         case "decimal":
286                                 return typeof (decimal);
287                         case "double":
288                                 return typeof (double);
289                         case "float":
290                                 return typeof (float);
291                         case "byte":
292                                 return typeof (sbyte);
293                         case "short":
294                                 return typeof (short);
295                         case "int":
296                                 return typeof (int);
297                         case "long":
298                                 return typeof (long);
299                         case "unsignedByte":
300                                 return typeof (byte);
301                         case "unsignedShort":
302                                 return typeof (ushort);
303                         case "unsignedInt":
304                                 return typeof (uint);
305                         case "unsignedLong":
306                                 return typeof (ulong);
307                         case "string":
308                                 return typeof (string);
309                         case "anyType":
310                                 return typeof (object);
311                         case "guid":
312                                 return typeof (Guid);
313                         case "char":
314                                 return typeof (char);
315                         default:
316                                 return null;
317                         }
318                 }
319
320
321                 internal static object PredefinedTypeStringToObject (string s,
322                         string name, XmlReader reader)
323                 {
324                         switch (name) {
325                         case "anyURI":
326                                 return new Uri(s,UriKind.RelativeOrAbsolute);
327                         case "boolean":
328                                 return XmlConvert.ToBoolean (s);
329                         case "base64Binary":
330                                 return Convert.FromBase64String (s);
331                         case "dateTime":
332                                 return XmlConvert.ToDateTime (s, XmlDateTimeSerializationMode.RoundtripKind);
333                         case "duration":
334                                 return XmlConvert.ToTimeSpan (s);
335                         case "QName":
336                                 int idx = s.IndexOf (':');
337                                 string l = idx < 0 ? s : s.Substring (idx + 1);
338                                 return idx < 0 ? new QName (l) :
339                                         new QName (l, reader.LookupNamespace (
340                                                 s.Substring (0, idx)));
341                         case "decimal":
342                                 return XmlConvert.ToDecimal (s);
343                         case "double":
344                                 return XmlConvert.ToDouble (s);
345                         case "float":
346                                 return XmlConvert.ToSingle (s);
347                         case "byte":
348                                 return XmlConvert.ToSByte (s);
349                         case "short":
350                                 return XmlConvert.ToInt16 (s);
351                         case "int":
352                                 return XmlConvert.ToInt32 (s);
353                         case "long":
354                                 return XmlConvert.ToInt64 (s);
355                         case "unsignedByte":
356                                 return XmlConvert.ToByte (s);
357                         case "unsignedShort":
358                                 return XmlConvert.ToUInt16 (s);
359                         case "unsignedInt":
360                                 return XmlConvert.ToUInt32 (s);
361                         case "unsignedLong":
362                                 return XmlConvert.ToUInt64 (s);
363                         case "string":
364                                 return s;
365                         case "guid":
366                                 return XmlConvert.ToGuid (s);
367                         case "anyType":
368                                 return s;
369                         case "char":
370                                 return (char) XmlConvert.ToUInt32 (s);
371                         default:
372                                 throw new Exception ("Unanticipated primitive type: " + name);
373                         }
374                 }
375
376                 List<SerializationMap> contracts = new List<SerializationMap> ();
377
378                 public KnownTypeCollection ()
379                 {
380                 }
381
382                 protected override void ClearItems ()
383                 {
384                         base.Clear ();
385                 }
386
387                 protected override void InsertItem (int index, Type type)
388                 {
389                         if (ShouldNotRegister (type))
390                                 return;
391                         if (!Contains (type)) {
392                                 TryRegister (type);
393                                 base.InsertItem (index, type);
394                         }
395                 }
396
397                 // FIXME: it could remove other types' dependencies.
398                 protected override void RemoveItem (int index)
399                 {
400                         Type t = base [index];
401                         List<SerializationMap> l = new List<SerializationMap> ();
402                         foreach (SerializationMap m in contracts) {
403                                 if (m.RuntimeType == t)
404                                         l.Add (m);
405                         }
406                         foreach (SerializationMap m in l) {
407                                 contracts.Remove (m);
408                                 base.RemoveItem (index);
409                         }
410                 }
411
412                 protected override void SetItem (int index, Type type)
413                 {
414                         if (ShouldNotRegister (type))
415                                 return;
416
417                         // Since this collection is not assured to be ordered, it ignores the whole Set operation if the type already exists.
418                         if (Contains (type))
419                                 return;
420
421                         if (index != Count)
422                                 RemoveItem (index);
423                         if (TryRegister (type))
424                                 base.InsertItem (index - 1, type);
425                 }
426
427                 internal SerializationMap FindUserMap (QName qname)
428                 {
429                         return contracts.FirstOrDefault (c => c.XmlName == qname);
430                 }
431
432                 internal Type GetSerializedType (Type type)
433                 {
434                         Type element = GetCollectionElementType (type);
435                         if (element == null)
436                                 return type;
437                         QName name = GetQName (type);
438                         var map = FindUserMap (name);
439                         if (map != null)
440                                 return map.RuntimeType;
441                         return type;
442                 }
443
444                 internal SerializationMap FindUserMap (Type type)
445                 {
446                         for (int i = 0; i < contracts.Count; i++)
447                                 if (type == contracts [i].RuntimeType)
448                                         return contracts [i];
449                         return null;
450                 }
451
452                 internal QName GetQName (Type type)
453                 {
454                         SerializationMap map = FindUserMap (type);
455                         if (map != null)
456                                 // already mapped.
457                                 return map.XmlName; 
458                         return GetStaticQName (type);
459                 }
460
461                 public static QName GetStaticQName (Type type)
462                 {
463                         if (IsPrimitiveNotEnum (type))
464                                 return GetPrimitiveTypeName (type);
465
466                         if (type.IsEnum)
467                                 return GetEnumQName (type);
468
469                         QName qname = GetContractQName (type);
470                         if (qname != null)
471                                 return qname;
472
473                         if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") != null)
474                                 //FIXME: Reusing GetSerializableQName here, since we just 
475                                 //need name of the type..
476                                 return GetSerializableQName (type);
477
478                         qname = GetCollectionContractQName (type);
479                         if (qname != null)
480                                 return qname;
481
482                         Type element = GetCollectionElementType (type);
483                         if (element != null)
484                                 return GetCollectionQName (element);
485
486                         if (GetAttribute<SerializableAttribute> (type) != null)
487                                 return GetSerializableQName (type);
488
489                         // default type map - still uses GetContractQName().
490                         return GetContractQName (type, null, null);
491                 }
492
493                 internal static QName GetContractQName (Type type)
494                 {
495                         var a = GetAttribute<DataContractAttribute> (type);
496                         return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
497                 }
498
499                 static QName GetCollectionContractQName (Type type)
500                 {
501                         var a = GetAttribute<CollectionDataContractAttribute> (type);
502                         return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
503                 }
504
505                 static QName GetContractQName (Type type, string name, string ns)
506                 {
507                         if (name == null)
508                                 name = GetDefaultName (type);
509                         if (ns == null)
510                                 ns = GetDefaultNamespace (type);
511                         return new QName (name, ns);
512                 }
513
514                 static QName GetEnumQName (Type type)
515                 {
516                         string name = null, ns = null;
517
518                         if (!type.IsEnum)
519                                 return null;
520
521                         var dca = GetAttribute<DataContractAttribute> (type);
522
523                         if (dca != null) {
524                                 ns = dca.Namespace;
525                                 name = dca.Name;
526                         }
527
528                         if (ns == null)
529                                 ns = GetDefaultNamespace (type);
530
531                         if (name == null)
532                                 name = type.Namespace == null ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
533
534                         return new QName (name, ns);
535                 }
536
537                 internal static string GetDefaultName (Type type)
538                 {
539                         // FIXME: there could be decent ways to get 
540                         // the same result...
541                         string name = type.Namespace == null || type.Namespace.Length == 0 ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
542                         if (type.IsGenericType) {
543                                 name = name.Substring (0, name.IndexOf ('`')) + "Of";
544                                 foreach (var t in type.GetGenericArguments ())
545                                         name += t.Name; // FIXME: check namespaces too
546                         }
547                         return name;
548                 }
549
550                 internal static string GetDefaultNamespace (Type type)
551                 {
552                         foreach (ContractNamespaceAttribute a in type.Assembly.GetCustomAttributes (typeof (ContractNamespaceAttribute), true))
553                                 if (a.ClrNamespace == type.Namespace)
554                                         return a.ContractNamespace;
555                         return DefaultClrNamespaceBase + type.Namespace;
556                 }
557
558                 static QName GetCollectionQName (Type element)
559                 {
560                         QName eqname = GetStaticQName (element);
561                         
562                         string ns = eqname.Namespace;
563                         if (eqname.Namespace == MSSimpleNamespace)
564                                 //Arrays of Primitive types
565                                 ns = MSArraysNamespace;
566
567                         return new QName (
568                                 "ArrayOf" + XmlConvert.EncodeLocalName (eqname.Name),
569                                 ns);
570                 }
571
572                 static QName GetSerializableQName (Type type)
573                 {
574 #if !NET_2_1
575                         // First, check XmlSchemaProviderAttribute and try GetSchema() to see if it returns a schema in the expected format.
576                         var xpa = type.GetCustomAttribute<XmlSchemaProviderAttribute> (true);
577                         if (xpa != null) {
578                                 var mi = type.GetMethod (xpa.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
579                                 if (mi != null) {
580                                         try {
581                                                 var xss = new XmlSchemaSet ();
582                                                 return (XmlQualifiedName) mi.Invoke (null, new object [] {xss});
583                                         } catch {
584                                                 // ignore.
585                                         }
586                                 }
587                         }
588 #endif
589
590                         string xmlName = type.Name;
591                         if (type.IsGenericType) {
592                                 xmlName = xmlName.Substring (0, xmlName.IndexOf ('`')) + "Of";
593                                 foreach (var t in type.GetGenericArguments ())
594                                         xmlName += GetStaticQName (t).Name; // FIXME: check namespaces too
595                         }
596                         string xmlNamespace = GetDefaultNamespace (type);
597                         var x = GetAttribute<XmlRootAttribute> (type);
598                         if (x != null) {
599                                 xmlName = x.ElementName;
600                                 xmlNamespace = x.Namespace;
601                         }
602                         return new QName (XmlConvert.EncodeLocalName (xmlName), xmlNamespace);
603                 }
604
605                 static bool IsPrimitiveNotEnum (Type type)
606                 {
607                         if (type.IsEnum)
608                                 return false;
609                         if (Type.GetTypeCode (type) != TypeCode.Object) // explicitly primitive
610                                 return true;
611                         if (type == typeof (Guid) || type == typeof (object) || type == typeof(TimeSpan) || type == typeof(byte[]) || type==typeof(Uri)) // special primitives
612                                 return true;
613                         // nullable
614                         if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
615                                 return IsPrimitiveNotEnum (type.GetGenericArguments () [0]);
616                         return false;
617                 }
618
619                 bool ShouldNotRegister (Type type)
620                 {
621                         return IsPrimitiveNotEnum (type);
622                 }
623
624                 internal bool TryRegister (Type type)
625                 {
626                         // exclude predefined maps
627                         if (ShouldNotRegister (type))
628                                 return false;
629
630                         if (FindUserMap (type) != null)
631                                 return false;
632
633                         if (RegisterEnum (type) != null)
634                                 return true;
635
636                         if (RegisterDictionary (type) != null)
637                                 return true;
638
639                         if (RegisterCollectionContract (type) != null)
640                                 return true;
641
642                         if (RegisterContract (type) != null)
643                                 return true;
644
645                         if (RegisterIXmlSerializable (type) != null)
646                                 return true;
647
648                         if (RegisterCollection (type) != null)
649                                 return true;
650
651                         if (GetAttribute<SerializableAttribute> (type) != null) {
652                                 RegisterSerializable (type);
653                                 return true;
654                         }
655
656                         RegisterDefaultTypeMap (type);
657                         return true;
658                 }
659
660                 static Type GetCollectionElementType (Type type)
661                 {
662                         if (type.IsArray)
663                                 return type.GetElementType ();
664
665                         Type [] ifaces = type.GetInterfaces ();
666                         foreach (Type i in ifaces)
667                                 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (ICollection<>)))
668                                         return i.GetGenericArguments () [0];
669                         foreach (Type i in ifaces)
670                                 if (i == typeof (IList))
671                                         return typeof (object);
672                         return null;
673                 }
674
675                 internal static T GetAttribute<T> (ICustomAttributeProvider ap) where T : Attribute
676                 {
677                         object [] atts = ap.GetCustomAttributes (typeof (T), false);
678                         return atts.Length == 0 ? null : (T) atts [0];
679                 }
680
681                 private CollectionContractTypeMap RegisterCollectionContract (Type type)
682                 {
683                         var cdca = GetAttribute<CollectionDataContractAttribute> (type);
684                         if (cdca == null)
685                                 return null;
686
687                         Type element = GetCollectionElementType (type);
688                         if (element == null)
689                                 throw new InvalidOperationException (String.Format ("Type '{0}' is marked as collection contract, but it is not a collection", type));
690
691                         TryRegister (element); // must be registered before the name conflict check.
692
693                         QName qname = GetCollectionContractQName (type);
694                         CheckStandardQName (qname);
695                         if (FindUserMap (qname) != null)
696                                 throw new InvalidOperationException (String.Format ("Failed to add type {0} to known type collection. There already is a registered type for XML name {1}", type, qname));
697
698                         var ret = new CollectionContractTypeMap (type, cdca, element, qname, this);
699                         contracts.Add (ret);
700                         return ret;
701                 }
702
703                 private CollectionTypeMap RegisterCollection (Type type)
704                 {
705                         Type element = GetCollectionElementType (type);
706                         if (element == null)
707                                 return null;
708
709                         TryRegister (element);
710
711                         QName qname = GetCollectionQName (element);
712
713                         var map = FindUserMap (qname);
714                         if (map != null) {
715                                 var cmap = map as CollectionTypeMap;
716                                 if (cmap == null || cmap.RuntimeType != type)
717                                         throw new InvalidOperationException (String.Format ("Failed to add type {0} to known type collection. There already is a registered type for XML name {1}", type, qname));
718                                 return cmap;
719                         }
720
721                         CollectionTypeMap ret =
722                                 new CollectionTypeMap (type, element, qname, this);
723                         contracts.Add (ret);
724                         return ret;
725                 }
726
727                 static bool TypeImplementsIDictionary (Type type)
728                 {
729                         foreach (var iface in type.GetInterfaces ())
730                                 if (iface == typeof (IDictionary) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>)))
731                                         return true;
732
733                         return false;
734                 }
735
736                 // it also supports contract-based dictionary.
737                 private DictionaryTypeMap RegisterDictionary (Type type)
738                 {
739                         if (!TypeImplementsIDictionary (type))
740                                 return null;
741
742                         var cdca = GetAttribute<CollectionDataContractAttribute> (type);
743
744                         DictionaryTypeMap ret =
745                                 new DictionaryTypeMap (type, cdca, this);
746
747                         if (FindUserMap (ret.XmlName) != null)
748                                 throw new InvalidOperationException (String.Format ("Failed to add type {0} to known type collection. There already is a registered type for XML name {1}", type, ret.XmlName));
749                         contracts.Add (ret);
750
751                         TryRegister (ret.KeyType);
752                         TryRegister (ret.ValueType);
753
754                         return ret;
755                 }
756
757                 private SerializationMap RegisterSerializable (Type type)
758                 {
759                         QName qname = GetSerializableQName (type);
760
761                         if (FindUserMap (qname) != null)
762                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
763
764                         SharedTypeMap ret = new SharedTypeMap (type, qname, this);
765                         contracts.Add (ret);
766                         ret.Initialize ();
767                         return ret;
768                 }
769
770                 private SerializationMap RegisterIXmlSerializable (Type type)
771                 {
772                         if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") == null)
773                                 return null;
774
775                         QName qname = GetSerializableQName (type);
776
777                         if (FindUserMap (qname) != null)
778                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
779
780                         XmlSerializableMap ret = new XmlSerializableMap (type, qname, this);
781                         contracts.Add (ret);
782
783                         return ret;
784                 }
785
786                 void CheckStandardQName (QName qname)
787                 {
788                         switch (qname.Namespace) {
789                         case XmlSchema.Namespace:
790                         case XmlSchema.InstanceNamespace:
791                         case MSSimpleNamespace:
792                         case MSArraysNamespace:
793                                 throw new InvalidOperationException (String.Format ("Namespace {0} is reserved and cannot be used for user serialization", qname.Namespace));
794                         }
795
796                 }
797
798                 private SharedContractMap RegisterContract (Type type)
799                 {
800                         QName qname = GetContractQName (type);
801                         if (qname == null)
802                                 return null;
803                         CheckStandardQName (qname);
804                         if (FindUserMap (qname) != null)
805                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
806
807                         SharedContractMap ret = new SharedContractMap (type, qname, this);
808                         contracts.Add (ret);
809                         ret.Initialize ();
810
811                         if (type.BaseType != typeof (object)) {
812                                 TryRegister (type.BaseType);
813                                 if (!FindUserMap (type.BaseType).IsContractAllowedType)
814                                         throw new InvalidDataContractException (String.Format ("To be serializable by data contract, type '{0}' cannot inherit from non-contract and non-Serializable type '{1}'", type, type.BaseType));
815                         }
816
817                         object [] attrs = type.GetCustomAttributes (typeof (KnownTypeAttribute), true);
818                         for (int i = 0; i < attrs.Length; i++) {
819                                 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
820                                 TryRegister (kt.Type);
821                         }
822
823                         return ret;
824                 }
825
826                 DefaultTypeMap RegisterDefaultTypeMap (Type type)
827                 {
828                         DefaultTypeMap ret = new DefaultTypeMap (type, this);
829                         contracts.Add (ret);
830                         ret.Initialize ();
831                         return ret;
832                 }
833
834                 private EnumMap RegisterEnum (Type type)
835                 {
836                         QName qname = GetEnumQName (type);
837                         if (qname == null)
838                                 return null;
839
840                         if (FindUserMap (qname) != null)
841                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
842
843                         EnumMap ret =
844                                 new EnumMap (type, qname, this);
845                         contracts.Add (ret);
846                         return ret;
847                 }
848         }
849 }
850 #endif