440aa4dee1faa71e50fa9a3c862f0b4578ee3c51
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / CollectionDataContract.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.Runtime.Serialization
5 {
6     using System;
7     using System.Collections;
8     using System.Diagnostics;
9     using System.Collections.Generic;
10     using System.IO;
11     using System.Globalization;
12     using System.Reflection;
13     using System.Threading;
14     using System.Xml;
15     using System.Runtime.Serialization.Configuration;
16     using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, DataContract>;
17     using System.Security;
18     using System.Security.Permissions;
19
20     [DataContract(Namespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays")]
21 #if USE_REFEMIT
22     public struct KeyValue<K, V>
23 #else
24     internal struct KeyValue<K, V>
25 #endif
26     {
27         K key;
28         V value;
29
30         internal KeyValue(K key, V value)
31         {
32             this.key = key;
33             this.value = value;
34         }
35
36         [DataMember(IsRequired = true)]
37         public K Key
38         {
39             get { return key; }
40             set { key = value; }
41         }
42
43         [DataMember(IsRequired = true)]
44         public V Value
45         {
46             get { return value; }
47             set { this.value = value; }
48         }
49     }
50
51     internal enum CollectionKind : byte
52     {
53         None,
54         GenericDictionary,
55         Dictionary,
56         GenericList,
57         GenericCollection,
58         List,
59         GenericEnumerable,
60         Collection,
61         Enumerable,
62         Array,
63     }
64
65 #if USE_REFEMIT
66     public sealed class CollectionDataContract : DataContract
67 #else
68     internal sealed class CollectionDataContract : DataContract
69 #endif
70     {
71         [Fx.Tag.SecurityNote(Critical = "XmlDictionaryString representing the XML element name for collection items."
72             + "Statically cached and used from IL generated code.")]
73         [SecurityCritical]
74         XmlDictionaryString collectionItemName;
75
76         [Fx.Tag.SecurityNote(Critical = "XmlDictionaryString representing the XML namespace for collection items."
77             + "Statically cached and used from IL generated code.")]
78         [SecurityCritical]
79         XmlDictionaryString childElementNamespace;
80
81         [Fx.Tag.SecurityNote(Critical = "Internal DataContract representing the contract for collection items."
82             + "Statically cached and used from IL generated code.")]
83         [SecurityCritical]
84         DataContract itemContract;
85
86         [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that is cached statically for serialization. "
87             + "Static fields are marked SecurityCritical or readonly to prevent data from being modified or leaked to other components in appdomain.")]
88         [SecurityCritical]
89         CollectionDataContractCriticalHelper helper;
90
91         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
92             Safe = "Doesn't leak anything.")]
93         [SecuritySafeCritical]
94         internal CollectionDataContract(CollectionKind kind)
95             : base(new CollectionDataContractCriticalHelper(kind))
96         {
97             InitCollectionDataContract(this);
98         }
99
100         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
101             Safe = "Doesn't leak anything.")]
102         [SecuritySafeCritical]
103         internal CollectionDataContract(Type type)
104             : base(new CollectionDataContractCriticalHelper(type))
105         {
106             InitCollectionDataContract(this);
107         }
108
109         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
110             Safe = "Doesn't leak anything.")]
111         [SecuritySafeCritical]
112         internal CollectionDataContract(Type type, DataContract itemContract)
113             : base(new CollectionDataContractCriticalHelper(type, itemContract))
114         {
115             InitCollectionDataContract(this);
116         }
117
118
119         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
120             Safe = "Doesn't leak anything.")]
121         [SecuritySafeCritical]
122         CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string serializationExceptionMessage, string deserializationExceptionMessage)
123             : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage))
124         {
125             InitCollectionDataContract(GetSharedTypeContract(type));
126         }
127
128         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
129             Safe = "Doesn't leak anything.")]
130         [SecuritySafeCritical]
131         CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor)
132             : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor))
133         {
134             InitCollectionDataContract(GetSharedTypeContract(type));
135         }
136
137         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
138             Safe = "Doesn't leak anything.")]
139         [SecuritySafeCritical]
140         CollectionDataContract(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor, bool isConstructorCheckRequired)
141             : base(new CollectionDataContractCriticalHelper(type, kind, itemType, getEnumeratorMethod, addMethod, constructor, isConstructorCheckRequired))
142         {
143             InitCollectionDataContract(GetSharedTypeContract(type));
144         }
145
146         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'.",
147             Safe = "Doesn't leak anything.")]
148         [SecuritySafeCritical]
149         CollectionDataContract(Type type, string invalidCollectionInSharedContractMessage)
150             : base(new CollectionDataContractCriticalHelper(type, invalidCollectionInSharedContractMessage))
151         {
152             InitCollectionDataContract(GetSharedTypeContract(type));
153         }
154
155         [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical fields; called from all constructors.")]
156         [SecurityCritical]
157         void InitCollectionDataContract(DataContract sharedTypeContract)
158         {
159             this.helper = base.Helper as CollectionDataContractCriticalHelper;
160             this.collectionItemName = helper.CollectionItemName;
161             if (helper.Kind == CollectionKind.Dictionary || helper.Kind == CollectionKind.GenericDictionary)
162             {
163                 this.itemContract = helper.ItemContract;
164             }
165             this.helper.SharedTypeContract = sharedTypeContract;
166         }
167
168         void InitSharedTypeContract()
169         {
170         }
171
172         static Type[] KnownInterfaces
173         {
174             [Fx.Tag.SecurityNote(Critical = "Fetches the critical knownInterfaces property.",
175                 Safe = "knownInterfaces only needs to be protected for write.")]
176             [SecuritySafeCritical]
177             get { return CollectionDataContractCriticalHelper.KnownInterfaces; }
178         }
179
180         internal CollectionKind Kind
181         {
182             [Fx.Tag.SecurityNote(Critical = "Fetches the critical kind property.",
183                 Safe = "kind only needs to be protected for write.")]
184             [SecuritySafeCritical]
185             get { return helper.Kind; }
186         }
187
188         internal Type ItemType
189         {
190             [Fx.Tag.SecurityNote(Critical = "Fetches the critical itemType property.",
191                 Safe = "itemType only needs to be protected for write.")]
192             [SecuritySafeCritical]
193             get { return helper.ItemType; }
194         }
195
196         public DataContract ItemContract
197         {
198             [Fx.Tag.SecurityNote(Critical = "Fetches the critical itemContract property.",
199                 Safe = "itemContract only needs to be protected for write.")]
200             [SecuritySafeCritical]
201             get { return itemContract ?? helper.ItemContract; }
202
203             [Fx.Tag.SecurityNote(Critical = "Sets the critical itemContract property.")]
204             [SecurityCritical]
205             set
206             {
207                 itemContract = value;
208                 helper.ItemContract = value;
209             }
210         }
211
212         internal DataContract SharedTypeContract
213         {
214             [Fx.Tag.SecurityNote(Critical = "Fetches the critical sharedTypeContract property.",
215                 Safe = "sharedTypeContract only needs to be protected for write.")]
216             [SecuritySafeCritical]
217             get { return helper.SharedTypeContract; }
218         }
219
220         internal string ItemName
221         {
222             [Fx.Tag.SecurityNote(Critical = "Fetches the critical itemName property.",
223                 Safe = "itemName only needs to be protected for write.")]
224             [SecuritySafeCritical]
225             get { return helper.ItemName; }
226
227             [Fx.Tag.SecurityNote(Critical = "Sets the critical itemName property.")]
228             [SecurityCritical]
229             set { helper.ItemName = value; }
230         }
231
232         public XmlDictionaryString CollectionItemName
233         {
234             [Fx.Tag.SecurityNote(Critical = "Fetches the critical collectionItemName property.",
235                 Safe = "collectionItemName only needs to be protected for write.")]
236             [SecuritySafeCritical]
237             get { return this.collectionItemName; }
238         }
239
240         internal string KeyName
241         {
242             [Fx.Tag.SecurityNote(Critical = "Fetches the critical keyName property.",
243                 Safe = "keyName only needs to be protected for write.")]
244             [SecuritySafeCritical]
245             get { return helper.KeyName; }
246
247             [Fx.Tag.SecurityNote(Critical = "Sets the critical keyName property.")]
248             [SecurityCritical]
249             set { helper.KeyName = value; }
250         }
251
252         internal string ValueName
253         {
254             [Fx.Tag.SecurityNote(Critical = "Fetches the critical valueName property.",
255                 Safe = "valueName only needs to be protected for write.")]
256             [SecuritySafeCritical]
257             get { return helper.ValueName; }
258
259             [Fx.Tag.SecurityNote(Critical = "Sets the critical valueName property.")]
260             [SecurityCritical]
261             set { helper.ValueName = value; }
262         }
263
264         internal bool IsDictionary
265         {
266             get { return KeyName != null; }
267         }
268
269         public XmlDictionaryString ChildElementNamespace
270         {
271             [Fx.Tag.SecurityNote(Critical = "Fetches the critical childElementNamespace property.",
272                 Safe = "childElementNamespace only needs to be protected for write; initialized in getter if null.")]
273             [SecuritySafeCritical]
274             get
275             {
276                 if (this.childElementNamespace == null)
277                 {
278                     lock (this)
279                     {
280                         if (this.childElementNamespace == null)
281                         {
282                             if (helper.ChildElementNamespace == null && !IsDictionary)
283                             {
284                                 XmlDictionaryString tempChildElementNamespace = ClassDataContract.GetChildNamespaceToDeclare(this, ItemType, new XmlDictionary());
285                                 Thread.MemoryBarrier();
286                                 helper.ChildElementNamespace = tempChildElementNamespace;
287                             }
288                             this.childElementNamespace = helper.ChildElementNamespace;
289                         }
290                     }
291                 }
292                 return childElementNamespace;
293             }
294         }
295
296         internal bool IsItemTypeNullable
297         {
298             [Fx.Tag.SecurityNote(Critical = "Fetches the critical isItemTypeNullable property.",
299                 Safe = "isItemTypeNullable only needs to be protected for write.")]
300             [SecuritySafeCritical]
301             get { return helper.IsItemTypeNullable; }
302
303             [Fx.Tag.SecurityNote(Critical = "Sets the critical isItemTypeNullable property.")]
304             [SecurityCritical]
305             set { helper.IsItemTypeNullable = value; }
306         }
307
308         internal bool IsConstructorCheckRequired
309         {
310             [Fx.Tag.SecurityNote(Critical = "Fetches the critical isConstructorCheckRequired property.",
311                 Safe = "isConstructorCheckRequired only needs to be protected for write.")]
312             [SecuritySafeCritical]
313             get { return helper.IsConstructorCheckRequired; }
314
315             [Fx.Tag.SecurityNote(Critical = "Sets the critical isConstructorCheckRequired property.")]
316             [SecurityCritical]
317             set { helper.IsConstructorCheckRequired = value; }
318         }
319
320         internal MethodInfo GetEnumeratorMethod
321         {
322             [Fx.Tag.SecurityNote(Critical = "Fetches the critical getEnumeratorMethod property.",
323                 Safe = "getEnumeratorMethod only needs to be protected for write.")]
324             [SecuritySafeCritical]
325             get { return helper.GetEnumeratorMethod; }
326         }
327
328         internal MethodInfo AddMethod
329         {
330             [Fx.Tag.SecurityNote(Critical = "Fetches the critical addMethod property.",
331                 Safe = "addMethod only needs to be protected for write.")]
332             [SecuritySafeCritical]
333             get { return helper.AddMethod; }
334         }
335
336         internal ConstructorInfo Constructor
337         {
338             [Fx.Tag.SecurityNote(Critical = "Fetches the critical constructor property.",
339                 Safe = "constructor only needs to be protected for write.")]
340             [SecuritySafeCritical]
341             get { return helper.Constructor; }
342         }
343
344         internal override DataContractDictionary KnownDataContracts
345         {
346             [Fx.Tag.SecurityNote(Critical = "Fetches the critical knownDataContracts property.",
347                 Safe = "knownDataContracts only needs to be protected for write.")]
348             [SecuritySafeCritical]
349             get { return helper.KnownDataContracts; }
350
351             [Fx.Tag.SecurityNote(Critical = "Sets the critical knownDataContracts property.")]
352             [SecurityCritical]
353             set { helper.KnownDataContracts = value; }
354         }
355
356         internal string InvalidCollectionInSharedContractMessage
357         {
358             [Fx.Tag.SecurityNote(Critical = "Fetches the critical invalidCollectionInSharedContractMessage property.",
359                 Safe = "invalidCollectionInSharedContractMessage only needs to be protected for write.")]
360             [SecuritySafeCritical]
361             get { return helper.InvalidCollectionInSharedContractMessage; }
362         }
363
364         internal string SerializationExceptionMessage
365         {
366             [Fx.Tag.SecurityNote(Critical = "Fetches the critical serializationExceptionMessage property.",
367                 Safe = "serializationExceptionMessage only needs to be protected for write.")]
368             [SecuritySafeCritical]
369             get { return helper.SerializationExceptionMessage; }
370         }
371
372         internal string DeserializationExceptionMessage
373         {
374             [Fx.Tag.SecurityNote(Critical = "Fetches the critical deserializationExceptionMessage property.",
375                 Safe = "deserializationExceptionMessage only needs to be protected for write.")]
376             [SecuritySafeCritical]
377             get { return helper.DeserializationExceptionMessage; }
378         }
379
380         internal bool IsReadOnlyContract
381         {
382             get { return this.DeserializationExceptionMessage != null; }
383         }
384
385         bool ItemNameSetExplicit
386         {
387             [Fx.Tag.SecurityNote(Critical = "Fetches the critical itemNameSetExplicit property.",
388                 Safe = "itemNameSetExplicit only needs to be protected for write.")]
389             [SecuritySafeCritical]
390             get { return helper.ItemNameSetExplicit; }
391         }
392
393         internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate
394         {
395             [Fx.Tag.SecurityNote(Critical = "Fetches the critical xmlFormatWriterDelegate property.",
396                 Safe = "xmlFormatWriterDelegate only needs to be protected for write; initialized in getter if null.")]
397             [SecuritySafeCritical]
398             get
399             {
400                 if (helper.XmlFormatWriterDelegate == null)
401                 {
402                     lock (this)
403                     {
404                         if (helper.XmlFormatWriterDelegate == null)
405                         {
406                             XmlFormatCollectionWriterDelegate tempDelegate = new XmlFormatWriterGenerator().GenerateCollectionWriter(this);
407                             Thread.MemoryBarrier();
408                             helper.XmlFormatWriterDelegate = tempDelegate;
409                         }
410                     }
411                 }
412                 return helper.XmlFormatWriterDelegate;
413             }
414         }
415
416         internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate
417         {
418             [Fx.Tag.SecurityNote(Critical = "Fetches the critical xmlFormatReaderDelegate property.",
419                 Safe = "xmlFormatReaderDelegate only needs to be protected for write; initialized in getter if null.")]
420             [SecuritySafeCritical]
421             get
422             {
423                 if (helper.XmlFormatReaderDelegate == null)
424                 {
425                     lock (this)
426                     {
427                         if (helper.XmlFormatReaderDelegate == null)
428                         {
429                             if (this.IsReadOnlyContract)
430                             {
431                                 ThrowInvalidDataContractException(helper.DeserializationExceptionMessage, null /*type*/);
432                             }
433                             XmlFormatCollectionReaderDelegate tempDelegate = new XmlFormatReaderGenerator().GenerateCollectionReader(this);
434                             Thread.MemoryBarrier();
435                             helper.XmlFormatReaderDelegate = tempDelegate;
436                         }
437                     }
438                 }
439                 return helper.XmlFormatReaderDelegate;
440             }
441         }
442
443         internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate
444         {
445             [Fx.Tag.SecurityNote(Critical = "Fetches the critical xmlFormatGetOnlyCollectionReaderDelegate property.",
446                 Safe = "xmlFormatGetOnlyCollectionReaderDelegate only needs to be protected for write; initialized in getter if null.")]
447             [SecuritySafeCritical]
448             get
449             {
450                 if (helper.XmlFormatGetOnlyCollectionReaderDelegate == null)
451                 {
452                     lock (this)
453                     {
454                         if (helper.XmlFormatGetOnlyCollectionReaderDelegate == null)
455                         {
456                             if (this.UnderlyingType.IsInterface && (this.Kind == CollectionKind.Enumerable || this.Kind == CollectionKind.Collection || this.Kind == CollectionKind.GenericEnumerable))
457                             {
458                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.GetOnlyCollectionMustHaveAddMethod, DataContract.GetClrTypeFullName(this.UnderlyingType))));
459                             }
460                             if (this.IsReadOnlyContract)
461                             {
462                                 ThrowInvalidDataContractException(helper.DeserializationExceptionMessage, null /*type*/);
463                             }
464                             Fx.Assert(this.AddMethod != null || this.Kind == CollectionKind.Array, "Add method cannot be null if the collection is being used as a get-only property");
465                             XmlFormatGetOnlyCollectionReaderDelegate tempDelegate = new XmlFormatReaderGenerator().GenerateGetOnlyCollectionReader(this);
466                             Thread.MemoryBarrier();
467                             helper.XmlFormatGetOnlyCollectionReaderDelegate = tempDelegate;
468                         }
469                     }
470                 }
471                 return helper.XmlFormatGetOnlyCollectionReaderDelegate;
472             }
473         }
474
475         [Fx.Tag.SecurityNote(Critical = "Holds all state used for (de)serializing collections. Since the data is cached statically, we lock down access to it.")]
476         [SecurityCritical(SecurityCriticalScope.Everything)]
477         class CollectionDataContractCriticalHelper : DataContract.DataContractCriticalHelper
478         {
479             static Type[] _knownInterfaces;
480
481             Type itemType;
482             bool isItemTypeNullable;
483             CollectionKind kind;
484             readonly MethodInfo getEnumeratorMethod, addMethod;
485             readonly ConstructorInfo constructor;
486             readonly string serializationExceptionMessage, deserializationExceptionMessage;
487             DataContract itemContract;
488             DataContract sharedTypeContract;
489             DataContractDictionary knownDataContracts;
490             bool isKnownTypeAttributeChecked;
491             string itemName;
492             bool itemNameSetExplicit;
493             XmlDictionaryString collectionItemName;
494             string keyName;
495             string valueName;
496             XmlDictionaryString childElementNamespace;
497             string invalidCollectionInSharedContractMessage;
498             XmlFormatCollectionReaderDelegate xmlFormatReaderDelegate;
499             XmlFormatGetOnlyCollectionReaderDelegate xmlFormatGetOnlyCollectionReaderDelegate;
500             XmlFormatCollectionWriterDelegate xmlFormatWriterDelegate;
501             bool isConstructorCheckRequired = false;
502
503             internal static Type[] KnownInterfaces
504             {
505                 get
506                 {
507                     if (_knownInterfaces == null)
508                     {
509                         // Listed in priority order
510                         _knownInterfaces = new Type[]
511                     {
512                         Globals.TypeOfIDictionaryGeneric,
513                         Globals.TypeOfIDictionary,
514                         Globals.TypeOfIListGeneric,
515                         Globals.TypeOfICollectionGeneric,
516                         Globals.TypeOfIList,
517                         Globals.TypeOfIEnumerableGeneric,
518                         Globals.TypeOfICollection,
519                         Globals.TypeOfIEnumerable
520                     };
521                     }
522                     return _knownInterfaces;
523                 }
524             }
525
526             void Init(CollectionKind kind, Type itemType, CollectionDataContractAttribute collectionContractAttribute)
527             {
528                 this.kind = kind;
529                 if (itemType != null)
530                 {
531                     this.itemType = itemType;
532                     this.isItemTypeNullable = DataContract.IsTypeNullable(itemType);
533
534                     bool isDictionary = (kind == CollectionKind.Dictionary || kind == CollectionKind.GenericDictionary);
535                     string itemName = null, keyName = null, valueName = null;
536                     if (collectionContractAttribute != null)
537                     {
538                         if (collectionContractAttribute.IsItemNameSetExplicit)
539                         {
540                             if (collectionContractAttribute.ItemName == null || collectionContractAttribute.ItemName.Length == 0)
541                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidCollectionContractItemName, DataContract.GetClrTypeFullName(UnderlyingType))));
542                             itemName = DataContract.EncodeLocalName(collectionContractAttribute.ItemName);
543                             itemNameSetExplicit = true;
544                         }
545                         if (collectionContractAttribute.IsKeyNameSetExplicit)
546                         {
547                             if (collectionContractAttribute.KeyName == null || collectionContractAttribute.KeyName.Length == 0)
548                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidCollectionContractKeyName, DataContract.GetClrTypeFullName(UnderlyingType))));
549                             if (!isDictionary)
550                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidCollectionContractKeyNoDictionary, DataContract.GetClrTypeFullName(UnderlyingType), collectionContractAttribute.KeyName)));
551                             keyName = DataContract.EncodeLocalName(collectionContractAttribute.KeyName);
552                         }
553                         if (collectionContractAttribute.IsValueNameSetExplicit)
554                         {
555                             if (collectionContractAttribute.ValueName == null || collectionContractAttribute.ValueName.Length == 0)
556                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidCollectionContractValueName, DataContract.GetClrTypeFullName(UnderlyingType))));
557                             if (!isDictionary)
558                                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.InvalidCollectionContractValueNoDictionary, DataContract.GetClrTypeFullName(UnderlyingType), collectionContractAttribute.ValueName)));
559                             valueName = DataContract.EncodeLocalName(collectionContractAttribute.ValueName);
560                         }
561                     }
562
563                     XmlDictionary dictionary = isDictionary ? new XmlDictionary(5) : new XmlDictionary(3);
564                     this.Name = dictionary.Add(this.StableName.Name);
565                     this.Namespace = dictionary.Add(this.StableName.Namespace);
566                     this.itemName = itemName ?? DataContract.GetStableName(DataContract.UnwrapNullableType(itemType)).Name;
567                     this.collectionItemName = dictionary.Add(this.itemName);
568                     if (isDictionary)
569                     {
570                         this.keyName = keyName ?? Globals.KeyLocalName;
571                         this.valueName = valueName ?? Globals.ValueLocalName;
572                     }
573                 }
574                 if (collectionContractAttribute != null)
575                 {
576                     this.IsReference = collectionContractAttribute.IsReference;
577                 }
578             }
579
580             internal CollectionDataContractCriticalHelper(CollectionKind kind)
581                 : base()
582             {
583                 Init(kind, null, null);
584             }
585
586             // array
587             internal CollectionDataContractCriticalHelper(Type type)
588                 : base(type)
589             {
590                 if (type == Globals.TypeOfArray)
591                     type = Globals.TypeOfObjectArray;
592                 if (type.GetArrayRank() > 1)
593                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SupportForMultidimensionalArraysNotPresent)));
594                 this.StableName = DataContract.GetStableName(type);
595                 Init(CollectionKind.Array, type.GetElementType(), null);
596             }
597
598             // array
599             internal CollectionDataContractCriticalHelper(Type type, DataContract itemContract)
600                 : base(type)
601             {
602                 if (type.GetArrayRank() > 1)
603                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SupportForMultidimensionalArraysNotPresent)));
604                 this.StableName = CreateQualifiedName(Globals.ArrayPrefix + itemContract.StableName.Name, itemContract.StableName.Namespace);
605                 this.itemContract = itemContract;
606                 Init(CollectionKind.Array, type.GetElementType(), null);
607             }
608
609             // read-only collection
610             internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, string serializationExceptionMessage, string deserializationExceptionMessage)
611                 : base(type)
612             {
613                 if (getEnumeratorMethod == null)
614                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.CollectionMustHaveGetEnumeratorMethod, DataContract.GetClrTypeFullName(type))));
615                 if (itemType == null)
616                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.CollectionMustHaveItemType, DataContract.GetClrTypeFullName(type))));
617
618                 CollectionDataContractAttribute collectionContractAttribute;
619                 this.StableName = DataContract.GetCollectionStableName(type, itemType, out collectionContractAttribute);
620
621                 Init(kind, itemType, collectionContractAttribute);
622                 this.getEnumeratorMethod = getEnumeratorMethod;
623                 this.serializationExceptionMessage = serializationExceptionMessage;
624                 this.deserializationExceptionMessage = deserializationExceptionMessage;
625             }
626
627             // collection
628             internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor)
629                 : this(type, kind, itemType, getEnumeratorMethod, (string)null, (string)null)
630             {
631                 if (addMethod == null && !type.IsInterface)
632                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.CollectionMustHaveAddMethod, DataContract.GetClrTypeFullName(type))));
633                 this.addMethod = addMethod;
634                 this.constructor = constructor;
635             }
636
637             // collection
638             internal CollectionDataContractCriticalHelper(Type type, CollectionKind kind, Type itemType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, ConstructorInfo constructor, bool isConstructorCheckRequired)
639                 : this(type, kind, itemType, getEnumeratorMethod, addMethod, constructor)
640             {
641                 this.isConstructorCheckRequired = isConstructorCheckRequired;
642             }
643
644             internal CollectionDataContractCriticalHelper(Type type, string invalidCollectionInSharedContractMessage)
645                 : base(type)
646             {
647                 Init(CollectionKind.Collection, null /*itemType*/, null);
648                 this.invalidCollectionInSharedContractMessage = invalidCollectionInSharedContractMessage;
649             }
650
651             internal CollectionKind Kind
652             {
653                 get { return kind; }
654             }
655
656             internal Type ItemType
657             {
658                 get { return itemType; }
659             }
660
661             internal DataContract ItemContract
662             {
663                 get
664                 {
665                     if (itemContract == null && UnderlyingType != null)
666                     {
667                         if (IsDictionary)
668                         {
669                             if (String.CompareOrdinal(KeyName, ValueName) == 0)
670                             {
671                                 DataContract.ThrowInvalidDataContractException(
672                                     SR.GetString(SR.DupKeyValueName, DataContract.GetClrTypeFullName(UnderlyingType), KeyName),
673                                     UnderlyingType);
674                             }
675                             itemContract = ClassDataContract.CreateClassDataContractForKeyValue(ItemType, Namespace, new string[] { KeyName, ValueName });
676                             // Ensure that DataContract gets added to the static DataContract cache for dictionary items
677                             DataContract.GetDataContract(ItemType);
678                         }
679                         else
680                         {
681                             itemContract = DataContract.GetDataContract(ItemType);
682                         }
683                     }
684                     return itemContract;
685                 }
686                 set
687                 {
688                     itemContract = value;
689                 }
690             }
691
692             internal DataContract SharedTypeContract
693             {
694                 get { return sharedTypeContract; }
695                 set { sharedTypeContract = value; }
696             }
697
698             internal string ItemName
699             {
700                 get { return itemName; }
701                 set { itemName = value; }
702             }
703
704             internal bool IsConstructorCheckRequired
705             {
706                 get { return isConstructorCheckRequired; }
707                 set { isConstructorCheckRequired = value; }
708             }
709
710             public XmlDictionaryString CollectionItemName
711             {
712                 get { return collectionItemName; }
713             }
714
715             internal string KeyName
716             {
717                 get { return keyName; }
718                 set { keyName = value; }
719             }
720
721             internal string ValueName
722             {
723                 get { return valueName; }
724                 set { valueName = value; }
725             }
726
727             internal bool IsDictionary
728             {
729                 get { return KeyName != null; }
730             }
731
732             public string SerializationExceptionMessage
733             {
734                 get { return serializationExceptionMessage; }
735             }
736
737             public string DeserializationExceptionMessage
738             {
739                 get { return deserializationExceptionMessage; }
740             }
741
742             public XmlDictionaryString ChildElementNamespace
743             {
744                 get { return childElementNamespace; }
745                 set { childElementNamespace = value; }
746             }
747
748             internal bool IsItemTypeNullable
749             {
750                 get { return isItemTypeNullable; }
751                 set { isItemTypeNullable = value; }
752             }
753
754             internal MethodInfo GetEnumeratorMethod
755             {
756                 get { return getEnumeratorMethod; }
757             }
758
759             internal MethodInfo AddMethod
760             {
761                 get { return addMethod; }
762             }
763
764             internal ConstructorInfo Constructor
765             {
766                 get { return constructor; }
767             }
768
769             internal override DataContractDictionary KnownDataContracts
770             {
771                 get
772                 {
773                     if (!isKnownTypeAttributeChecked && UnderlyingType != null)
774                     {
775                         lock (this)
776                         {
777                             if (!isKnownTypeAttributeChecked)
778                             {
779                                 knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType);
780                                 Thread.MemoryBarrier();
781                                 isKnownTypeAttributeChecked = true;
782                             }
783                         }
784                     }
785                     return knownDataContracts;
786                 }
787                 set { knownDataContracts = value; }
788             }
789
790             internal string InvalidCollectionInSharedContractMessage
791             {
792                 get { return invalidCollectionInSharedContractMessage; }
793             }
794
795             internal bool ItemNameSetExplicit
796             {
797                 get { return itemNameSetExplicit; }
798             }
799
800             internal XmlFormatCollectionWriterDelegate XmlFormatWriterDelegate
801             {
802                 get { return xmlFormatWriterDelegate; }
803                 set { xmlFormatWriterDelegate = value; }
804             }
805
806             internal XmlFormatCollectionReaderDelegate XmlFormatReaderDelegate
807             {
808                 get { return xmlFormatReaderDelegate; }
809                 set { xmlFormatReaderDelegate = value; }
810             }
811
812             internal XmlFormatGetOnlyCollectionReaderDelegate XmlFormatGetOnlyCollectionReaderDelegate
813             {
814                 get { return xmlFormatGetOnlyCollectionReaderDelegate; }
815                 set { xmlFormatGetOnlyCollectionReaderDelegate = value; }
816             }
817         }
818
819         DataContract GetSharedTypeContract(Type type)
820         {
821             if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false))
822             {
823                 return this;
824             }
825             // ClassDataContract.IsNonAttributedTypeValidForSerialization does not need to be called here. It should
826             // never pass because it returns false for types that implement any of CollectionDataContract.KnownInterfaces
827             if (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false))
828             {
829                 return new ClassDataContract(type);
830             }
831             return null;
832         }
833
834         internal static bool IsCollectionInterface(Type type)
835         {
836             if (type.IsGenericType)
837                 type = type.GetGenericTypeDefinition();
838             return ((IList<Type>)KnownInterfaces).Contains(type);
839         }
840
841         internal static bool IsCollection(Type type)
842         {
843             Type itemType;
844             return IsCollection(type, out itemType);
845         }
846
847         internal static bool IsCollection(Type type, out Type itemType)
848         {
849             return IsCollectionHelper(type, out itemType, true /*constructorRequired*/);
850         }
851
852         internal static bool IsCollection(Type type, bool constructorRequired, bool skipIfReadOnlyContract)
853         {
854             Type itemType;
855             return IsCollectionHelper(type, out itemType, constructorRequired, skipIfReadOnlyContract);
856         }
857
858         static bool IsCollectionHelper(Type type, out Type itemType, bool constructorRequired, bool skipIfReadOnlyContract = false)
859         {
860             if (type.IsArray && DataContract.GetBuiltInDataContract(type) == null)
861             {
862                 itemType = type.GetElementType();
863                 return true;
864             }
865             DataContract dataContract;
866             return IsCollectionOrTryCreate(type, false /*tryCreate*/, out dataContract, out itemType, constructorRequired, skipIfReadOnlyContract);
867         }
868
869         internal static bool TryCreate(Type type, out DataContract dataContract)
870         {
871             Type itemType;
872             return IsCollectionOrTryCreate(type, true /*tryCreate*/, out dataContract, out itemType, true /*constructorRequired*/);
873         }
874
875         internal static bool TryCreateGetOnlyCollectionDataContract(Type type, out DataContract dataContract)
876         {
877             Type itemType;
878             if (type.IsArray)
879             {
880                 dataContract = new CollectionDataContract(type);
881                 return true;
882             }
883             else
884             {
885                 return IsCollectionOrTryCreate(type, true /*tryCreate*/, out dataContract, out itemType, false /*constructorRequired*/);
886             }
887         }
888
889         internal static MethodInfo GetTargetMethodWithName(string name, Type type, Type interfaceType)
890         {
891             InterfaceMapping mapping = type.GetInterfaceMap(interfaceType);
892             for (int i = 0; i < mapping.TargetMethods.Length; i++)
893             {
894                 if (mapping.InterfaceMethods[i].Name == name)
895                     return mapping.InterfaceMethods[i];
896             }
897             return null;
898         }
899
900         static bool IsArraySegment(Type t)
901         {
902             return t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(ArraySegment<>));
903         }
904
905         [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.Globalization, FxCop.Rule.DoNotPassLiteralsAsLocalizedParameters, Justification = "Private code.")]
906         static bool IsCollectionOrTryCreate(Type type, bool tryCreate, out DataContract dataContract, out Type itemType, bool constructorRequired, bool skipIfReadOnlyContract = false)
907         {
908             dataContract = null;
909             itemType = Globals.TypeOfObject;
910
911             if (DataContract.GetBuiltInDataContract(type) != null)
912             {
913                 return HandleIfInvalidCollection(type, tryCreate, false/*hasCollectionDataContract*/, false/*isBaseTypeCollection*/,
914                     SR.CollectionTypeCannotBeBuiltIn, null, ref dataContract);
915             }
916             MethodInfo addMethod, getEnumeratorMethod;
917             bool hasCollectionDataContract = IsCollectionDataContract(type);
918             bool isReadOnlyContract = false;
919             string serializationExceptionMessage = null, deserializationExceptionMessage = null;
920             Type baseType = type.BaseType;
921             bool isBaseTypeCollection = (baseType != null && baseType != Globals.TypeOfObject
922                 && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri) ? IsCollection(baseType) : false;
923
924             // Avoid creating an invalid collection contract for Serializable types since we can create a ClassDataContract instead
925             bool createContractWithException = isBaseTypeCollection && !type.IsSerializable;
926
927             if (type.IsDefined(Globals.TypeOfDataContractAttribute, false))
928             {
929                 return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
930                     SR.CollectionTypeCannotHaveDataContract, null, ref dataContract);
931             }
932
933             if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) || IsArraySegment(type))
934             {
935                 return false;
936             }
937
938             if (!Globals.TypeOfIEnumerable.IsAssignableFrom(type))
939             {
940                 return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
941                     SR.CollectionTypeIsNotIEnumerable, null, ref dataContract);
942             }
943             if (type.IsInterface)
944             {
945                 Type interfaceTypeToCheck = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
946                 Type[] knownInterfaces = KnownInterfaces;
947                 for (int i = 0; i < knownInterfaces.Length; i++)
948                 {
949                     if (knownInterfaces[i] == interfaceTypeToCheck)
950                     {
951                         addMethod = null;
952                         if (type.IsGenericType)
953                         {
954                             Type[] genericArgs = type.GetGenericArguments();
955                             if (interfaceTypeToCheck == Globals.TypeOfIDictionaryGeneric)
956                             {
957                                 itemType = Globals.TypeOfKeyValue.MakeGenericType(genericArgs);
958                                 addMethod = type.GetMethod(Globals.AddMethodName);
959                                 getEnumeratorMethod = Globals.TypeOfIEnumerableGeneric.MakeGenericType(Globals.TypeOfKeyValuePair.MakeGenericType(genericArgs)).GetMethod(Globals.GetEnumeratorMethodName);
960                             }
961                             else
962                             {
963                                 itemType = genericArgs[0];
964                                 if (interfaceTypeToCheck == Globals.TypeOfICollectionGeneric || interfaceTypeToCheck == Globals.TypeOfIListGeneric)
965                                 {
966                                     addMethod = Globals.TypeOfICollectionGeneric.MakeGenericType(itemType).GetMethod(Globals.AddMethodName);
967                                 }
968                                 getEnumeratorMethod = Globals.TypeOfIEnumerableGeneric.MakeGenericType(itemType).GetMethod(Globals.GetEnumeratorMethodName);
969                             }
970                         }
971                         else
972                         {
973                             if (interfaceTypeToCheck == Globals.TypeOfIDictionary)
974                             {
975                                 itemType = typeof(KeyValue<object, object>);
976                                 addMethod = type.GetMethod(Globals.AddMethodName);
977                             }
978                             else
979                             {
980                                 itemType = Globals.TypeOfObject;
981                                 if (interfaceTypeToCheck == Globals.TypeOfIList)
982                                 {
983                                     addMethod = Globals.TypeOfIList.GetMethod(Globals.AddMethodName);
984                                 }
985                             }
986                             getEnumeratorMethod = Globals.TypeOfIEnumerable.GetMethod(Globals.GetEnumeratorMethodName);
987                         }
988                         if (tryCreate)
989                             dataContract = new CollectionDataContract(type, (CollectionKind)(i + 1), itemType, getEnumeratorMethod, addMethod, null/*defaultCtor*/);
990                         return true;
991                     }
992                 }
993             }
994             ConstructorInfo defaultCtor = null;
995             if (!type.IsValueType)
996             {
997                 defaultCtor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Globals.EmptyTypeArray, null);
998                 if (defaultCtor == null && constructorRequired)
999                 {
1000                     // All collection types could be considered read-only collections except collection types that are marked [Serializable]. 
1001                     // Collection types marked [Serializable] cannot be read-only collections for backward compatibility reasons.
1002                     // DataContract types and POCO types cannot be collection types, so they don't need to be factored in
1003                     if (type.IsSerializable)
1004                     {
1005                         return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
1006                             SR.CollectionTypeDoesNotHaveDefaultCtor, null, ref dataContract);
1007                     }
1008                     else
1009                     {
1010                         isReadOnlyContract = true;
1011                         GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveDefaultCtor, null, out serializationExceptionMessage, out deserializationExceptionMessage);
1012                     }
1013                 }
1014             }
1015
1016             Type knownInterfaceType = null;
1017             CollectionKind kind = CollectionKind.None;
1018             bool multipleDefinitions = false;
1019             Type[] interfaceTypes = type.GetInterfaces();
1020             foreach (Type interfaceType in interfaceTypes)
1021             {
1022                 Type interfaceTypeToCheck = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType;
1023                 Type[] knownInterfaces = KnownInterfaces;
1024                 for (int i = 0; i < knownInterfaces.Length; i++)
1025                 {
1026                     if (knownInterfaces[i] == interfaceTypeToCheck)
1027                     {
1028                         CollectionKind currentKind = (CollectionKind)(i + 1);
1029                         if (kind == CollectionKind.None || currentKind < kind)
1030                         {
1031                             kind = currentKind;
1032                             knownInterfaceType = interfaceType;
1033                             multipleDefinitions = false;
1034                         }
1035                         else if ((kind & currentKind) == currentKind)
1036                             multipleDefinitions = true;
1037                         break;
1038                     }
1039                 }
1040             }
1041
1042             if (kind == CollectionKind.None)
1043             {
1044                 return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
1045                     SR.CollectionTypeIsNotIEnumerable, null, ref dataContract);
1046             }
1047
1048             if (kind == CollectionKind.Enumerable || kind == CollectionKind.Collection || kind == CollectionKind.GenericEnumerable)
1049             {
1050                 if (multipleDefinitions)
1051                     knownInterfaceType = Globals.TypeOfIEnumerable;
1052                 itemType = knownInterfaceType.IsGenericType ? knownInterfaceType.GetGenericArguments()[0] : Globals.TypeOfObject;
1053                 GetCollectionMethods(type, knownInterfaceType, new Type[] { itemType },
1054                                      false /*addMethodOnInterface*/,
1055                                      out getEnumeratorMethod, out addMethod);
1056                 if (addMethod == null)
1057                 {
1058                     // All collection types could be considered read-only collections except collection types that are marked [Serializable]. 
1059                     // Collection types marked [Serializable] cannot be read-only collections for backward compatibility reasons.
1060                     // DataContract types and POCO types cannot be collection types, so they don't need to be factored in.
1061                     if (type.IsSerializable || skipIfReadOnlyContract)
1062                     {
1063                         return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException && !skipIfReadOnlyContract,
1064                             SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), ref dataContract);
1065                     }
1066                     else
1067                     {
1068                         isReadOnlyContract = true;
1069                         GetReadOnlyCollectionExceptionMessages(type, hasCollectionDataContract, SR.CollectionTypeDoesNotHaveAddMethod, DataContract.GetClrTypeFullName(itemType), out serializationExceptionMessage, out deserializationExceptionMessage);
1070                     }
1071                 }
1072
1073                 if (tryCreate)
1074                 {
1075                     dataContract = isReadOnlyContract ?
1076                         new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) :
1077                         new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired);
1078                 }
1079             }
1080             else
1081             {
1082                 if (multipleDefinitions)
1083                 {
1084                     return HandleIfInvalidCollection(type, tryCreate, hasCollectionDataContract, createContractWithException,
1085                         SR.CollectionTypeHasMultipleDefinitionsOfInterface, KnownInterfaces[(int)kind - 1].Name, ref dataContract);
1086                 }
1087                 Type[] addMethodTypeArray = null;
1088                 switch (kind)
1089                 {
1090                     case CollectionKind.GenericDictionary:
1091                         addMethodTypeArray = knownInterfaceType.GetGenericArguments();
1092                         bool isOpenGeneric = knownInterfaceType.IsGenericTypeDefinition
1093                             || (addMethodTypeArray[0].IsGenericParameter && addMethodTypeArray[1].IsGenericParameter);
1094                         itemType = isOpenGeneric ? Globals.TypeOfKeyValue : Globals.TypeOfKeyValue.MakeGenericType(addMethodTypeArray);
1095                         break;
1096                     case CollectionKind.Dictionary:
1097                         addMethodTypeArray = new Type[] { Globals.TypeOfObject, Globals.TypeOfObject };
1098                         itemType = Globals.TypeOfKeyValue.MakeGenericType(addMethodTypeArray);
1099                         break;
1100                     case CollectionKind.GenericList:
1101                     case CollectionKind.GenericCollection:
1102                         addMethodTypeArray = knownInterfaceType.GetGenericArguments();
1103                         itemType = addMethodTypeArray[0];
1104                         break;
1105                     case CollectionKind.List:
1106                         itemType = Globals.TypeOfObject;
1107                         addMethodTypeArray = new Type[] { itemType };
1108                         break;
1109                 }
1110
1111                 if (tryCreate)
1112                 {
1113                     GetCollectionMethods(type, knownInterfaceType, addMethodTypeArray,
1114                                      true /*addMethodOnInterface*/,
1115                                      out getEnumeratorMethod, out addMethod);
1116                     dataContract = isReadOnlyContract ?
1117                         new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, serializationExceptionMessage, deserializationExceptionMessage) :
1118                         new CollectionDataContract(type, kind, itemType, getEnumeratorMethod, addMethod, defaultCtor, !constructorRequired);
1119                 }
1120             }
1121
1122             return !(isReadOnlyContract && skipIfReadOnlyContract);
1123         }
1124
1125         internal static bool IsCollectionDataContract(Type type)
1126         {
1127             return type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false);
1128         }
1129
1130         static bool HandleIfInvalidCollection(Type type, bool tryCreate, bool hasCollectionDataContract, bool createContractWithException, string message, string param, ref DataContract dataContract)
1131         {
1132             if (hasCollectionDataContract)
1133             {
1134                 if (tryCreate)
1135                     throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(GetInvalidCollectionMessage(message, SR.GetString(SR.InvalidCollectionDataContract, DataContract.GetClrTypeFullName(type)), param)));
1136                 return true;
1137             }
1138
1139             if (createContractWithException)
1140             {
1141                 if (tryCreate)
1142                     dataContract = new CollectionDataContract(type, GetInvalidCollectionMessage(message, SR.GetString(SR.InvalidCollectionType, DataContract.GetClrTypeFullName(type)), param));
1143                 return true;
1144             }
1145
1146             return false;
1147         }
1148
1149         static void GetReadOnlyCollectionExceptionMessages(Type type, bool hasCollectionDataContract, string message, string param, out string serializationExceptionMessage, out string deserializationExceptionMessage)
1150         {
1151             serializationExceptionMessage = GetInvalidCollectionMessage(message, SR.GetString(hasCollectionDataContract ? SR.InvalidCollectionDataContract : SR.InvalidCollectionType, DataContract.GetClrTypeFullName(type)), param);
1152             deserializationExceptionMessage = GetInvalidCollectionMessage(message, SR.GetString(SR.ReadOnlyCollectionDeserialization, DataContract.GetClrTypeFullName(type)), param);
1153         }
1154
1155         static string GetInvalidCollectionMessage(string message, string nestedMessage, string param)
1156         {
1157             return (param == null) ? SR.GetString(message, nestedMessage) : SR.GetString(message, nestedMessage, param);
1158         }
1159
1160         static void FindCollectionMethodsOnInterface(Type type, Type interfaceType, ref MethodInfo addMethod, ref MethodInfo getEnumeratorMethod)
1161         {
1162             InterfaceMapping mapping = type.GetInterfaceMap(interfaceType);
1163             for (int i = 0; i < mapping.TargetMethods.Length; i++)
1164             {
1165                 if (mapping.InterfaceMethods[i].Name == Globals.AddMethodName)
1166                     addMethod = mapping.InterfaceMethods[i];
1167                 else if (mapping.InterfaceMethods[i].Name == Globals.GetEnumeratorMethodName)
1168                     getEnumeratorMethod = mapping.InterfaceMethods[i];
1169             }
1170         }
1171
1172         static void GetCollectionMethods(Type type, Type interfaceType, Type[] addMethodTypeArray, bool addMethodOnInterface, out MethodInfo getEnumeratorMethod, out MethodInfo addMethod)
1173         {
1174             addMethod = getEnumeratorMethod = null;
1175
1176             if (addMethodOnInterface)
1177             {
1178                 addMethod = type.GetMethod(Globals.AddMethodName, BindingFlags.Instance | BindingFlags.Public, null, addMethodTypeArray, null);
1179                 if (addMethod == null || addMethod.GetParameters()[0].ParameterType != addMethodTypeArray[0])
1180                 {
1181                     FindCollectionMethodsOnInterface(type, interfaceType, ref addMethod, ref getEnumeratorMethod);
1182                     if (addMethod == null)
1183                     {
1184                         Type[] parentInterfaceTypes = interfaceType.GetInterfaces();
1185                         foreach (Type parentInterfaceType in parentInterfaceTypes)
1186                         {
1187                             if (IsKnownInterface(parentInterfaceType))
1188                             {
1189                                 FindCollectionMethodsOnInterface(type, parentInterfaceType, ref addMethod, ref getEnumeratorMethod);
1190                                 if (addMethod == null)
1191                                 {
1192                                     break;
1193                                 }
1194                             }
1195                         }
1196                     }
1197                 }
1198             }
1199             else
1200             {
1201                 // GetMethod returns Add() method with parameter closest matching T in assignability/inheritance chain
1202                 addMethod = type.GetMethod(Globals.AddMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, addMethodTypeArray, null);
1203             }
1204
1205             if (getEnumeratorMethod == null)
1206             {
1207                 getEnumeratorMethod = type.GetMethod(Globals.GetEnumeratorMethodName, BindingFlags.Instance | BindingFlags.Public, null, Globals.EmptyTypeArray, null);
1208                 if (getEnumeratorMethod == null || !Globals.TypeOfIEnumerator.IsAssignableFrom(getEnumeratorMethod.ReturnType))
1209                 {
1210                     Type ienumerableInterface = interfaceType.GetInterface("System.Collections.Generic.IEnumerable*");
1211                     if (ienumerableInterface == null)
1212                         ienumerableInterface = Globals.TypeOfIEnumerable;
1213                     getEnumeratorMethod = GetTargetMethodWithName(Globals.GetEnumeratorMethodName, type, ienumerableInterface);
1214                 }
1215             }
1216         }
1217
1218         static bool IsKnownInterface(Type type)
1219         {
1220             Type typeToCheck = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
1221             foreach (Type knownInterfaceType in KnownInterfaces)
1222             {
1223                 if (typeToCheck == knownInterfaceType)
1224                 {
1225                     return true;
1226                 }
1227             }
1228             return false;
1229         }
1230
1231         [Fx.Tag.SecurityNote(Critical = "Sets critical properties on CollectionDataContract .",
1232             Safe = "Called during schema import/code generation.")]
1233         [SecuritySafeCritical]
1234         internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary<DataContract, DataContract> boundContracts)
1235         {
1236             DataContract boundContract;
1237             if (boundContracts.TryGetValue(this, out boundContract))
1238                 return boundContract;
1239
1240             CollectionDataContract boundCollectionContract = new CollectionDataContract(Kind);
1241             boundContracts.Add(this, boundCollectionContract);
1242             boundCollectionContract.ItemContract = this.ItemContract.BindGenericParameters(paramContracts, boundContracts);
1243             boundCollectionContract.IsItemTypeNullable = !boundCollectionContract.ItemContract.IsValueType;
1244             boundCollectionContract.ItemName = ItemNameSetExplicit ? this.ItemName : boundCollectionContract.ItemContract.StableName.Name;
1245             boundCollectionContract.KeyName = this.KeyName;
1246             boundCollectionContract.ValueName = this.ValueName;
1247             boundCollectionContract.StableName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(this.StableName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(this.UnderlyingType), paramContracts)),
1248                 IsCollectionDataContract(UnderlyingType) ? this.StableName.Namespace : DataContract.GetCollectionNamespace(boundCollectionContract.ItemContract.StableName.Namespace));
1249             return boundCollectionContract;
1250         }
1251
1252         internal override DataContract GetValidContract(SerializationMode mode)
1253         {
1254             if (mode == SerializationMode.SharedType)
1255             {
1256                 if (SharedTypeContract == null)
1257                     DataContract.ThrowTypeNotSerializable(UnderlyingType);
1258                 return SharedTypeContract;
1259             }
1260
1261             ThrowIfInvalid();
1262             return this;
1263         }
1264
1265         void ThrowIfInvalid()
1266         {
1267             if (InvalidCollectionInSharedContractMessage != null)
1268                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(InvalidCollectionInSharedContractMessage));
1269         }
1270
1271         internal override DataContract GetValidContract()
1272         {
1273             if (this.IsConstructorCheckRequired)
1274             {
1275                 CheckConstructor();
1276             }
1277             return this;
1278         }
1279
1280         [Fx.Tag.SecurityNote(Critical = "Sets the critical IsConstructorCheckRequired property on CollectionDataContract.",
1281             Safe = "Does not leak anything.")]
1282         [SecuritySafeCritical]
1283         void CheckConstructor()
1284         {
1285             if (this.Constructor == null)
1286             {
1287                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.CollectionTypeDoesNotHaveDefaultCtor, DataContract.GetClrTypeFullName(this.UnderlyingType))));
1288             }
1289             else
1290             {
1291                 this.IsConstructorCheckRequired = false;
1292             }
1293         }
1294
1295         internal override bool IsValidContract(SerializationMode mode)
1296         {
1297             if (mode == SerializationMode.SharedType)
1298                 return (SharedTypeContract != null);
1299             return (InvalidCollectionInSharedContractMessage == null);
1300         }
1301
1302         [Fx.Tag.SecurityNote(Miscellaneous =
1303             "RequiresReview - Calculates whether this collection requires MemberAccessPermission for deserialization."
1304             + " Since this information is used to determine whether to give the generated code access"
1305             + " permissions to private members, any changes to the logic should be reviewed.")]
1306         internal bool RequiresMemberAccessForRead(SecurityException securityException)
1307         {
1308             if (!IsTypeVisible(UnderlyingType))
1309             {
1310                 if (securityException != null)
1311                 {
1312                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1313                         new SecurityException(SR.GetString(
1314                                 SR.PartialTrustCollectionContractTypeNotPublic,
1315                                 DataContract.GetClrTypeFullName(UnderlyingType)),
1316                             securityException));
1317                 }
1318                 return true;
1319             }
1320             if (ItemType != null && !IsTypeVisible(ItemType))
1321             {
1322                 if (securityException != null)
1323                 {
1324                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1325                         new SecurityException(SR.GetString(
1326                                 SR.PartialTrustCollectionContractTypeNotPublic,
1327                                 DataContract.GetClrTypeFullName(ItemType)),
1328                             securityException));
1329                 }
1330                 return true;
1331             }
1332             if (ConstructorRequiresMemberAccess(Constructor))
1333             {
1334                 if (securityException != null)
1335                 {
1336                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1337                         new SecurityException(SR.GetString(
1338                                 SR.PartialTrustCollectionContractNoPublicConstructor,
1339                                 DataContract.GetClrTypeFullName(UnderlyingType)),
1340                             securityException));
1341                 }
1342                 return true;
1343             }
1344             if (MethodRequiresMemberAccess(this.AddMethod))
1345             {
1346                 if (securityException != null)
1347                 {
1348                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1349                            new SecurityException(SR.GetString(
1350                                    SR.PartialTrustCollectionContractAddMethodNotPublic,
1351                                    DataContract.GetClrTypeFullName(UnderlyingType),
1352                                    this.AddMethod.Name),
1353                                securityException));
1354                 }
1355                 return true;
1356             }
1357
1358             return false;
1359         }
1360
1361         [Fx.Tag.SecurityNote(Miscellaneous =
1362             "RequiresReview - Calculates whether this collection requires MemberAccessPermission for serialization."
1363             + " Since this information is used to determine whether to give the generated code access"
1364             + " permissions to private members, any changes to the logic should be reviewed.")]
1365         internal bool RequiresMemberAccessForWrite(SecurityException securityException)
1366         {
1367             if (!IsTypeVisible(UnderlyingType))
1368             {
1369                 if (securityException != null)
1370                 {
1371                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1372                         new SecurityException(SR.GetString(
1373                                 SR.PartialTrustCollectionContractTypeNotPublic,
1374                                 DataContract.GetClrTypeFullName(UnderlyingType)),
1375                             securityException));
1376                 }
1377                 return true;
1378             }
1379             if (ItemType != null && !IsTypeVisible(ItemType))
1380             {
1381                 if (securityException != null)
1382                 {
1383                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1384                         new SecurityException(SR.GetString(
1385                                 SR.PartialTrustCollectionContractTypeNotPublic,
1386                                 DataContract.GetClrTypeFullName(ItemType)),
1387                             securityException));
1388                 }
1389                 return true;
1390             }
1391
1392             return false;
1393         }
1394
1395         internal override bool Equals(object other, Dictionary<DataContractPairKey, object> checkedContracts)
1396         {
1397             if (IsEqualOrChecked(other, checkedContracts))
1398                 return true;
1399
1400             if (base.Equals(other, checkedContracts))
1401             {
1402                 CollectionDataContract dataContract = other as CollectionDataContract;
1403                 if (dataContract != null)
1404                 {
1405                     bool thisItemTypeIsNullable = (ItemContract == null) ? false : !ItemContract.IsValueType;
1406                     bool otherItemTypeIsNullable = (dataContract.ItemContract == null) ? false : !dataContract.ItemContract.IsValueType;
1407                     return ItemName == dataContract.ItemName &&
1408                         (IsItemTypeNullable || thisItemTypeIsNullable) == (dataContract.IsItemTypeNullable || otherItemTypeIsNullable) &&
1409                         ItemContract.Equals(dataContract.ItemContract, checkedContracts);
1410                 }
1411             }
1412             return false;
1413         }
1414
1415         public override int GetHashCode()
1416         {
1417             return base.GetHashCode();
1418         }
1419
1420         public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
1421         {
1422             // IsGetOnlyCollection value has already been used to create current collectiondatacontract, value can now be reset. 
1423             context.IsGetOnlyCollection = false;
1424             XmlFormatWriterDelegate(xmlWriter, obj, context, this);
1425         }
1426
1427         public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
1428         {
1429             xmlReader.Read();
1430             object o = null;
1431             if (context.IsGetOnlyCollection)
1432             {
1433                 // IsGetOnlyCollection value has already been used to create current collectiondatacontract, value can now be reset. 
1434                 context.IsGetOnlyCollection = false;
1435                 XmlFormatGetOnlyCollectionReaderDelegate(xmlReader, context, CollectionItemName, Namespace, this);
1436             }
1437             else
1438             {
1439                 o = XmlFormatReaderDelegate(xmlReader, context, CollectionItemName, Namespace, this);
1440             }
1441             xmlReader.ReadEndElement();
1442             return o;
1443         }
1444
1445         public class DictionaryEnumerator : IEnumerator<KeyValue<object, object>>
1446         {
1447             IDictionaryEnumerator enumerator;
1448
1449             public DictionaryEnumerator(IDictionaryEnumerator enumerator)
1450             {
1451                 this.enumerator = enumerator;
1452             }
1453
1454             public void Dispose()
1455             {
1456             }
1457
1458             public bool MoveNext()
1459             {
1460                 return enumerator.MoveNext();
1461             }
1462
1463             public KeyValue<object, object> Current
1464             {
1465                 get { return new KeyValue<object, object>(enumerator.Key, enumerator.Value); }
1466             }
1467
1468             object System.Collections.IEnumerator.Current
1469             {
1470                 get { return Current; }
1471             }
1472
1473             public void Reset()
1474             {
1475                 enumerator.Reset();
1476             }
1477         }
1478
1479         public class GenericDictionaryEnumerator<K, V> : IEnumerator<KeyValue<K, V>>
1480         {
1481             IEnumerator<KeyValuePair<K, V>> enumerator;
1482
1483             public GenericDictionaryEnumerator(IEnumerator<KeyValuePair<K, V>> enumerator)
1484             {
1485                 this.enumerator = enumerator;
1486             }
1487
1488             public void Dispose()
1489             {
1490             }
1491
1492             public bool MoveNext()
1493             {
1494                 return enumerator.MoveNext();
1495             }
1496
1497             public KeyValue<K, V> Current
1498             {
1499                 get
1500                 {
1501                     KeyValuePair<K, V> current = enumerator.Current;
1502                     return new KeyValue<K, V>(current.Key, current.Value);
1503                 }
1504             }
1505
1506             object System.Collections.IEnumerator.Current
1507             {
1508                 get { return Current; }
1509             }
1510
1511             public void Reset()
1512             {
1513                 enumerator.Reset();
1514             }
1515         }
1516
1517     }
1518 }