Wrap always_inline and noinline attributes in compiler checks and use MSVC equivalent.
[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                 public static IEnumerable<Type> GetInterfacesOrSelfInterface (this Type type)
86                 {
87                         if (type.IsInterface)
88                                 yield return type;
89                         foreach (var t in type.GetInterfaces ())
90                                 yield return t;
91                 }
92         }
93
94         internal sealed class KnownTypeCollection : Collection<Type>
95         {
96                 internal const string MSSimpleNamespace =
97                         "http://schemas.microsoft.com/2003/10/Serialization/";
98                 internal const string MSArraysNamespace =
99                         "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
100                 internal const string DefaultClrNamespaceBase =
101                         "http://schemas.datacontract.org/2004/07/";
102                 internal const string DefaultClrNamespaceSystem =
103                         "http://schemas.datacontract.org/2004/07/System";
104
105
106                 static QName any_type, bool_type,
107                         byte_type, date_type, decimal_type, double_type,
108                         float_type, string_type,
109                         short_type, int_type, long_type,
110                         ubyte_type, ushort_type, uint_type, ulong_type,
111                         // non-TypeCode
112                         any_uri_type, base64_type, duration_type, qname_type,
113                         // custom in ms nsURI schema
114                         char_type, guid_type,
115                         // not in ms nsURI schema
116                         dbnull_type, date_time_offset_type;
117
118                 // XmlSchemaType.GetBuiltInPrimitiveType() does not exist in moonlight, so I had to explicitly add them. And now that we have it, it does not make much sense to use #if MOONLIGHT ... #endif for XmlSchemaType anymore :-(
119                 static Dictionary<string,Type> xs_predefined_types = new Dictionary<string,Type> ();
120
121                 static KnownTypeCollection ()
122                 {
123                         string s = MSSimpleNamespace;
124                         any_type = new QName ("anyType", s);
125                         any_uri_type = new QName ("anyURI", s);
126                         bool_type = new QName ("boolean", s);
127                         base64_type = new QName ("base64Binary", s);
128                         date_type = new QName ("dateTime", s);
129                         duration_type = new QName ("duration", s);
130                         qname_type = new QName ("QName", s);
131                         decimal_type = new QName ("decimal", s);
132                         double_type = new QName ("double", s);
133                         float_type = new QName ("float", s);
134                         byte_type = new QName ("byte", s);
135                         short_type = new QName ("short", s);
136                         int_type = new QName ("int", s);
137                         long_type = new QName ("long", s);
138                         ubyte_type = new QName ("unsignedByte", s);
139                         ushort_type = new QName ("unsignedShort", s);
140                         uint_type = new QName ("unsignedInt", s);
141                         ulong_type = new QName ("unsignedLong", s);
142                         string_type = new QName ("string", s);
143                         guid_type = new QName ("guid", s);
144                         char_type = new QName ("char", s);
145
146                         dbnull_type = new QName ("DBNull", DefaultClrNamespaceBase + "System");
147                         date_time_offset_type = new QName ("DateTimeOffset", DefaultClrNamespaceBase + "System");
148
149                         xs_predefined_types.Add ("string", typeof (string));
150                         xs_predefined_types.Add ("boolean", typeof (bool));
151                         xs_predefined_types.Add ("float", typeof (float));
152                         xs_predefined_types.Add ("double", typeof (double));
153                         xs_predefined_types.Add ("decimal", typeof (decimal));
154                         xs_predefined_types.Add ("duration", typeof (TimeSpan));
155                         xs_predefined_types.Add ("dateTime", typeof (DateTime));
156                         xs_predefined_types.Add ("date", typeof (DateTime));
157                         xs_predefined_types.Add ("time", typeof (DateTime));
158                         xs_predefined_types.Add ("gYearMonth", typeof (DateTime));
159                         xs_predefined_types.Add ("gYear", typeof (DateTime));
160                         xs_predefined_types.Add ("gMonthDay", typeof (DateTime));
161                         xs_predefined_types.Add ("gDay", typeof (DateTime));
162                         xs_predefined_types.Add ("gMonth", typeof (DateTime));
163                         xs_predefined_types.Add ("hexBinary", typeof (byte []));
164                         xs_predefined_types.Add ("base64Binary", typeof (byte []));
165                         xs_predefined_types.Add ("anyURI", typeof (Uri));
166                         xs_predefined_types.Add ("QName", typeof (QName));
167                         xs_predefined_types.Add ("NOTATION", typeof (string));
168
169                         xs_predefined_types.Add ("normalizedString", typeof (string));
170                         xs_predefined_types.Add ("token", typeof (string));
171                         xs_predefined_types.Add ("language", typeof (string));
172                         xs_predefined_types.Add ("IDREFS", typeof (string []));
173                         xs_predefined_types.Add ("ENTITIES", typeof (string []));
174                         xs_predefined_types.Add ("NMTOKEN", typeof (string));
175                         xs_predefined_types.Add ("NMTOKENS", typeof (string []));
176                         xs_predefined_types.Add ("Name", typeof (string));
177                         xs_predefined_types.Add ("NCName", typeof (string));
178                         xs_predefined_types.Add ("ID", typeof (string));
179                         xs_predefined_types.Add ("IDREF", typeof (string));
180                         xs_predefined_types.Add ("ENTITY", typeof (string));
181
182                         xs_predefined_types.Add ("integer", typeof (decimal));
183                         xs_predefined_types.Add ("nonPositiveInteger", typeof (int));
184                         xs_predefined_types.Add ("negativeInteger", typeof (int));
185                         xs_predefined_types.Add ("long", typeof (long));
186                         xs_predefined_types.Add ("int", typeof (int));
187                         xs_predefined_types.Add ("short", typeof (short));
188                         xs_predefined_types.Add ("byte", typeof (sbyte));
189                         xs_predefined_types.Add ("nonNegativeInteger", typeof (decimal));
190                         xs_predefined_types.Add ("unsignedLong", typeof (ulong));
191                         xs_predefined_types.Add ("unsignedInt", typeof (uint));
192                         xs_predefined_types.Add ("unsignedShort", typeof (ushort));
193                         xs_predefined_types.Add ("unsignedByte", typeof (byte));
194                         xs_predefined_types.Add ("positiveInteger", typeof (decimal));
195
196                         xs_predefined_types.Add ("anyType", typeof (object));
197                 }
198
199                 // FIXME: find out how QName and guid are processed
200
201                 internal QName GetXmlName (Type type)
202                 {
203                         SerializationMap map = FindUserMap (type);
204                         if (map != null)
205                                 return map.XmlName;
206                         return GetPredefinedTypeName (type);
207                 }
208
209                 internal static QName GetPredefinedTypeName (Type type)
210                 {
211                         QName name = GetPrimitiveTypeName (type);
212                         if (name != QName.Empty)
213                                 return name;
214                         if (type == typeof (DBNull))
215                                 return dbnull_type;
216                         return QName.Empty;
217                 }
218
219                 internal static QName GetPrimitiveTypeName (Type type)
220                 {
221                         if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
222                                 return GetPrimitiveTypeName (type.GetGenericArguments () [0]);
223
224                         if (type.IsEnum)
225                                 return QName.Empty;
226
227                         switch (Type.GetTypeCode (type)) {
228                         case TypeCode.Object: // other than System.Object
229                         case TypeCode.DBNull: // it is natively mapped, but not in ms serialization namespace.
230                         case TypeCode.Empty:
231                         default:
232                                 if (type == typeof (object))
233                                         return any_type;
234                                 if (type == typeof (Guid))
235                                         return guid_type;
236                                 if (type == typeof (TimeSpan))
237                                         return duration_type;
238                                 if (type == typeof (byte []))
239                                         return base64_type;
240                                 if (type == typeof (Uri))
241                                         return any_uri_type;
242                                 if (type == typeof (DateTimeOffset))
243                                         return date_time_offset_type;
244                                 return QName.Empty;
245                         case TypeCode.Boolean:
246                                 return bool_type;
247                         case TypeCode.Byte:
248                                 return ubyte_type;
249                         case TypeCode.Char:
250                                 return char_type;
251                         case TypeCode.DateTime:
252                                 return date_type;
253                         case TypeCode.Decimal:
254                                 return decimal_type;
255                         case TypeCode.Double:
256                                 return double_type;
257                         case TypeCode.Int16:
258                                 return short_type;
259                         case TypeCode.Int32:
260                                 return int_type;
261                         case TypeCode.Int64:
262                                 return long_type;
263                         case TypeCode.SByte:
264                                 return byte_type;
265                         case TypeCode.Single:
266                                 return float_type;
267                         case TypeCode.String:
268                                 return string_type;
269                         case TypeCode.UInt16:
270                                 return ushort_type;
271                         case TypeCode.UInt32:
272                                 return uint_type;
273                         case TypeCode.UInt64:
274                                 return ulong_type;
275                         }
276                 }
277
278                 internal static string PredefinedTypeObjectToString (object obj)
279                 {
280                         Type type = obj.GetType ();
281                         switch (Type.GetTypeCode (type)) {
282                         case TypeCode.Object: // other than System.Object
283                         case TypeCode.Empty:
284                         default:
285                                 if (type == typeof (object))
286                                         return String.Empty;
287                                 if (type == typeof (Guid))
288                                         return XmlConvert.ToString ((Guid) obj);
289                                 if (type == typeof (TimeSpan))
290                                         return XmlConvert.ToString ((TimeSpan) obj);
291                                 if (type == typeof (byte []))
292                                         return Convert.ToBase64String ((byte []) obj);
293                                 if (type == typeof (Uri))
294                                         return ((Uri) obj).ToString ();
295                                 throw new Exception ("Internal error: missing predefined type serialization for type " + type.FullName);
296                         case TypeCode.DBNull: // predefined, but not primitive
297                                 return String.Empty;
298                         case TypeCode.Boolean:
299                                 return XmlConvert.ToString ((bool) obj);
300                         case TypeCode.Byte:
301                                 return XmlConvert.ToString ((int)((byte) obj));
302                         case TypeCode.Char:
303                                 return XmlConvert.ToString ((uint) (char) obj);
304                         case TypeCode.DateTime:
305                                 return XmlConvert.ToString ((DateTime) obj, XmlDateTimeSerializationMode.RoundtripKind);
306                         case TypeCode.Decimal:
307                                 return XmlConvert.ToString ((decimal) obj);
308                         case TypeCode.Double:
309                                 return XmlConvert.ToString ((double) obj);
310                         case TypeCode.Int16:
311                                 return XmlConvert.ToString ((short) obj);
312                         case TypeCode.Int32:
313                                 return XmlConvert.ToString ((int) obj);
314                         case TypeCode.Int64:
315                                 return XmlConvert.ToString ((long) obj);
316                         case TypeCode.SByte:
317                                 return XmlConvert.ToString ((sbyte) obj);
318                         case TypeCode.Single:
319                                 return XmlConvert.ToString ((float) obj);
320                         case TypeCode.String:
321                                 return (string) obj;
322                         case TypeCode.UInt16:
323                                 return XmlConvert.ToString ((int) (ushort) obj);
324                         case TypeCode.UInt32:
325                                 return XmlConvert.ToString ((uint) obj);
326                         case TypeCode.UInt64:
327                                 return XmlConvert.ToString ((ulong) obj);
328                         }
329                 }
330
331                 internal static Type GetPrimitiveTypeFromName (QName name)
332                 {
333                         switch (name.Namespace) {
334                         case DefaultClrNamespaceSystem:
335                                 switch (name.Name) {
336                                 case "DBNull":
337                                         return typeof (DBNull);
338                                 case "DateTimeOffset":
339                                         return typeof (DateTimeOffset);
340                                 }
341                                 break;
342                         case XmlSchema.Namespace:
343                                 return xs_predefined_types.FirstOrDefault (p => p.Key == name.Name).Value;
344                         case MSSimpleNamespace:
345                                 switch (name.Name) {
346                                 case "anyURI":
347                                         return typeof (Uri);
348                                 case "boolean":
349                                         return typeof (bool);
350                                 case "base64Binary":
351                                         return typeof (byte []);
352                                 case "dateTime":
353                                         return typeof (DateTime);
354                                 case "duration":
355                                         return typeof (TimeSpan);
356                                 case "QName":
357                                         return typeof (QName);
358                                 case "decimal":
359                                         return typeof (decimal);
360                                 case "double":
361                                         return typeof (double);
362                                 case "float":
363                                         return typeof (float);
364                                 case "byte":
365                                         return typeof (sbyte);
366                                 case "short":
367                                         return typeof (short);
368                                 case "int":
369                                         return typeof (int);
370                                 case "long":
371                                         return typeof (long);
372                                 case "unsignedByte":
373                                         return typeof (byte);
374                                 case "unsignedShort":
375                                         return typeof (ushort);
376                                 case "unsignedInt":
377                                         return typeof (uint);
378                                 case "unsignedLong":
379                                         return typeof (ulong);
380                                 case "string":
381                                         return typeof (string);
382                                 case "anyType":
383                                         return typeof (object);
384                                 case "guid":
385                                         return typeof (Guid);
386                                 case "char":
387                                         return typeof (char);
388                                 }
389                                 break;
390                         }
391                         return null;
392                 }
393
394
395                 internal static object PredefinedTypeStringToObject (string s,
396                         string name, XmlReader reader)
397                 {
398                         switch (name) {
399                         case "anyURI":
400                                 return new Uri(s,UriKind.RelativeOrAbsolute);
401                         case "boolean":
402                                 return XmlConvert.ToBoolean (s);
403                         case "base64Binary":
404                                 return Convert.FromBase64String (s);
405                         case "dateTime":
406                                 return XmlConvert.ToDateTime (s, XmlDateTimeSerializationMode.RoundtripKind);
407                         case "duration":
408                                 return XmlConvert.ToTimeSpan (s);
409                         case "QName":
410                                 int idx = s.IndexOf (':');
411                                 string l = idx < 0 ? s : s.Substring (idx + 1);
412                                 return idx < 0 ? new QName (l) :
413                                         new QName (l, reader.LookupNamespace (
414                                                 s.Substring (0, idx)));
415                         case "decimal":
416                                 return XmlConvert.ToDecimal (s);
417                         case "double":
418                                 return XmlConvert.ToDouble (s);
419                         case "float":
420                                 return XmlConvert.ToSingle (s);
421                         case "byte":
422                                 return XmlConvert.ToSByte (s);
423                         case "short":
424                                 return XmlConvert.ToInt16 (s);
425                         case "int":
426                                 return XmlConvert.ToInt32 (s);
427                         case "long":
428                                 return XmlConvert.ToInt64 (s);
429                         case "unsignedByte":
430                                 return XmlConvert.ToByte (s);
431                         case "unsignedShort":
432                                 return XmlConvert.ToUInt16 (s);
433                         case "unsignedInt":
434                                 return XmlConvert.ToUInt32 (s);
435                         case "unsignedLong":
436                                 return XmlConvert.ToUInt64 (s);
437                         case "string":
438                                 return s;
439                         case "guid":
440                                 return XmlConvert.ToGuid (s);
441                         case "anyType":
442                                 return s;
443                         case "char":
444                                 return (char) XmlConvert.ToUInt32 (s);
445                         default:
446                                 throw new Exception ("Unanticipated primitive type: " + name);
447                         }
448                 }
449
450                 List<SerializationMap> contracts = new List<SerializationMap> ();
451
452                 public KnownTypeCollection ()
453                 {
454                 }
455
456                 protected override void ClearItems ()
457                 {
458                         base.Clear ();
459                 }
460
461                 protected override void InsertItem (int index, Type type)
462                 {
463                         if (ShouldNotRegister (type))
464                                 return;
465                         if (!Contains (type)) {
466                                 TryRegister (type);
467                                 base.InsertItem (index, type);
468                         }
469                 }
470
471                 // FIXME: it could remove other types' dependencies.
472                 protected override void RemoveItem (int index)
473                 {
474                         lock (this)
475                                 DoRemoveItem (index);
476                 }
477
478                 void DoRemoveItem (int index)
479                 {
480                         Type t = base [index];
481                         List<SerializationMap> l = new List<SerializationMap> ();
482                         foreach (SerializationMap m in contracts) {
483                                 if (m.RuntimeType == t)
484                                         l.Add (m);
485                         }
486                         foreach (SerializationMap m in l) {
487                                 contracts.Remove (m);
488                                 base.RemoveItem (index);
489                         }
490                 }
491
492                 protected override void SetItem (int index, Type type)
493                 {
494                         if (ShouldNotRegister (type))
495                                 return;
496
497                         // Since this collection is not assured to be ordered, it ignores the whole Set operation if the type already exists.
498                         if (Contains (type))
499                                 return;
500
501                         if (index != Count)
502                                 RemoveItem (index);
503                         if (TryRegister (type))
504                                 base.InsertItem (index - 1, type);
505                 }
506
507                 internal SerializationMap FindUserMap (Type type)
508                 {
509                         lock (this) {
510                                 for (int i = 0; i < contracts.Count; i++)
511                                         if (type == contracts [i].RuntimeType)
512                                                 return contracts [i];
513                                 return null;
514                         }
515                 }
516
517                 internal SerializationMap FindUserMap (QName qname)
518                 {
519                         lock (this)
520                                 return contracts.FirstOrDefault (c => c.XmlName == qname);
521                 }
522
523                 internal SerializationMap FindUserMap (QName qname, Type type)
524                 {
525                         lock (this)
526                                 return contracts.FirstOrDefault (c => c.XmlName == qname && c.RuntimeType == type);
527                 }
528
529                 internal Type GetSerializedType (Type type)
530                 {
531                         if (IsPrimitiveNotEnum (type))
532                                 return type;
533                         Type element = GetCollectionElementType (type);
534                         if (element == null)
535                                 return type;
536                         QName name = GetQName (type);
537                         var map = FindUserMap (name, type);
538                         if (map != null)
539                                 return map.RuntimeType;
540                         return type;
541                 }
542
543                 internal QName GetQName (Type type)
544                 {
545                         SerializationMap map = FindUserMap (type);
546                         if (map != null)
547                                 // already mapped.
548                                 return map.XmlName;
549                         return GetStaticQName (type);
550                 }
551
552                 public static QName GetStaticQName (Type type)
553                 {
554                         if (IsPrimitiveNotEnum (type))
555                                 return GetPrimitiveTypeName (type);
556
557                         if (type.IsEnum)
558                                 return GetEnumQName (type);
559
560                         QName qname = GetContractQName (type);
561                         if (qname != null)
562                                 return qname;
563
564                         if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") != null)
565                                 //FIXME: Reusing GetSerializableQName here, since we just
566                                 //need name of the type..
567                                 return GetSerializableQName (type);
568
569                         qname = GetCollectionContractQName (type);
570                         if (qname != null)
571                                 return qname;
572
573                         Type element = GetCollectionElementType (type);
574                         if (element != null)
575                                 return GetCollectionQName (element);
576
577                         if (GetAttribute<SerializableAttribute> (type) != null)
578                                 return GetSerializableQName (type);
579
580                         // default type map - still uses GetContractQName().
581                         return GetContractQName (type, null, null);
582                 }
583
584                 internal static QName GetContractQName (Type type)
585                 {
586                         var a = GetAttribute<DataContractAttribute> (type);
587                         return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
588                 }
589
590                 static QName GetCollectionContractQName (Type type)
591                 {
592                         var a = GetAttribute<CollectionDataContractAttribute> (type);
593                         return a == null ? null : GetContractQName (type, a.Name, a.Namespace);
594                 }
595
596                 static QName GetContractQName (Type type, string name, string ns)
597                 {
598                         if (name == null)
599                                 name = GetDefaultName (type);
600                         else if (type.IsGenericType) {
601                                 var args = type.GetGenericArguments ();
602                                 for (int i = 0; i < args.Length; i++)
603                                         name = name.Replace ("{" + i + "}", GetStaticQName (args [i]).Name);
604                         }
605
606                         if (ns == null)
607                                 ns = GetDefaultNamespace (type);
608                         return new QName (name, ns);
609                 }
610
611                 static QName GetEnumQName (Type type)
612                 {
613                         string name = null, ns = null;
614
615                         if (!type.IsEnum)
616                                 return null;
617
618                         var dca = GetAttribute<DataContractAttribute> (type);
619
620                         if (dca != null) {
621                                 ns = dca.Namespace;
622                                 name = dca.Name;
623                         }
624
625                         if (ns == null)
626                                 ns = GetDefaultNamespace (type);
627
628                         if (name == null)
629                                 name = type.Namespace == null ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
630
631                         return new QName (name, ns);
632                 }
633
634                 internal static string GetDefaultName (Type type)
635                 {
636                         // FIXME: there could be decent ways to get
637                         // the same result...
638                         string name = type.Namespace == null || type.Namespace.Length == 0 ? type.Name : type.FullName.Substring (type.Namespace.Length + 1).Replace ('+', '.');
639                         if (type.IsGenericType) {
640                                 name = name.Substring (0, name.IndexOf ('`')) + "Of";
641                                 foreach (var t in type.GetGenericArguments ())
642                                         name += t.Name; // FIXME: check namespaces too
643                         }
644                         return name;
645                 }
646
647                 internal static string GetDefaultNamespace (Type type)
648                 {
649                         foreach (ContractNamespaceAttribute a in type.Assembly.GetCustomAttributes (typeof (ContractNamespaceAttribute), true))
650                                 if (a.ClrNamespace == type.Namespace)
651                                         return a.ContractNamespace;
652                         return DefaultClrNamespaceBase + type.Namespace;
653                 }
654
655                 static QName GetCollectionQName (Type element)
656                 {
657                         QName eqname = GetStaticQName (element);
658
659                         string ns = eqname.Namespace;
660                         if (eqname.Namespace == MSSimpleNamespace)
661                                 //Arrays of Primitive types
662                                 ns = MSArraysNamespace;
663
664                         return new QName (
665                                 "ArrayOf" + XmlConvert.EncodeLocalName (eqname.Name),
666                                 ns);
667                 }
668
669                 static QName GetSerializableQName (Type type)
670                 {
671 #if !MOONLIGHT
672                         // First, check XmlSchemaProviderAttribute and try GetSchema() to see if it returns a schema in the expected format.
673                         var xpa = type.GetCustomAttribute<XmlSchemaProviderAttribute> (true);
674                         if (xpa != null) {
675                                 var mi = type.GetMethod (xpa.MethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
676                                 if (mi != null) {
677                                         try {
678                                                 var xss = new XmlSchemaSet ();
679                                                 return (XmlQualifiedName) mi.Invoke (null, new object [] {xss});
680                                         } catch {
681                                                 // ignore.
682                                         }
683                                 }
684                         }
685 #endif
686
687                         string xmlName = type.Name;
688                         if (type.IsGenericType) {
689                                 xmlName = xmlName.Substring (0, xmlName.IndexOf ('`')) + "Of";
690                                 foreach (var t in type.GetGenericArguments ())
691                                         xmlName += GetStaticQName (t).Name; // FIXME: check namespaces too
692                         }
693                         string xmlNamespace = GetDefaultNamespace (type);
694                         var x = GetAttribute<XmlRootAttribute> (type);
695                         if (x != null) {
696                                 xmlName = x.ElementName;
697                                 xmlNamespace = x.Namespace;
698                         }
699                         return new QName (XmlConvert.EncodeLocalName (xmlName), xmlNamespace);
700                 }
701
702                 static bool IsPrimitiveNotEnum (Type type)
703                 {
704                         if (type.IsEnum)
705                                 return false;
706                         if (Type.GetTypeCode (type) != TypeCode.Object) // explicitly primitive
707                                 return true;
708                         if (type == typeof (Guid) || type == typeof (object) || type == typeof(TimeSpan) || type == typeof(byte[]) || type == typeof(Uri) || type == typeof(DateTimeOffset)) // special primitives
709                                 return true;
710 #if !MOONLIGHT
711                         // DOM nodes
712                         if (type == typeof (XmlElement) || type == typeof (XmlNode []))
713                                 return true;
714 #endif
715                         // nullable
716                         if (type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>))
717                                 return IsPrimitiveNotEnum (type.GetGenericArguments () [0]);
718                         return false;
719                 }
720
721                 bool ShouldNotRegister (Type type)
722                 {
723                         return IsPrimitiveNotEnum (type);
724                 }
725
726                 internal bool TryRegister (Type type)
727                 {
728                         lock (this) {
729                                 return DoTryRegister (type);
730                         }
731                 }
732
733                 bool DoTryRegister (Type type)
734                 {
735                         // exclude predefined maps
736                         if (ShouldNotRegister (type))
737                                 return false;
738
739                         if (FindUserMap (type) != null)
740                                 return false;
741
742                         if (RegisterEnum (type) != null)
743                                 return true;
744
745                         if (RegisterDictionary (type) != null)
746                                 return true;
747
748                         if (RegisterCollectionContract (type) != null)
749                                 return true;
750
751                         if (RegisterContract (type) != null)
752                                 return true;
753
754                         if (RegisterIXmlSerializable (type) != null)
755                                 return true;
756
757                         if (RegisterCollection (type) != null)
758                                 return true;
759
760                         if (GetAttribute<SerializableAttribute> (type) != null) {
761                                 RegisterSerializable (type);
762                                 return true;
763                         }
764
765                         RegisterDefaultTypeMap (type);
766                         return true;
767                 }
768
769                 static Type GetCollectionElementType (Type type)
770                 {
771                         if (type.IsArray)
772                                 return type.GetElementType ();
773                         var ifaces = type.GetInterfacesOrSelfInterface ();
774                         foreach (Type i in ifaces)
775                                 if (i.IsGenericType && i.GetGenericTypeDefinition ().Equals (typeof (IEnumerable<>)))
776                                         return i.GetGenericArguments () [0];
777                         foreach (Type i in ifaces)
778                                 if (i == typeof (IList))
779                                         return typeof (object);
780                         return null;
781                 }
782
783                 internal static T GetAttribute<T> (ICustomAttributeProvider ap) where T : Attribute
784                 {
785                         object [] atts = ap.GetCustomAttributes (typeof (T), false);
786                         return atts.Length == 0 ? null : (T) atts [0];
787                 }
788
789                 private CollectionContractTypeMap RegisterCollectionContract (Type type)
790                 {
791                         var cdca = GetAttribute<CollectionDataContractAttribute> (type);
792                         if (cdca == null)
793                                 return null;
794
795                         Type element = GetCollectionElementType (type);
796                         if (element == null)
797                                 throw new InvalidOperationException (String.Format ("Type '{0}' is marked as collection contract, but it is not a collection", type));
798
799                         TryRegister (element); // must be registered before the name conflict check.
800
801                         QName qname = GetCollectionContractQName (type);
802                         CheckStandardQName (qname);
803                         var map = FindUserMap (qname, type);
804                         if (map != null) {
805                                 var cmap = map as CollectionContractTypeMap;
806                                 if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
807                                         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));
808                         }
809
810                         var ret = new CollectionContractTypeMap (type, cdca, element, qname, this);
811                         contracts.Add (ret);
812                         return ret;
813                 }
814
815                 private CollectionTypeMap RegisterCollection (Type type)
816                 {
817                         Type element = GetCollectionElementType (type);
818                         if (element == null)
819                                 return null;
820
821                         TryRegister (element);
822
823                         QName qname = GetCollectionQName (element);
824
825                         var map = FindUserMap (qname, element);
826                         if (map != null) {
827                                 var cmap = map as CollectionTypeMap;
828                                 if (cmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
829                                         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));
830                                 return cmap;
831                         }
832
833                         CollectionTypeMap ret =
834                                 new CollectionTypeMap (type, element, qname, this);
835                         contracts.Add (ret);
836                         return ret;
837                 }
838
839                 static bool TypeImplementsIDictionary (Type type)
840                 {
841                         foreach (var iface in type.GetInterfacesOrSelfInterface ())
842                                 if (iface == typeof (IDictionary) || (iface.IsGenericType && iface.GetGenericTypeDefinition () == typeof (IDictionary<,>)))
843                                         return true;
844
845                         return false;
846                 }
847
848                 // it also supports contract-based dictionary.
849                 private DictionaryTypeMap RegisterDictionary (Type type)
850                 {
851                         if (!TypeImplementsIDictionary (type))
852                                 return null;
853
854                         var cdca = GetAttribute<CollectionDataContractAttribute> (type);
855
856                         DictionaryTypeMap ret =
857                                 new DictionaryTypeMap (type, cdca, this);
858
859                         TryRegister (ret.KeyType);
860                         TryRegister (ret.ValueType);
861
862                         var map = FindUserMap (ret.XmlName, type);
863                         if (map != null) {
864                                 var dmap = map as DictionaryTypeMap;
865                                 if (dmap == null) // The runtime type may still differ (between array and other IList; see bug #670560)
866                                         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));
867                         }
868                         contracts.Add (ret);
869
870                         return ret;
871                 }
872
873                 private SerializationMap RegisterSerializable (Type type)
874                 {
875                         QName qname = GetSerializableQName (type);
876
877                         if (FindUserMap (qname, type) != null)
878                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
879
880                         SharedTypeMap ret = new SharedTypeMap (type, qname, this);
881                         contracts.Add (ret);
882                         ret.Initialize ();
883                         return ret;
884                 }
885
886                 private SerializationMap RegisterIXmlSerializable (Type type)
887                 {
888                         if (type.GetInterface ("System.Xml.Serialization.IXmlSerializable") == null)
889                                 return null;
890
891                         QName qname = GetSerializableQName (type);
892
893                         if (FindUserMap (qname, type) != null)
894                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
895
896                         XmlSerializableMap ret = new XmlSerializableMap (type, qname, this);
897                         contracts.Add (ret);
898
899                         return ret;
900                 }
901
902                 void CheckStandardQName (QName qname)
903                 {
904                         switch (qname.Namespace) {
905                         case XmlSchema.Namespace:
906                         case XmlSchema.InstanceNamespace:
907                         case MSSimpleNamespace:
908                         case MSArraysNamespace:
909                                 throw new InvalidOperationException (String.Format ("Namespace {0} is reserved and cannot be used for user serialization", qname.Namespace));
910                         }
911
912                 }
913
914                 private SharedContractMap RegisterContract (Type type)
915                 {
916                         QName qname = GetContractQName (type);
917                         if (qname == null)
918                                 return null;
919                         CheckStandardQName (qname);
920                         if (FindUserMap (qname, type) != null)
921                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
922
923                         SharedContractMap ret = new SharedContractMap (type, qname, this);
924                         contracts.Add (ret);
925                         ret.Initialize ();
926
927                         if (type.BaseType != typeof (object)) {
928                                 TryRegister (type.BaseType);
929                                 if (!FindUserMap (type.BaseType).IsContractAllowedType)
930                                         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));
931                         }
932
933                         object [] attrs = type.GetCustomAttributes (typeof (KnownTypeAttribute), true);
934                         for (int i = 0; i < attrs.Length; i++) {
935                                 KnownTypeAttribute kt = (KnownTypeAttribute) attrs [i];
936                                 foreach (var t in kt.GetTypes (type))
937                                         TryRegister (t);
938                         }
939
940                         return ret;
941                 }
942
943                 DefaultTypeMap RegisterDefaultTypeMap (Type type)
944                 {
945                         DefaultTypeMap ret = new DefaultTypeMap (type, this);
946                         contracts.Add (ret);
947                         ret.Initialize ();
948                         return ret;
949                 }
950
951                 private EnumMap RegisterEnum (Type type)
952                 {
953                         QName qname = GetEnumQName (type);
954                         if (qname == null)
955                                 return null;
956
957                         if (FindUserMap (qname, type) != null)
958                                 throw new InvalidOperationException (String.Format ("There is already a registered type for XML name {0}", qname));
959
960                         EnumMap ret =
961                                 new EnumMap (type, qname, this);
962                         contracts.Add (ret);
963                         return ret;
964                 }
965         }
966 }
967 #endif