File: System\Runtime\Serialization\XmlObjectSerializerReadContextComplex.cs
Project: ndp\cdf\src\WCF\Serialization\System.Runtime.Serialization.csproj (System.Runtime.Serialization)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.Runtime.Serialization
{
    using System;
    using System.Collections;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using DataContractDictionary = System.Collections.Generic.Dictionary<System.Xml.XmlQualifiedName, DataContract>;
    using System.Collections.Generic;
    using System.Runtime.Serialization.Diagnostics.Application;
    using System.Runtime.Serialization.Formatters;
    using System.Security;
    using System.Security.Permissions;
    using System.Runtime.CompilerServices;
 
#if USE_REFEMIT
    public class XmlObjectSerializerReadContextComplex : XmlObjectSerializerReadContext
#else
    internal class XmlObjectSerializerReadContextComplex : XmlObjectSerializerReadContext
#endif
    {
        static Hashtable dataContractTypeCache = new Hashtable();
 
        bool preserveObjectReferences;
        protected IDataContractSurrogate dataContractSurrogate;
        SerializationMode mode;
        SerializationBinder binder;
        ISurrogateSelector surrogateSelector;
        FormatterAssemblyStyle assemblyFormat;
        Hashtable surrogateDataContracts;
 
        internal XmlObjectSerializerReadContextComplex(DataContractSerializer serializer, DataContract rootTypeDataContract, DataContractResolver dataContractResolver)
            : base(serializer, rootTypeDataContract, dataContractResolver)
        {
            this.mode = SerializationMode.SharedContract;
            this.preserveObjectReferences = serializer.PreserveObjectReferences;
            this.dataContractSurrogate = serializer.DataContractSurrogate;
        }
 
        internal XmlObjectSerializerReadContextComplex(NetDataContractSerializer serializer)
            : base(serializer)
        {
            this.mode = SerializationMode.SharedType;
            this.preserveObjectReferences = true;
            this.binder = serializer.Binder;
            this.surrogateSelector = serializer.SurrogateSelector;
            this.assemblyFormat = serializer.AssemblyFormat;
        }
 
        internal XmlObjectSerializerReadContextComplex(XmlObjectSerializer serializer, int maxItemsInObjectGraph, StreamingContext streamingContext, bool ignoreExtensionDataObject)
            : base(serializer, maxItemsInObjectGraph, streamingContext, ignoreExtensionDataObject)
        {
        }
 
        internal override SerializationMode Mode
        {
            get { return mode; }
        }
 
        internal override DataContract GetDataContract(int id, RuntimeTypeHandle typeHandle)
        {
            DataContract dataContract = null;
            if (mode == SerializationMode.SharedType && surrogateSelector != null)
            {
                dataContract = NetDataContractSerializer.GetDataContractFromSurrogateSelector(surrogateSelector, GetStreamingContext(), typeHandle, null /*type*/, ref surrogateDataContracts);
            }
 
            if (dataContract != null)
            {
                if (this.IsGetOnlyCollection && dataContract is SurrogateDataContract)
                {
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupportedSerDeser,
                        DataContract.GetClrTypeFullName(dataContract.UnderlyingType))));
                }
                return dataContract;
            }
 
            return base.GetDataContract(id, typeHandle);
        }
 
        internal override DataContract GetDataContract(RuntimeTypeHandle typeHandle, Type type)
        {
            DataContract dataContract = null;
            if (mode == SerializationMode.SharedType && surrogateSelector != null)
            {
                dataContract = NetDataContractSerializer.GetDataContractFromSurrogateSelector(surrogateSelector, GetStreamingContext(), typeHandle, type, ref surrogateDataContracts);
            }
 
            if (dataContract != null)
            {
                if (this.IsGetOnlyCollection && dataContract is SurrogateDataContract)
                {
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupportedSerDeser,
                        DataContract.GetClrTypeFullName(dataContract.UnderlyingType))));
                }
                return dataContract;
            }
 
            return base.GetDataContract(typeHandle, type);
        }
 
        public override object InternalDeserialize(XmlReaderDelegator xmlReader, int declaredTypeID, RuntimeTypeHandle declaredTypeHandle, string name, string ns)
        {
            if (mode == SerializationMode.SharedContract)
            {
                if (dataContractSurrogate == null)
                    return base.InternalDeserialize(xmlReader, declaredTypeID, declaredTypeHandle, name, ns);
                else
                    return InternalDeserializeWithSurrogate(xmlReader, Type.GetTypeFromHandle(declaredTypeHandle), null /*surrogateDataContract*/, name, ns);
            }
            else
            {
                return InternalDeserializeInSharedTypeMode(xmlReader, declaredTypeID, Type.GetTypeFromHandle(declaredTypeHandle), name, ns);
            }
        }
 
        internal override object InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, string name, string ns)
        {
            if (mode == SerializationMode.SharedContract)
            {
                if (dataContractSurrogate == null)
                    return base.InternalDeserialize(xmlReader, declaredType, name, ns);
                else
                    return InternalDeserializeWithSurrogate(xmlReader, declaredType, null /*surrogateDataContract*/, name, ns);
            }
            else
            {
                return InternalDeserializeInSharedTypeMode(xmlReader, -1, declaredType, name, ns);
            }
        }
 
        internal override object InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, string name, string ns)
        {
            if (mode == SerializationMode.SharedContract)
            {
                if (dataContractSurrogate == null)
                    return base.InternalDeserialize(xmlReader, declaredType, dataContract, name, ns);
                else
                    return InternalDeserializeWithSurrogate(xmlReader, declaredType, dataContract, name, ns);
            }
            else
            {
                return InternalDeserializeInSharedTypeMode(xmlReader, -1, declaredType, name, ns);
            }
        }
 
        object InternalDeserializeInSharedTypeMode(XmlReaderDelegator xmlReader, int declaredTypeID, Type declaredType, string name, string ns)
        {
            object retObj = null;
            if (TryHandleNullOrRef(xmlReader, declaredType, name, ns, ref retObj))
                return retObj;
 
            DataContract dataContract;
            string assemblyName = attributes.ClrAssembly;
            string typeName = attributes.ClrType;
            if (assemblyName != null && typeName != null)
            {
                Assembly assembly;
                Type type;
                dataContract = ResolveDataContractInSharedTypeMode(assemblyName, typeName, out assembly, out type);
                if (dataContract == null)
                {
                    if (assembly == null)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.AssemblyNotFound, assemblyName)));
                    if (type == null)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.ClrTypeNotFound, assembly.FullName, typeName)));
                }
                //Array covariance is not supported in XSD. If declared type is array, data is sent in format of base array
                if (declaredType != null && declaredType.IsArray)
                    dataContract = (declaredTypeID < 0) ? GetDataContract(declaredType) : GetDataContract(declaredTypeID, declaredType.TypeHandle);
            }
            else
            {
                if (assemblyName != null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.GetString(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrTypeLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))));
                else if (typeName != null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.GetString(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrAssemblyLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))));
                else if (declaredType == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(XmlObjectSerializer.TryAddLineInfo(xmlReader, SR.GetString(SR.AttributeNotFound, Globals.SerializationNamespace, Globals.ClrTypeLocalName, xmlReader.NodeType, xmlReader.NamespaceURI, xmlReader.LocalName))));
                dataContract = (declaredTypeID < 0) ? GetDataContract(declaredType) : GetDataContract(declaredTypeID, declaredType.TypeHandle);
            }
            return ReadDataContractValue(dataContract, xmlReader);
        }
 
        object InternalDeserializeWithSurrogate(XmlReaderDelegator xmlReader, Type declaredType, DataContract surrogateDataContract, string name, string ns)
        {
            if (TD.DCDeserializeWithSurrogateStartIsEnabled())
            {
                TD.DCDeserializeWithSurrogateStart(declaredType.FullName);
            }
 
            DataContract dataContract = surrogateDataContract ??
                GetDataContract(DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, declaredType));
            if (this.IsGetOnlyCollection && dataContract.UnderlyingType != declaredType)
            {
                throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupportedSerDeser,
                    DataContract.GetClrTypeFullName(declaredType))));
            }
            ReadAttributes(xmlReader);
            string objectId = GetObjectId();
            object oldObj = InternalDeserialize(xmlReader, name, ns, declaredType, ref dataContract);
            object obj = DataContractSurrogateCaller.GetDeserializedObject(dataContractSurrogate, oldObj, dataContract.UnderlyingType, declaredType);
            ReplaceDeserializedObject(objectId, oldObj, obj);
 
            if (TD.DCDeserializeWithSurrogateStopIsEnabled())
            {
                TD.DCDeserializeWithSurrogateStop();
            }
 
            return obj;
        }
 
        Type ResolveDataContractTypeInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly)
        {
            assembly = null;
            Type type = null;
 
            if (binder != null)
                type = binder.BindToType(assemblyName, typeName);
            if (type == null)
            {
                XmlObjectDataContractTypeKey key = new XmlObjectDataContractTypeKey(assemblyName, typeName);
                XmlObjectDataContractTypeInfo dataContractTypeInfo = (XmlObjectDataContractTypeInfo)dataContractTypeCache[key];
                if (dataContractTypeInfo == null)
                {
                    if (assemblyFormat == FormatterAssemblyStyle.Full)
                    {
                        if (assemblyName == Globals.MscorlibAssemblyName)
                        {
                            assembly = Globals.TypeOfInt.Assembly;
                        }
                        else
                        {
                            assembly = Assembly.Load(assemblyName);
                        }
                        if (assembly != null)
                            type = assembly.GetType(typeName);
                    }
                    else
                    {
                        assembly = XmlObjectSerializerReadContextComplex.ResolveSimpleAssemblyName(assemblyName);
                        if (assembly != null)
                        {
                            // Catching any exceptions that could be thrown from a failure on assembly load
                            // This is necessary, for example, if there are generic parameters that are qualified with a version of the assembly that predates the one available
                            try
                            {
                                type = assembly.GetType(typeName);
                            }
                            catch (TypeLoadException) { }
                            catch (FileNotFoundException) { }
                            catch (FileLoadException) { }
                            catch (BadImageFormatException) { }
 
                            if (type == null)
                            {
                                type = Type.GetType(typeName, XmlObjectSerializerReadContextComplex.ResolveSimpleAssemblyName, new TopLevelAssemblyTypeResolver(assembly).ResolveType, false /* throwOnError */);
                            }
                        }
                    }
 
                    if (type != null)
                    {
                        CheckTypeForwardedTo(assembly, type.Assembly, type);
 
                        dataContractTypeInfo = new XmlObjectDataContractTypeInfo(assembly, type);
                        lock (dataContractTypeCache)
                        {
                            if (!dataContractTypeCache.ContainsKey(key))
                            {
                                dataContractTypeCache[key] = dataContractTypeInfo;
                            }
                        }
                    }
                }
                else
                {
                    assembly = dataContractTypeInfo.Assembly;
                    type = dataContractTypeInfo.Type;
                }
            }
 
            return type;
        }
 
        DataContract ResolveDataContractInSharedTypeMode(string assemblyName, string typeName, out Assembly assembly, out Type type)
        {
            type = ResolveDataContractTypeInSharedTypeMode(assemblyName, typeName, out assembly);
            if (type != null)
            {
                return GetDataContract(type);
            }
 
            return null;
        }
 
        protected override DataContract ResolveDataContractFromTypeName()
        {
            if (mode == SerializationMode.SharedContract)
            {
                return base.ResolveDataContractFromTypeName();
            }
            else
            {
                if (attributes.ClrAssembly != null && attributes.ClrType != null)
                {
                    Assembly assembly;
                    Type type;
                    return ResolveDataContractInSharedTypeMode(attributes.ClrAssembly, attributes.ClrType, out assembly, out type);
                }
            }
            return null;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls the critical methods of ISurrogateSelector", Safe = "Demands for FullTrust")]
        [SecuritySafeCritical]
        [PermissionSet(SecurityAction.Demand, Unrestricted = true)]
        [MethodImpl(MethodImplOptions.NoInlining)]
        bool CheckIfTypeSerializableForSharedTypeMode(Type memberType)
        {
            Fx.Assert(surrogateSelector != null, "Method should not be called when surrogateSelector is null.");
            ISurrogateSelector surrogateSelectorNotUsed;
            return (surrogateSelector.GetSurrogate(memberType, GetStreamingContext(), out surrogateSelectorNotUsed) != null);
        }
 
        internal override void CheckIfTypeSerializable(Type memberType, bool isMemberTypeSerializable)
        {
            if (mode == SerializationMode.SharedType && surrogateSelector != null &&
                CheckIfTypeSerializableForSharedTypeMode(memberType))
            {
                return;
            }
            else
            {
                if (dataContractSurrogate != null)
                {
                    while (memberType.IsArray)
                        memberType = memberType.GetElementType();
                    memberType = DataContractSurrogateCaller.GetDataContractType(dataContractSurrogate, memberType);
                    if (!DataContract.IsTypeSerializable(memberType))
                        throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.TypeNotSerializable, memberType)));
                    return;
                }
            }
 
            base.CheckIfTypeSerializable(memberType, isMemberTypeSerializable);
        }
 
        internal override Type GetSurrogatedType(Type type)
        {
            if (dataContractSurrogate == null)
            {
                return base.GetSurrogatedType(type);
            }
            else
            {
                type = DataContract.UnwrapNullableType(type);
                Type surrogateType = DataContractSerializer.GetSurrogatedType(dataContractSurrogate, type);
                if (this.IsGetOnlyCollection && surrogateType != type)
                {
                    throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.SurrogatesWithGetOnlyCollectionsNotSupportedSerDeser,
                        DataContract.GetClrTypeFullName(type))));
                }
                else
                {
                    return surrogateType;
                }
            }
        }
 
#if USE_REFEMIT
        public override int GetArraySize()
#else
        internal override int GetArraySize()
#endif
        {
            return preserveObjectReferences ? attributes.ArraySZSize : -1;
        }
 
        static Assembly ResolveSimpleAssemblyName(AssemblyName assemblyName)
        {
            return ResolveSimpleAssemblyName(assemblyName.FullName);
        }
 
        static Assembly ResolveSimpleAssemblyName(string assemblyName)
        {
            Assembly assembly;
            if (assemblyName == Globals.MscorlibAssemblyName)
            {
                assembly = Globals.TypeOfInt.Assembly;
            }
            else
            {
                assembly = Assembly.LoadWithPartialName(assemblyName);
                if (assembly == null)
                {
                    AssemblyName an = new AssemblyName(assemblyName);
                    an.Version = null;
                    assembly = Assembly.LoadWithPartialName(an.FullName);
                }
            }
            return assembly;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Gets the SecurityCritical PermissionSet for sourceAssembly and destinationAssembly.",
            Safe = "Doesn't leak anything.")]
        [SecuritySafeCritical]
        static void CheckTypeForwardedTo(Assembly sourceAssembly, Assembly destinationAssembly, Type resolvedType)
        {
            if (sourceAssembly != destinationAssembly && !NetDataContractSerializer.UnsafeTypeForwardingEnabled && !sourceAssembly.IsFullyTrusted)
            {
                // We have a TypeForwardedTo attribute
                if (!destinationAssembly.PermissionSet.IsSubsetOf(sourceAssembly.PermissionSet))
                {
                    // We look for a matching TypeForwardedFrom attribute
                    TypeInformation typeInfo = NetDataContractSerializer.GetTypeInformation(resolvedType);
                    if (typeInfo.HasTypeForwardedFrom)
                    {
                        Assembly typeForwardedFromAssembly = null;
                        try
                        {
                            // if this Assembly.Load fails, we still want to throw security exception
                            typeForwardedFromAssembly = Assembly.Load(typeInfo.AssemblyString);
                        }
                        catch { }
 
                        if (typeForwardedFromAssembly == sourceAssembly)
                        {
                            return;
                        }
                    }
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.CannotDeserializeForwardedType, DataContract.GetClrTypeFullName(resolvedType))));
                }
            }
        }
 
        sealed class TopLevelAssemblyTypeResolver
        {
            Assembly topLevelAssembly;
 
            public TopLevelAssemblyTypeResolver(Assembly topLevelAssembly)
            {
                this.topLevelAssembly = topLevelAssembly;
            }
            public Type ResolveType(Assembly assembly, string simpleTypeName, bool ignoreCase)
            {
                if (assembly == null)
                    assembly = topLevelAssembly;
 
                return assembly.GetType(simpleTypeName, false, ignoreCase);
            }
        }
 
        class XmlObjectDataContractTypeInfo
        {
            Assembly assembly;
            Type type;
            public XmlObjectDataContractTypeInfo(Assembly assembly, Type type)
            {
                this.assembly = assembly;
                this.type = type;
            }
 
            public Assembly Assembly
            {
                get
                {
                    return this.assembly;
                }
            }
 
            public Type Type
            {
                get
                {
                    return this.type;
                }
            }
        }
 
        class XmlObjectDataContractTypeKey
        {
            string assemblyName;
            string typeName;
            public XmlObjectDataContractTypeKey(string assemblyName, string typeName)
            {
                this.assemblyName = assemblyName;
                this.typeName = typeName;
            }
 
            public override bool Equals(object obj)
            {
                if (object.ReferenceEquals(this, obj))
                    return true;
 
                XmlObjectDataContractTypeKey other = obj as XmlObjectDataContractTypeKey;
                if (other == null)
                    return false;
 
                if (this.assemblyName != other.assemblyName)
                    return false;
 
                if (this.typeName != other.typeName)
                    return false;
 
                return true;
            }
 
            public override int GetHashCode()
            {
                int hashCode = 0;
                if (this.assemblyName != null)
                    hashCode = this.assemblyName.GetHashCode();
 
                if (this.typeName != null)
                    hashCode ^= this.typeName.GetHashCode();
 
                return hashCode;
            }
        }
    }
}