//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.Runtime.Serialization { using System; using System.Xml; using System.Xml.Schema; using System.Collections; using System.Collections.Generic; using System.Reflection; using DataContractDictionary = System.Collections.Generic.Dictionary; using System.Text; internal class DataContractSet { Dictionary contracts; Dictionary processedContracts; IDataContractSurrogate dataContractSurrogate; Hashtable surrogateDataTable; DataContractDictionary knownTypesForObject; ICollection referencedTypes; ICollection referencedCollectionTypes; Dictionary referencedTypesDictionary; Dictionary referencedCollectionTypesDictionary; internal DataContractSet(IDataContractSurrogate dataContractSurrogate) : this(dataContractSurrogate, null, null) { } internal DataContractSet(IDataContractSurrogate dataContractSurrogate, ICollection referencedTypes, ICollection referencedCollectionTypes) { this.dataContractSurrogate = dataContractSurrogate; this.referencedTypes = referencedTypes; this.referencedCollectionTypes = referencedCollectionTypes; } internal DataContractSet(DataContractSet dataContractSet) { if (dataContractSet == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("dataContractSet")); this.dataContractSurrogate = dataContractSet.dataContractSurrogate; this.referencedTypes = dataContractSet.referencedTypes; this.referencedCollectionTypes = dataContractSet.referencedCollectionTypes; foreach (KeyValuePair pair in dataContractSet) { Add(pair.Key, pair.Value); } if (dataContractSet.processedContracts != null) { foreach (KeyValuePair pair in dataContractSet.processedContracts) { ProcessedContracts.Add(pair.Key, pair.Value); } } } Dictionary Contracts { get { if (contracts == null) { contracts = new Dictionary(); } return contracts; } } Dictionary ProcessedContracts { get { if (processedContracts == null) { processedContracts = new Dictionary(); } return processedContracts; } } Hashtable SurrogateDataTable { get { if (surrogateDataTable == null) surrogateDataTable = new Hashtable(); return surrogateDataTable; } } internal DataContractDictionary KnownTypesForObject { get { return knownTypesForObject; } set { knownTypesForObject = value; } } internal void Add(Type type) { DataContract dataContract = GetDataContract(type); EnsureTypeNotGeneric(dataContract.UnderlyingType); Add(dataContract); } internal static void EnsureTypeNotGeneric(Type type) { if (type.ContainsGenericParameters) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.GenericTypeNotExportable, type))); } void Add(DataContract dataContract) { Add(dataContract.StableName, dataContract); } public void Add(XmlQualifiedName name, DataContract dataContract) { if (dataContract.IsBuiltInDataContract) return; InternalAdd(name, dataContract); } internal void InternalAdd(XmlQualifiedName name, DataContract dataContract) { DataContract dataContractInSet = null; if (Contracts.TryGetValue(name, out dataContractInSet)) { if (!dataContractInSet.Equals(dataContract)) { if (dataContract.UnderlyingType == null || dataContractInSet.UnderlyingType == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.DupContractInDataContractSet, dataContract.StableName.Name, dataContract.StableName.Namespace))); else { bool typeNamesEqual = (DataContract.GetClrTypeFullName(dataContract.UnderlyingType) == DataContract.GetClrTypeFullName(dataContractInSet.UnderlyingType)); 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))); } } } else { Contracts.Add(name, dataContract); if (dataContract is ClassDataContract) { AddClassDataContract((ClassDataContract)dataContract); } else if (dataContract is CollectionDataContract) { AddCollectionDataContract((CollectionDataContract)dataContract); } else if (dataContract is XmlDataContract) { AddXmlDataContract((XmlDataContract)dataContract); } } } void AddClassDataContract(ClassDataContract classDataContract) { if (classDataContract.BaseContract != null) { Add(classDataContract.BaseContract.StableName, classDataContract.BaseContract); } if (!classDataContract.IsISerializable) { if (classDataContract.Members != null) { for (int i = 0; i < classDataContract.Members.Count; i++) { DataMember dataMember = classDataContract.Members[i]; DataContract memberDataContract = GetMemberTypeDataContract(dataMember); if (dataContractSurrogate != null && dataMember.MemberInfo != null) { object customData = DataContractSurrogateCaller.GetCustomDataToExport( dataContractSurrogate, dataMember.MemberInfo, memberDataContract.UnderlyingType); if (customData != null) SurrogateDataTable.Add(dataMember, customData); } Add(memberDataContract.StableName, memberDataContract); } } } AddKnownDataContracts(classDataContract.KnownDataContracts); } void AddCollectionDataContract(CollectionDataContract collectionDataContract) { if (collectionDataContract.IsDictionary) { ClassDataContract keyValueContract = collectionDataContract.ItemContract as ClassDataContract; AddClassDataContract(keyValueContract); } else { DataContract itemContract = GetItemTypeDataContract(collectionDataContract); if (itemContract != null) Add(itemContract.StableName, itemContract); } AddKnownDataContracts(collectionDataContract.KnownDataContracts); } void AddXmlDataContract(XmlDataContract xmlDataContract) { AddKnownDataContracts(xmlDataContract.KnownDataContracts); } void AddKnownDataContracts(DataContractDictionary knownDataContracts) { if (knownDataContracts != null) { foreach (DataContract knownDataContract in knownDataContracts.Values) { Add(knownDataContract); } } } internal XmlQualifiedName GetStableName(Type clrType) { if (dataContractSurrogate != null) { Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, clrType); //if (clrType.IsValueType != dcType.IsValueType) // throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ValueTypeMismatchInSurrogatedType, dcType, clrType))); return DataContract.GetStableName(dcType); } return DataContract.GetStableName(clrType); } internal DataContract GetDataContract(Type clrType) { if (dataContractSurrogate == null) return DataContract.GetDataContract(clrType); DataContract dataContract = DataContract.GetBuiltInDataContract(clrType); if (dataContract != null) return dataContract; Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, clrType); //if (clrType.IsValueType != dcType.IsValueType) // throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ValueTypeMismatchInSurrogatedType, dcType, clrType))); dataContract = DataContract.GetDataContract(dcType); if (!SurrogateDataTable.Contains(dataContract)) { object customData = DataContractSurrogateCaller.GetCustomDataToExport( dataContractSurrogate, clrType, dcType); if (customData != null) SurrogateDataTable.Add(dataContract, customData); } return dataContract; } internal DataContract GetMemberTypeDataContract(DataMember dataMember) { if (dataMember.MemberInfo != null) { Type dataMemberType = dataMember.MemberType; if (dataMember.IsGetOnlyCollection) { if (dataContractSurrogate != null) { Type dcType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, dataMemberType); if (dcType != dataMemberType) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupported, DataContract.GetClrTypeFullName(dataMemberType), DataContract.GetClrTypeFullName(dataMember.MemberInfo.DeclaringType), dataMember.MemberInfo.Name))); } } return DataContract.GetGetOnlyCollectionDataContract(DataContract.GetId(dataMemberType.TypeHandle), dataMemberType.TypeHandle, dataMemberType, SerializationMode.SharedContract); } else { return GetDataContract(dataMemberType); } } return dataMember.MemberTypeContract; } internal DataContract GetItemTypeDataContract(CollectionDataContract collectionContract) { if (collectionContract.ItemType != null) return GetDataContract(collectionContract.ItemType); return collectionContract.ItemContract; } internal object GetSurrogateData(object key) { return SurrogateDataTable[key]; } internal void SetSurrogateData(object key, object surrogateData) { SurrogateDataTable[key] = surrogateData; } public DataContract this[XmlQualifiedName key] { get { DataContract dataContract = DataContract.GetBuiltInDataContract(key.Name, key.Namespace); if (dataContract == null) { Contracts.TryGetValue(key, out dataContract); } return dataContract; } } public IDataContractSurrogate DataContractSurrogate { get { return dataContractSurrogate; } } public bool Remove(XmlQualifiedName key) { if (DataContract.GetBuiltInDataContract(key.Name, key.Namespace) != null) return false; return Contracts.Remove(key); } public IEnumerator> GetEnumerator() { return Contracts.GetEnumerator(); } internal bool IsContractProcessed(DataContract dataContract) { return ProcessedContracts.ContainsKey(dataContract); } internal void SetContractProcessed(DataContract dataContract) { ProcessedContracts.Add(dataContract, dataContract); } #if !NO_CODEDOM internal ContractCodeDomInfo GetContractCodeDomInfo(DataContract dataContract) { object info; if (ProcessedContracts.TryGetValue(dataContract, out info)) return (ContractCodeDomInfo)info; return null; } internal void SetContractCodeDomInfo(DataContract dataContract, ContractCodeDomInfo info) { ProcessedContracts.Add(dataContract, info); } #endif Dictionary GetReferencedTypes() { if (referencedTypesDictionary == null) { referencedTypesDictionary = new Dictionary(); //Always include Nullable as referenced type //Do not allow surrogating Nullable referencedTypesDictionary.Add(DataContract.GetStableName(Globals.TypeOfNullable), Globals.TypeOfNullable); if (this.referencedTypes != null) { foreach (Type type in this.referencedTypes) { if (type == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReferencedTypesCannotContainNull))); AddReferencedType(referencedTypesDictionary, type); } } } return referencedTypesDictionary; } Dictionary GetReferencedCollectionTypes() { if (referencedCollectionTypesDictionary == null) { referencedCollectionTypesDictionary = new Dictionary(); if (this.referencedCollectionTypes != null) { foreach (Type type in this.referencedCollectionTypes) { if (type == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ReferencedCollectionTypesCannotContainNull))); AddReferencedType(referencedCollectionTypesDictionary, type); } } XmlQualifiedName genericDictionaryName = DataContract.GetStableName(Globals.TypeOfDictionaryGeneric); if (!referencedCollectionTypesDictionary.ContainsKey(genericDictionaryName) && GetReferencedTypes().ContainsKey(genericDictionaryName)) AddReferencedType(referencedCollectionTypesDictionary, Globals.TypeOfDictionaryGeneric); } return referencedCollectionTypesDictionary; } void AddReferencedType(Dictionary referencedTypes, Type type) { if (IsTypeReferenceable(type)) { XmlQualifiedName stableName; try { stableName = this.GetStableName(type); } catch (InvalidDataContractException) { // Type not referenceable if we can't get a stable name. return; } catch (InvalidOperationException) { // Type not referenceable if we can't get a stable name. return; } object value; if (referencedTypes.TryGetValue(stableName, out value)) { Type referencedType = value as Type; if (referencedType != null) { if (referencedType != type) { referencedTypes.Remove(stableName); List types = new List(); types.Add(referencedType); types.Add(type); referencedTypes.Add(stableName, types); } } else { List types = (List)value; if (!types.Contains(type)) types.Add(type); } } else referencedTypes.Add(stableName, type); } } internal bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, out Type type) { return TryGetReferencedType(stableName, dataContract, false/*useReferencedCollectionTypes*/, out type); } internal bool TryGetReferencedCollectionType(XmlQualifiedName stableName, DataContract dataContract, out Type type) { return TryGetReferencedType(stableName, dataContract, true/*useReferencedCollectionTypes*/, out type); } bool TryGetReferencedType(XmlQualifiedName stableName, DataContract dataContract, bool useReferencedCollectionTypes, out Type type) { object value; Dictionary referencedTypes = useReferencedCollectionTypes ? GetReferencedCollectionTypes() : GetReferencedTypes(); if (referencedTypes.TryGetValue(stableName, out value)) { type = value as Type; if (type != null) return true; else { // Throw ambiguous type match exception List types = (List)value; StringBuilder errorMessage = new StringBuilder(); bool containsGenericType = false; for (int i = 0; i < types.Count; i++) { Type conflictingType = types[i]; if (!containsGenericType) containsGenericType = conflictingType.IsGenericTypeDefinition; errorMessage.AppendFormat("{0}\"{1}\" ", Environment.NewLine, conflictingType.AssemblyQualifiedName); if (dataContract != null) { DataContract other = this.GetDataContract(conflictingType); errorMessage.Append(SR.GetString(((other != null && other.Equals(dataContract)) ? SR.ReferencedTypeMatchingMessage : SR.ReferencedTypeNotMatchingMessage))); } } if (containsGenericType) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString( (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes1 : SR.AmbiguousReferencedTypes1), errorMessage.ToString()))); } else { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString( (useReferencedCollectionTypes ? SR.AmbiguousReferencedCollectionTypes3 : SR.AmbiguousReferencedTypes3), XmlConvert.DecodeName(stableName.Name), stableName.Namespace, errorMessage.ToString()))); } } } type = null; return false; } static bool IsTypeReferenceable(Type type) { Type itemType; try { return (type.IsSerializable || type.IsDefined(Globals.TypeOfDataContractAttribute, false) || (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type) && !type.IsGenericTypeDefinition) || CollectionDataContract.IsCollection(type, out itemType) || ClassDataContract.IsNonAttributedTypeValidForSerialization(type)); } catch (Exception ex) { // 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. // Type.IsDefined is known to throw System.IO.FileLoadException. // ClassDataContract.IsNonAttributedTypeValidForSerialization is known to throw System.IO.FileNotFoundException. // We guard against all non-critical exceptions. if (Fx.IsFatal(ex)) { throw; } } return false; } } }