1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace System.Runtime.Serialization
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>;
16 internal class DataContractSet
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;
29 internal DataContractSet(IDataContractSurrogate dataContractSurrogate) : this(dataContractSurrogate, null, null) { }
31 internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollection<Type> referencedTypes, ICollection<Type> referencedCollectionTypes)
33 this.dataContractSurrogate = dataContractSurrogate;
34 this.referencedTypes = referencedTypes;
35 this.referencedCollectionTypes = referencedCollectionTypes;
38 internal DataContractSet(DataContractSet dataContractSet)
40 if (dataContractSet == null)
41 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("dataContractSet"));
43 this.dataContractSurrogate = dataContractSet.dataContractSurrogate;
44 this.referencedTypes = dataContractSet.referencedTypes;
45 this.referencedCollectionTypes = dataContractSet.referencedCollectionTypes;
47 foreach (KeyValuePair<XmlQualifiedName, DataContract> pair in dataContractSet)
49 Add(pair.Key, pair.Value);
52 if (dataContractSet.processedContracts != null)
54 foreach (KeyValuePair<DataContract, object> pair in dataContractSet.processedContracts)
56 ProcessedContracts.Add(pair.Key, pair.Value);
61 Dictionary<XmlQualifiedName, DataContract> Contracts
65 if (contracts == null)
67 contracts = new Dictionary<XmlQualifiedName, DataContract>();
73 Dictionary<DataContract, object> ProcessedContracts
77 if (processedContracts == null)
79 processedContracts = new Dictionary<DataContract, object>();
81 return processedContracts;
85 Hashtable SurrogateDataTable
89 if (surrogateDataTable == null)
90 surrogateDataTable = new Hashtable();
91 return surrogateDataTable;
95 internal DataContractDictionary KnownTypesForObject
97 get { return knownTypesForObject; }
98 set { knownTypesForObject = value; }
101 internal void Add(Type type)
103 DataContract dataContract = GetDataContract(type);
104 EnsureTypeNotGeneric(dataContract.UnderlyingType);
108 internal static void EnsureTypeNotGeneric(Type type)
110 if (type.ContainsGenericParameters)
111 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.GenericTypeNotExportable, type)));
114 void Add(DataContract dataContract)
116 Add(dataContract.StableName, dataContract);
119 public void Add(XmlQualifiedName name, DataContract dataContract)
121 if (dataContract.IsBuiltInDataContract)
123 InternalAdd(name, dataContract);
126 internal void InternalAdd(XmlQualifiedName name, DataContract dataContract)
128 DataContract dataContractInSet = null;
129 if (Contracts.TryGetValue(name, out dataContractInSet))
131 if (!dataContractInSet.Equals(dataContract))
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)));
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)));
144 Contracts.Add(name, dataContract);
146 if (dataContract is ClassDataContract)
148 AddClassDataContract((ClassDataContract)dataContract);
150 else if (dataContract is CollectionDataContract)
152 AddCollectionDataContract((CollectionDataContract)dataContract);
154 else if (dataContract is XmlDataContract)
156 AddXmlDataContract((XmlDataContract)dataContract);
161 void AddClassDataContract(ClassDataContract classDataContract)
163 if (classDataContract.BaseContract != null)
165 Add(classDataContract.BaseContract.StableName, classDataContract.BaseContract);
167 if (!classDataContract.IsISerializable)
169 if (classDataContract.Members != null)
171 for (int i = 0; i < classDataContract.Members.Count; i++)
173 DataMember dataMember = classDataContract.Members[i];
174 DataContract memberDataContract = GetMemberTypeDataContract(dataMember);
175 if (dataContractSurrogate != null && dataMember.MemberInfo != null)
177 object customData = DataContractSurrogateCaller.GetCustomDataToExport(
178 dataContractSurrogate,
179 dataMember.MemberInfo,
180 memberDataContract.UnderlyingType);
181 if (customData != null)
182 SurrogateDataTable.Add(dataMember, customData);
184 Add(memberDataContract.StableName, memberDataContract);
188 AddKnownDataContracts(classDataContract.KnownDataContracts);
191 void AddCollectionDataContract(CollectionDataContract collectionDataContract)
193 if (collectionDataContract.IsDictionary)
195 ClassDataContract keyValueContract = collectionDataContract.ItemContract as ClassDataContract;
196 AddClassDataContract(keyValueContract);
200 DataContract itemContract = GetItemTypeDataContract(collectionDataContract);
201 if (itemContract != null)
202 Add(itemContract.StableName, itemContract);
204 AddKnownDataContracts(collectionDataContract.KnownDataContracts);
207 void AddXmlDataContract(XmlDataContract xmlDataContract)
209 AddKnownDataContracts(xmlDataContract.KnownDataContracts);
212 void AddKnownDataContracts(DataContractDictionary knownDataContracts)
214 if (knownDataContracts != null)
216 foreach (DataContract knownDataContract in knownDataContracts.Values)
218 Add(knownDataContract);
223 internal XmlQualifiedName GetStableName(Type clrType)
225 if (dataContractSurrogate != null)
227 Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, clrType);
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);
233 return DataContract.GetStableName(clrType);
236 internal DataContract GetDataContract(Type clrType)
238 if (dataContractSurrogate == null)
239 return DataContract.GetDataContract(clrType);
240 DataContract dataContract = DataContract.GetBuiltInDataContract(clrType);
241 if (dataContract != null)
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))
249 object customData = DataContractSurrogateCaller.GetCustomDataToExport(
250 dataContractSurrogate, clrType, dcType);
251 if (customData != null)
252 SurrogateDataTable.Add(dataContract, customData);
257 internal DataContract GetMemberTypeDataContract(DataMember dataMember)
259 if (dataMember.MemberInfo != null)
261 Type dataMemberType = dataMember.MemberType;
262 if (dataMember.IsGetOnlyCollection)
264 if (dataContractSurrogate != null)
266 Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, dataMemberType);
267 if (dcType != dataMemberType)
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)));
273 return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType, SerializationMode.SharedContract);
277 return GetDataContract(dataMemberType);
280 return dataMember.MemberTypeContract;
283 internal DataContract GetItemTypeDataContract(CollectionDataContract collectionContract)
285 if (collectionContract.ItemType != null)
286 return GetDataContract(collectionContract.ItemType);
287 return collectionContract.ItemContract;
290 internal object GetSurrogateData(object key)
292 return SurrogateDataTable[key];
295 internal void SetSurrogateData(object key, object surrogateData)
297 SurrogateDataTable[key] = surrogateData;
300 public DataContract this[XmlQualifiedName key]
304 DataContract dataContract = DataContract.GetBuiltInDataContract(key.Name, key.Namespace);
305 if (dataContract == null)
307 Contracts.TryGetValue(key, out dataContract);
313 public IDataContractSurrogate DataContractSurrogate
315 get { return dataContractSurrogate; }
318 public bool Remove(XmlQualifiedName key)
320 if (DataContract.GetBuiltInDataContract(key.Name, key.Namespace) != null)
322 return Contracts.Remove(key);
325 public IEnumerator<KeyValuePair<XmlQualifiedName, DataContract>> GetEnumerator()
327 return Contracts.GetEnumerator();
330 internal bool IsContractProcessed(DataContract dataContract)
332 return ProcessedContracts.ContainsKey(dataContract);
335 internal void SetContractProcessed(DataContract dataContract)
337 ProcessedContracts.Add(dataContract, dataContract);
341 internal ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract)
344 if (ProcessedContracts.TryGetValue(dataContract, out info))
345 return (ContractCodeDomInfo)info;
349 internal void SetContractCodeDomInfo(DataContract dataContract, ContractCodeDomInfo info)
351 ProcessedContracts.Add(dataContract, info);
354 Dictionary<XmlQualifiedName, object> GetReferencedTypes()
356 if (referencedTypesDictionary == null)
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)
364 foreach (Type type in this.referencedTypes)
367 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReferencedTypesCannotContainNull)));
369 AddReferencedType(referencedTypesDictionary, type);
373 return referencedTypesDictionary;
376 Dictionary<XmlQualifiedName, object> GetReferencedCollectionTypes()
378 if (referencedCollectionTypesDictionary == null)
380 referencedCollectionTypesDictionary = new Dictionary<XmlQualifiedName, object>();
381 if (this.referencedCollectionTypes != null)
383 foreach (Type type in this.referencedCollectionTypes)
386 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReferencedCollectionTypesCannotContainNull)));
387 AddReferencedType(referencedCollectionTypesDictionary, type);
390 XmlQualifiedName genericDictionaryName = DataContract.GetStableName(Globals.TypeOfDictionaryGeneric);
391 if (!referencedCollectionTypesDictionary.ContainsKey(genericDictionaryName) && GetReferencedTypes().ContainsKey(genericDictionaryName))
392 AddReferencedType(referencedCollectionTypesDictionary, Globals.TypeOfDictionaryGeneric);
394 return referencedCollectionTypesDictionary;
397 void AddReferencedType(Dictionary<XmlQualifiedName, object> referencedTypes, Type type)
399 if (IsTypeReferenceable(type))
401 XmlQualifiedName stableName;
404 stableName = this.GetStableName(type);
406 catch (InvalidDataContractException)
408 // Type not referenceable if we can't get a stable name.
411 catch (InvalidOperationException)
413 // Type not referenceable if we can't get a stable name.
418 if (referencedTypes.TryGetValue(stableName, out value))
420 Type referencedType = value as Type;
421 if (referencedType != null)
423 if (referencedType != type)
425 referencedTypes.Remove(stableName);
426 List<Type> types = new List<Type>();
427 types.Add(referencedType);
429 referencedTypes.Add(stableName, types);
434 List<Type> types = (List<Type>)value;
435 if (!types.Contains(type))
440 referencedTypes.Add(stableName, type);
443 internal bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, out Type type)
445 return TryGetReferencedType(stableName, dataContract, false/*useReferencedCollectionTypes*/, out type);
448 internal bool TryGetReferencedCollectionType(XmlQualifiedName stableName, DataContract dataContract, out Type type)
450 return TryGetReferencedType(stableName, dataContract, true/*useReferencedCollectionTypes*/, out type);
453 bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, bool useReferencedCollectionTypes, out Type type)
456 Dictionary<XmlQualifiedName, object> referencedTypes = useReferencedCollectionTypes ? GetReferencedCollectionTypes() : GetReferencedTypes();
457 if (referencedTypes.TryGetValue(stableName, out value))
459 type = value as Type;
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++)
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)
476 DataContract other = this.GetDataContract(conflictingType);
477 errorMessage.Append(SR.GetString(((other != null && other.Equals(dataContract)) ? SR.ReferencedTypeMatchingMessage : SR.ReferencedTypeNotMatchingMessage)));
480 if (containsGenericType)
482 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
483 (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes1 : SR.AmbiguousReferencedTypes1),
484 errorMessage.ToString())));
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())));
500 static bool IsTypeReferenceable(Type type)
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));
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.