Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Runtime.Serialization / System / Runtime / Serialization / DataContractSet.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Runtime.Serialization
6 {
7     using System;
8     using System.Xml;
9     using System.Xml.Schema;
10     using System.Collections;
11     using System.Collections.Generic;
12     using System.Reflection;
13     using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, DataContract>;
14     using System.Text;
15
16     internal class DataContractSet
17     {
18         Dictionary<XmlQualifiedName, DataContract> contracts;
19         Dictionary<DataContract, object> processedContracts;
20         IDataContractSurrogate dataContractSurrogate;
21         Hashtable surrogateDataTable;
22         DataContractDictionary knownTypesForObject;
23         ICollection<Type> referencedTypes;
24         ICollection<Type> referencedCollectionTypes;
25         Dictionary<XmlQualifiedName, object> referencedTypesDictionary;
26         Dictionary<XmlQualifiedName, object> referencedCollectionTypesDictionary;
27
28
29         internal DataContractSet(IDataContractSurrogate dataContractSurrogate) : this(dataContractSurrogate, null, null) { }
30
31         internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollection<Type> referencedTypes, ICollection<Type> referencedCollectionTypes)
32         {
33             this.dataContractSurrogate = dataContractSurrogate;
34             this.referencedTypes = referencedTypes;
35             this.referencedCollectionTypes = referencedCollectionTypes;
36         }
37
38         internal DataContractSet(DataContractSet dataContractSet)
39         {
40             if (dataContractSet == null)
41                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("dataContractSet"));
42
43             this.dataContractSurrogate = dataContractSet.dataContractSurrogate;
44             this.referencedTypes = dataContractSet.referencedTypes;
45             this.referencedCollectionTypes = dataContractSet.referencedCollectionTypes;
46
47             foreach (KeyValuePair<XmlQualifiedName, DataContract> pair in dataContractSet)
48             {
49                 Add(pair.Key, pair.Value);
50             }
51
52             if (dataContractSet.processedContracts != null)
53             {
54                 foreach (KeyValuePair<DataContract, object> pair in dataContractSet.processedContracts)
55                 {
56                     ProcessedContracts.Add(pair.Key, pair.Value);
57                 }
58             }
59         }
60
61         Dictionary<XmlQualifiedName, DataContract> Contracts
62         {
63             get
64             {
65                 if (contracts == null)
66                 {
67                     contracts = new Dictionary<XmlQualifiedName, DataContract>();
68                 }
69                 return contracts;
70             }
71         }
72
73         Dictionary<DataContract, object> ProcessedContracts
74         {
75             get
76             {
77                 if (processedContracts == null)
78                 {
79                     processedContracts = new Dictionary<DataContract, object>();
80                 }
81                 return processedContracts;
82             }
83         }
84
85         Hashtable SurrogateDataTable
86         {
87             get
88             {
89                 if (surrogateDataTable == null)
90                     surrogateDataTable = new Hashtable();
91                 return surrogateDataTable;
92             }
93         }
94
95         internal DataContractDictionary KnownTypesForObject
96         {
97             get { return knownTypesForObject; }
98             set { knownTypesForObject = value; }
99         }
100
101         internal void Add(Type type)
102         {
103             DataContract dataContract = GetDataContract(type);
104             EnsureTypeNotGeneric(dataContract.UnderlyingType);
105             Add(dataContract);
106         }
107
108         internal static void EnsureTypeNotGeneric(Type type)
109         {
110             if (type.ContainsGenericParameters)
111                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.GenericTypeNotExportable, type)));
112         }
113
114         void Add(DataContract dataContract)
115         {
116             Add(dataContract.StableName, dataContract);
117         }
118
119         public void Add(XmlQualifiedName name, DataContract dataContract)
120         {
121             if (dataContract.IsBuiltInDataContract)
122                 return;
123             InternalAdd(name, dataContract);
124         }
125
126         internal void InternalAdd(XmlQualifiedName name, DataContract dataContract)
127         {
128             DataContract dataContractInSet = null;
129             if (Contracts.TryGetValue(name, out dataContractInSet))
130             {
131                 if (!dataContractInSet.Equals(dataContract))
132                 {
133                     if (dataContract.UnderlyingType == null || dataContractInSet.UnderlyingType == null)
134                         throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.DupContractInDataContractSet, dataContract.StableName.Name, dataContract.StableName.Namespace)));
135                     else
136                     {
137                         bool typeNamesEqual = (DataContract.GetClrTypeFullName(dataContract.UnderlyingType) == DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType));
138                         throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.DupTypeContractInDataContractSet, (typeNamesEqual ? dataContract.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContract.UnderlyingType)), (typeNamesEqual ? dataContractInSet.UnderlyingType.AssemblyQualifiedName : DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)), dataContract.StableName.Name, dataContract.StableName.Namespace)));
139                     }
140                 }
141             }
142             else
143             {
144                 Contracts.Add(name, dataContract);
145
146                 if (dataContract is ClassDataContract)
147                 {
148                     AddClassDataContract((ClassDataContract)dataContract);
149                 }
150                 else if (dataContract is CollectionDataContract)
151                 {
152                     AddCollectionDataContract((CollectionDataContract)dataContract);
153                 }
154                 else if (dataContract is XmlDataContract)
155                 {
156                     AddXmlDataContract((XmlDataContract)dataContract);
157                 }
158             }
159         }
160
161         void AddClassDataContract(ClassDataContract classDataContract)
162         {
163             if (classDataContract.BaseContract != null)
164             {
165                 Add(classDataContract.BaseContract.StableName, classDataContract.BaseContract);
166             }
167             if (!classDataContract.IsISerializable)
168             {
169                 if (classDataContract.Members != null)
170                 {
171                     for (int i = 0; i < classDataContract.Members.Count; i++)
172                     {
173                         DataMember dataMember = classDataContract.Members[i];
174                         DataContract memberDataContract = GetMemberTypeDataContract(dataMember);
175                         if (dataContractSurrogate != null && dataMember.MemberInfo != null)
176                         {
177                             object customData = DataContractSurrogateCaller.GetCustomDataToExport(
178                                                    dataContractSurrogate,
179                                                    dataMember.MemberInfo,
180                                                    memberDataContract.UnderlyingType);
181                             if (customData != null)
182                                 SurrogateDataTable.Add(dataMember, customData);
183                         }
184                         Add(memberDataContract.StableName, memberDataContract);
185                     }
186                 }
187             }
188             AddKnownDataContracts(classDataContract.KnownDataContracts);
189         }
190
191         void AddCollectionDataContract(CollectionDataContract collectionDataContract)
192         {
193             if (collectionDataContract.IsDictionary)
194             {
195                 ClassDataContract keyValueContract = collectionDataContract.ItemContract as ClassDataContract;
196                 AddClassDataContract(keyValueContract);
197             }
198             else
199             {
200                 DataContract itemContract = GetItemTypeDataContract(collectionDataContract);
201                 if (itemContract != null)
202                     Add(itemContract.StableName, itemContract);
203             }
204             AddKnownDataContracts(collectionDataContract.KnownDataContracts);
205         }
206
207         void AddXmlDataContract(XmlDataContract xmlDataContract)
208         {
209             AddKnownDataContracts(xmlDataContract.KnownDataContracts);
210         }
211
212         void AddKnownDataContracts(DataContractDictionary knownDataContracts)
213         {
214             if (knownDataContracts != null)
215             {
216                 foreach (DataContract knownDataContract in knownDataContracts.Values)
217                 {
218                     Add(knownDataContract);
219                 }
220             }
221         }
222
223         internal XmlQualifiedName GetStableName(Type clrType)
224         {
225             if (dataContractSurrogate != null)
226             {
227                 Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, clrType);
228
229                 //if (clrType.IsValueType != dcType.IsValueType)
230                 //    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ValueTypeMismatchInSurrogatedType, dcType, clrType)));
231                 return DataContract.GetStableName(dcType);
232             }
233             return DataContract.GetStableName(clrType);
234         }
235
236         internal DataContract GetDataContract(Type clrType)
237         {
238             if (dataContractSurrogate == null)
239                 return DataContract.GetDataContract(clrType);
240             DataContract dataContract = DataContract.GetBuiltInDataContract(clrType);
241             if (dataContract != null)
242                 return dataContract;
243             Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, clrType);
244             //if (clrType.IsValueType != dcType.IsValueType)
245             //    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ValueTypeMismatchInSurrogatedType, dcType, clrType)));
246             dataContract = DataContract.GetDataContract(dcType);
247             if (!SurrogateDataTable.Contains(dataContract))
248             {
249                 object customData = DataContractSurrogateCaller.GetCustomDataToExport(
250                                       dataContractSurrogate, clrType, dcType);
251                 if (customData != null)
252                     SurrogateDataTable.Add(dataContract, customData);
253             }
254             return dataContract;
255         }
256
257         internal DataContract GetMemberTypeDataContract(DataMember dataMember)
258         {
259             if (dataMember.MemberInfo != null)
260             {
261                 Type dataMemberType = dataMember.MemberType;
262                 if (dataMember.IsGetOnlyCollection)
263                 {
264                     if (dataContractSurrogate != null)
265                     {
266                         Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, dataMemberType);
267                         if (dcType != dataMemberType)
268                         {
269                             throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupported,
270                                 DataContract.GetClrTypeFullName(dataMemberType), DataContract.GetClrTypeFullName(dataMember.MemberInfo.DeclaringType), dataMember.MemberInfo.Name)));
271                         }
272                     }
273                     return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType, SerializationMode.SharedContract);
274                 }
275                 else
276                 {
277                     return GetDataContract(dataMemberType);
278                 }
279             }
280             return dataMember.MemberTypeContract;
281         }
282
283         internal DataContract GetItemTypeDataContract(CollectionDataContract collectionContract)
284         {
285             if (collectionContract.ItemType != null)
286                 return GetDataContract(collectionContract.ItemType);
287             return collectionContract.ItemContract;
288         }
289
290         internal object GetSurrogateData(object key)
291         {
292             return SurrogateDataTable[key];
293         }
294
295         internal void SetSurrogateData(object key, object surrogateData)
296         {
297             SurrogateDataTable[key] = surrogateData;
298         }
299
300         public DataContract this[XmlQualifiedName key]
301         {
302             get
303             {
304                 DataContract dataContract = DataContract.GetBuiltInDataContract(key.Name, key.Namespace);
305                 if (dataContract == null)
306                 {
307                     Contracts.TryGetValue(key, out dataContract);
308                 }
309                 return dataContract;
310             }
311         }
312
313         public IDataContractSurrogate DataContractSurrogate
314         {
315             get { return dataContractSurrogate; }
316         }
317
318         public bool Remove(XmlQualifiedName key)
319         {
320             if (DataContract.GetBuiltInDataContract(key.Name, key.Namespace) != null)
321                 return false;
322             return Contracts.Remove(key);
323         }
324
325         public IEnumerator<KeyValuePair<XmlQualifiedName, DataContract>> GetEnumerator()
326         {
327             return Contracts.GetEnumerator();
328         }
329
330         internal bool IsContractProcessed(DataContract dataContract)
331         {
332             return ProcessedContracts.ContainsKey(dataContract);
333         }
334
335         internal void SetContractProcessed(DataContract dataContract)
336         {
337             ProcessedContracts.Add(dataContract, dataContract);
338         }
339
340 #if !NO_CODEDOM
341         internal ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract)
342         {
343             object info;
344             if (ProcessedContracts.TryGetValue(dataContract, out info))
345                 return (ContractCodeDomInfo)info;
346             return null;
347         }
348
349         internal void SetContractCodeDomInfo(DataContract dataContract, ContractCodeDomInfo info)
350         {
351             ProcessedContracts.Add(dataContract, info);
352         }
353 #endif
354         Dictionary<XmlQualifiedName, object> GetReferencedTypes()
355         {
356             if (referencedTypesDictionary == null)
357             {
358                 referencedTypesDictionary = new Dictionary<XmlQualifiedName, object>();
359                 //Always include Nullable as referenced type
360                 //Do not allow surrogating Nullable<T>
361                 referencedTypesDictionary.Add(DataContract.GetStableName(Globals.TypeOfNullable), Globals.TypeOfNullable);
362                 if (this.referencedTypes != null)
363                 {
364                     foreach (Type type in this.referencedTypes)
365                     {
366                         if (type == null)
367                             throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReferencedTypesCannotContainNull)));
368
369                         AddReferencedType(referencedTypesDictionary, type);
370                     }
371                 }
372             }
373             return referencedTypesDictionary;
374         }
375
376         Dictionary<XmlQualifiedName, object> GetReferencedCollectionTypes()
377         {
378             if (referencedCollectionTypesDictionary == null)
379             {
380                 referencedCollectionTypesDictionary = new Dictionary<XmlQualifiedName, object>();
381                 if (this.referencedCollectionTypes != null)
382                 {
383                     foreach (Type type in this.referencedCollectionTypes)
384                     {
385                         if (type == null)
386                             throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReferencedCollectionTypesCannotContainNull)));
387                         AddReferencedType(referencedCollectionTypesDictionary, type);
388                     }
389                 }
390                 XmlQualifiedName genericDictionaryName = DataContract.GetStableName(Globals.TypeOfDictionaryGeneric);
391                 if (!referencedCollectionTypesDictionary.ContainsKey(genericDictionaryName) && GetReferencedTypes().ContainsKey(genericDictionaryName))
392                     AddReferencedType(referencedCollectionTypesDictionary, Globals.TypeOfDictionaryGeneric);
393             }
394             return referencedCollectionTypesDictionary;
395         }
396
397         void AddReferencedType(Dictionary<XmlQualifiedName, object> referencedTypes, Type type)
398         {
399             if (IsTypeReferenceable(type))
400             {
401                 XmlQualifiedName stableName;
402                 try
403                 {
404                     stableName = this.GetStableName(type);
405                 }
406                 catch (InvalidDataContractException)
407                 {
408                     // Type not referenceable if we can't get a stable name.
409                     return;
410                 }
411                 catch (InvalidOperationException)
412                 {
413                     // Type not referenceable if we can't get a stable name.
414                     return;
415                 }
416
417                 object value;
418                 if (referencedTypes.TryGetValue(stableName, out value))
419                 {
420                     Type referencedType = value as Type;
421                     if (referencedType != null)
422                     {
423                         if (referencedType != type)
424                         {
425                             referencedTypes.Remove(stableName);
426                             List<Type> types = new List<Type>();
427                             types.Add(referencedType);
428                             types.Add(type);
429                             referencedTypes.Add(stableName, types);
430                         }
431                     }
432                     else
433                     {
434                         List<Type> types = (List<Type>)value;
435                         if (!types.Contains(type))
436                             types.Add(type);
437                     }
438                 }
439                 else
440                     referencedTypes.Add(stableName, type);
441             }
442         }
443         internal bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, out Type type)
444         {
445             return TryGetReferencedType(stableName, dataContract, false/*useReferencedCollectionTypes*/, out type);
446         }
447
448         internal bool TryGetReferencedCollectionType(XmlQualifiedName stableName, DataContract dataContract, out Type type)
449         {
450             return TryGetReferencedType(stableName, dataContract, true/*useReferencedCollectionTypes*/, out type);
451         }
452
453         bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, bool useReferencedCollectionTypes, out Type type)
454         {
455             object value;
456             Dictionary<XmlQualifiedName, object> referencedTypes = useReferencedCollectionTypes ? GetReferencedCollectionTypes() : GetReferencedTypes();
457             if (referencedTypes.TryGetValue(stableName, out value))
458             {
459                 type = value as Type;
460                 if (type != null)
461                     return true;
462                 else
463                 {
464                     // Throw ambiguous type match exception
465                     List<Type> types = (List<Type>)value;
466                     StringBuilder errorMessage = new StringBuilder();
467                     bool containsGenericType = false;
468                     for (int i = 0; i < types.Count; i++)
469                     {
470                         Type conflictingType = types[i];
471                         if (!containsGenericType)
472                             containsGenericType = conflictingType.IsGenericTypeDefinition;
473                         errorMessage.AppendFormat("{0}\"{1}\" ", Environment.NewLine, conflictingType.AssemblyQualifiedName);
474                         if (dataContract != null)
475                         {
476                             DataContract other = this.GetDataContract(conflictingType);
477                             errorMessage.Append(SR.GetString(((other != null && other.Equals(dataContract)) ? SR.ReferencedTypeMatchingMessage : SR.ReferencedTypeNotMatchingMessage)));
478                         }
479                     }
480                     if (containsGenericType)
481                     {
482                         throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
483                             (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes1 : SR.AmbiguousReferencedTypes1),
484                             errorMessage.ToString())));
485                     }
486                     else
487                     {
488                         throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
489                             (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes3 : SR.AmbiguousReferencedTypes3),
490                             XmlConvert.DecodeName(stableName.Name),
491                             stableName.Namespace,
492                             errorMessage.ToString())));
493                     }
494                 }
495             }
496             type = null;
497             return false;
498         }
499
500         static bool IsTypeReferenceable(Type type)
501         {
502             Type itemType;
503
504             try
505             {
506                 return (type.IsSerializable ||
507                         type.IsDefined(Globals.TypeOfDataContractAttribute, false) ||
508                         (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) && !type.IsGenericTypeDefinition) ||
509                         CollectionDataContract.IsCollection(type, out itemType) ||
510                         ClassDataContract.IsNonAttributedTypeValidForSerialization(type));
511             }
512             catch (Exception ex)
513             {
514                 // An exception can be thrown in the designer when a project has a runtime binding redirection for a referenced assembly or a reference dependent assembly.
515                 // Type.IsDefined is known to throw System.IO.FileLoadException.
516                 // ClassDataContract.IsNonAttributedTypeValidForSerialization is known to throw System.IO.FileNotFoundException.
517                 // We guard against all non-critical exceptions.
518                 if (Fx.IsFatal(ex))
519                 {
520                     throw;
521                 }
522             }
523             
524             return false;
525         }
526     }
527 }
528
529
530