File: winforms\Managed\System\Resources\ResXDataNode.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="ResXDataNode.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
#if SYSTEM_WEB  // See DevDiv 9030
namespace System.PrivateResources {
#else
namespace System.Resources {
#endif
 
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    using System;
    using System.Windows.Forms;
    using System.Reflection;
    using Microsoft.Win32;
    using System.Drawing;
    using System.IO;
    using System.Text;
    using System.ComponentModel;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    using System.Resources;
    using System.Xml;
    using System.ComponentModel.Design;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Runtime.Versioning;
#if SYSTEM_WEB
    using System.Web;   // This is needed to access the SR resource strings
#endif
 
 
    /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode"]/*' />
    /// <devdoc>
    ///    
    /// </devdoc>
    [Serializable]
    [PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")]
#if SYSTEM_WEB
    internal sealed class ResXDataNode : ISerializable {
#else
    public sealed class ResXDataNode : ISerializable {
#endif
 
        private static readonly char[] SpecialChars = new char[]{' ', '\r', '\n'};
 
        private DataNodeInfo        nodeInfo;
 
        private string name;
        private string comment;
#if UNUSED
        private string mimeType;
        private string valueData;
#endif
 
        private string typeName; // is only used when we create a resxdatanode manually with an object and contains the FQN
 
        private string fileRefFullPath;
        private string fileRefType;
        private string fileRefTextEncoding;
 
        private object              value;
        private ResXFileRef         fileRef;
 
        private IFormatter binaryFormatter = null;
 
        // this is going to be used to check if a ResXDataNode is of type ResXFileRef
        private static ITypeResolutionService internalTypeResolver = new AssemblyNamesTypeResolutionService(new AssemblyName[] { new AssemblyName("System.Windows.Forms") });
 
        // call back function to get type name for multitargeting.
        // No public property to force using constructors for the following reasons:
        // 1. one of the constructors needs this field (if used) to initialize the object, make it consistent with the other ctrs to avoid errors.
        // 2. once the object is constructed the delegate should not be changed to avoid getting inconsistent results.
        private Func<Type, string> typeNameConverter;
 
        // constructors
        
        private ResXDataNode() {
        }
 
        // <devdoc>
        // this is a deep clone
        //</devdoc>
        internal ResXDataNode DeepClone() {
            ResXDataNode result = new ResXDataNode();
            result.nodeInfo = (this.nodeInfo != null) ? this.nodeInfo.Clone() : null; // nodeinfo is just made up of immutable objects, we don't need to clone it
            result.name = this.name;
            result.comment = this.comment;
#if UNUSED            
            result.mimeType = this.mimeType;
            result.valueData = this.valueData;
#endif            
            result.typeName = this.typeName;
            result.fileRefFullPath = this.fileRefFullPath;
            result.fileRefType = this.fileRefType;
            result.fileRefTextEncoding = this.fileRefTextEncoding;
            result.value = this.value; // we don't clone the value, because we don't know how
            result.fileRef = (this.fileRef != null) ? this.fileRef.Clone() : null;
            result.typeNameConverter = this.typeNameConverter;
            return result;
        }
            
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.ResXDataNode"]/*' />
        /// <devdoc>
        ///    
        /// </devdoc>        
        public ResXDataNode(string name, object value) : this(name, value, null) {
        }
 
        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
        [
            SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "name" is the name of the param passed in.
            // So we don't have to localize it.
        ]
        public ResXDataNode(string name, object value, Func<Type, string> typeNameConverter) {
            if(name == null) {
                throw (new ArgumentNullException("name"));
            }
            if(name.Length == 0) {
                throw (new ArgumentException("name"));
            }
 
            this.typeNameConverter = typeNameConverter;
 
            Type valueType = (value == null) ? typeof(object) : value.GetType();
            if (value != null && !valueType.IsSerializable) {
                throw new InvalidOperationException(SR.GetString(SR.NotSerializableType, name, valueType.FullName));
            } else if (value!= null) {
                this.typeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, this.typeNameConverter);
            } 
                                    
            this.name = name;
            this.value = value;
        }
        
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.ResXDataNode2"]/*' />
        /// <devdoc>
        ///    
        /// </devdoc>  
        public ResXDataNode(string name, ResXFileRef fileRef) : this(name, fileRef, null) {
        }
 
        [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
        [
            SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "name" is the name of the param passed in.
                                                                                                        // So we don't have to localize it.
        ]
        public ResXDataNode(string name, ResXFileRef fileRef, Func<Type, string> typeNameConverter) {
            if(name == null) {
                throw (new ArgumentNullException("name"));
            }
            if(fileRef == null) {
                throw (new ArgumentNullException("fileRef"));
            }
            if(name.Length == 0) {
                throw (new ArgumentException("name"));
            }
            this.name = name;
            this.fileRef = fileRef;
            this.typeNameConverter = typeNameConverter;
        }
 
        internal ResXDataNode(DataNodeInfo nodeInfo, string basePath) {
            this.nodeInfo = nodeInfo;
            InitializeDataNode(basePath);
        }
 
        private void InitializeDataNode(string basePath) {
 
            // we can only use our internal type resolver here
            // because we only want to check if this is a ResXFileRef node
            // and we can't be sure that we have a typeResolutionService that can 
            // recognize this. It's not very clean but this should work.
            Type nodeType = null;
            if(!string.IsNullOrEmpty(nodeInfo.TypeName)) // can be null if we have a string (default for string is TypeName == null)
                nodeType = internalTypeResolver.GetType(nodeInfo.TypeName, false, true);
            if(nodeType != null && nodeType.Equals(typeof(ResXFileRef))) {
                // we have a fileref, split the value data and populate the fields
                string[] fileRefDetails = ResXFileRef.Converter.ParseResxFileRefString(nodeInfo.ValueData);
                if(fileRefDetails != null && fileRefDetails.Length > 1) {
                    if(!Path.IsPathRooted(fileRefDetails[0]) && basePath != null) {
                        fileRefFullPath = Path.Combine(basePath, fileRefDetails[0]);
                    } else {
                        fileRefFullPath = fileRefDetails[0];
                    }
                    fileRefType = fileRefDetails[1];
                    if(fileRefDetails.Length > 2) {
                        fileRefTextEncoding = fileRefDetails[2];
                    }
                }
            }
        }
 
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.Comment"]/*' />
        /// <devdoc>
        ///    
        /// </devdoc>
        public string Comment {
            get {
                string result = comment;
                if(result == null && nodeInfo != null) {
                    result = nodeInfo.Comment;
                }
                return (result == null ? "" : result);
            }
            set {
                comment= value;
            }
        }
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.Name"]/*' />
        /// <devdoc>
        ///    
        /// </devdoc>
        public string Name {
            get {
                string result = name;
                if(result == null && nodeInfo != null) {
                    result = nodeInfo.Name;
                }
                return result;
            }
            [
                SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "Name" is the name of the property.
                                                                                                            // So we don't have to localize it.
            ]
            set {
                if(value == null) {
                    throw (new ArgumentNullException("Name"));
                }
                if(value.Length == 0) {
                    throw (new ArgumentException("Name"));
                }
                name = value;
            }
        }
 
#if UNUSED
        private string MimeType {
            get {
                string result = mimeType;
                if(result == null && nodeInfo != null) {
                    result = nodeInfo.MimeType;
                }
                return result;
            }
        }
 
        private string ValueData {
            get {
                string result = valueData;
                if(result == null && nodeInfo != null) {
                    result = nodeInfo.ValueData;
                }
                return result;
            }
        }
#endif
        
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.FileRef"]/*' />
        /// <devdoc>
        ///    
        /// </devdoc>
        public ResXFileRef FileRef {
            get {
                if(FileRefFullPath==null) {
                    return null;
                }
                if(fileRef == null) {
                    if(String.IsNullOrEmpty(fileRefTextEncoding)) 
                    {
                        fileRef = new ResXFileRef(FileRefFullPath, FileRefType);
                    } else {
                        fileRef = new ResXFileRef(FileRefFullPath, FileRefType, Encoding.GetEncoding(FileRefTextEncoding));
                    }
                }
                return fileRef;
            }
        }
 
        
        private string FileRefFullPath {
            get {
                string result = (fileRef==null ? null : fileRef.FileName);
                if(result == null) {
                    result = fileRefFullPath;
                }
                return result;
            }
        }
 
        private string FileRefType {
            get {
                string result = (fileRef==null ? null : fileRef.TypeName);
                if(result == null) {
                    result = fileRefType;
                }
                return result;
            }
        }
 
        private string FileRefTextEncoding {
            get {
                string result = (fileRef==null ? null : (fileRef.TextFileEncoding == null ? null : fileRef.TextFileEncoding.BodyName));
                if(result == null) {
                    result = fileRefTextEncoding;
                }
 
                return result;
            }
        }
 
 
#if !SYSTEM_WEB     // System.Web does not link with the Soap assembly
        /// <devdoc>
        ///     As a performance optimization, we isolate the soap class here in a separate
        ///     function.  We don't care about the binary formatter because it lives in 
        ///     mscorlib, which is already loaded.  The soap formatter lives in a separate
        ///     assembly, however, so there is value in preventing it from needlessly
        ///     being loaded.
        /// </devdoc>
        [MethodImplAttribute(MethodImplOptions.NoInlining)]
        private IFormatter CreateSoapFormatter() {
            return new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
        }
#endif
 
 
        private static string ToBase64WrappedString(byte[] data) {
            const int lineWrap = 80;
            const string crlf = "\r\n";
            const string prefix = "        ";
            string raw = Convert.ToBase64String(data);
            if (raw.Length > lineWrap) {
                StringBuilder output = new StringBuilder(raw.Length + (raw.Length / lineWrap) * 3); // word wrap on lineWrap chars, \r\n
                int current = 0;
                for (; current < raw.Length - lineWrap; current+=lineWrap) {
                    output.Append(crlf);
                    output.Append(prefix);
                    output.Append(raw, current, lineWrap);
                }
                output.Append(crlf);
                output.Append(prefix);
                output.Append(raw, current, raw.Length - current);
                output.Append(crlf);
                return output.ToString();
            }
            else {
                return raw;
            }
        }
 
        private void FillDataNodeInfoFromObject(DataNodeInfo nodeInfo, object value) {           
            CultureInfo ci = value as CultureInfo;
            if( ci != null) { // special-case CultureInfo, cannot use CultureInfoConverter for serialization (see DevDiv#578701).
                nodeInfo.ValueData = ci.Name;
                nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(CultureInfo), this.typeNameConverter);
            }
            else if (value is string) {
                nodeInfo.ValueData = (string)value;
                if(value == null) {
                    nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter);
                }
            }
            else if (value is byte[]) {
                nodeInfo.ValueData = ToBase64WrappedString((byte[])value);
                nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(byte[]), this.typeNameConverter);
            }
            else {
                Type valueType = (value == null) ? typeof(object) : value.GetType();
                if (value != null && !valueType.IsSerializable) {
                    throw new InvalidOperationException(SR.GetString(SR.NotSerializableType, name, valueType.FullName));
                }
                TypeConverter tc = TypeDescriptor.GetConverter(valueType);
                bool toString = tc.CanConvertTo(typeof(string));
                bool fromString = tc.CanConvertFrom(typeof(string));
                try {
                    if (toString && fromString) {
                        nodeInfo.ValueData = tc.ConvertToInvariantString(value);
                        nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, this.typeNameConverter);
                        return;
                    }
                }
                catch (Exception ex) {
                    // Some custom type converters will throw in ConvertTo(string)
                    // to indicate that this object should be serialized through ISeriazable
                    // instead of as a string. This is semi-wrong, but something we will have to
                    // live with to allow user created Cursors to be serializable.
                    if (ClientUtils.IsSecurityOrCriticalException(ex)) {
                        throw;
                    }
                }
 
                bool toByteArray = tc.CanConvertTo(typeof(byte[]));
                bool fromByteArray = tc.CanConvertFrom(typeof(byte[]));
                if (toByteArray && fromByteArray) {
                    byte[] data = (byte[])tc.ConvertTo(value, typeof(byte[]));
                    string text = ToBase64WrappedString(data);
                    nodeInfo.ValueData = text;
                    nodeInfo.MimeType = ResXResourceWriter.ByteArraySerializedObjectMimeType;
                    nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(valueType, this.typeNameConverter);
                    return;
                }
 
                if (value == null) {
                    nodeInfo.ValueData = string.Empty;
                    nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter);
                }
                else {
                    if (binaryFormatter == null) {
                        binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                        binaryFormatter.Binder = new ResXSerializationBinder(this.typeNameConverter);
                    }
 
                    MemoryStream ms = new MemoryStream();
                    binaryFormatter.Serialize(ms, value);
                    string text = ToBase64WrappedString(ms.ToArray());
                    nodeInfo.ValueData = text;
                    nodeInfo.MimeType = ResXResourceWriter.DefaultSerializedObjectMimeType;
                }
            }
        
        }
        
 
        private object GenerateObjectFromDataNodeInfo(DataNodeInfo dataNodeInfo, ITypeResolutionService typeResolver) {
            object result = null;
            string mimeTypeName = dataNodeInfo.MimeType;
            // default behavior: if we dont have a type name, it's a string
            string typeName = (dataNodeInfo.TypeName == null || dataNodeInfo.TypeName.Length==0 ? MultitargetUtil.GetAssemblyQualifiedName(typeof(string), this.typeNameConverter) : dataNodeInfo.TypeName);
            
            if (mimeTypeName != null && mimeTypeName.Length > 0) {
                if (String.Equals(mimeTypeName, ResXResourceWriter.BinSerializedObjectMimeType)
                    || String.Equals(mimeTypeName, ResXResourceWriter.Beta2CompatSerializedObjectMimeType)
                    || String.Equals(mimeTypeName, ResXResourceWriter.CompatBinSerializedObjectMimeType)) {
                    string text = dataNodeInfo.ValueData;
                    byte[] serializedData;
                    serializedData = FromBase64WrappedString(text);
 
                    if (binaryFormatter == null) {
                        binaryFormatter = new BinaryFormatter();
                        binaryFormatter.Binder = new ResXSerializationBinder(typeResolver);
                    }
                    IFormatter formatter = binaryFormatter;
                    if (serializedData != null && serializedData.Length > 0) {
                        result = formatter.Deserialize(new MemoryStream(serializedData));
                        if (result is ResXNullRef) {
                            result = null;
                        }
                    }
                }
#if !SYSTEM_WEB // System.Web does not link with the Soap assembly
                else if (String.Equals(mimeTypeName, ResXResourceWriter.SoapSerializedObjectMimeType)
                         || String.Equals(mimeTypeName, ResXResourceWriter.CompatSoapSerializedObjectMimeType)) {
                    string text = dataNodeInfo.ValueData;
                    byte[] serializedData;
                    serializedData = FromBase64WrappedString(text);
 
                    if (serializedData != null && serializedData.Length > 0) {
 
                        // Performance : don't inline a new SoapFormatter here.  That will always bring in
                        //               the soap assembly, which we don't want.  Throw this in another
                        //               function so the class doesn't have to get loaded.
                        //
                        IFormatter formatter = CreateSoapFormatter();
                        result = formatter.Deserialize(new MemoryStream(serializedData));
                        if (result is ResXNullRef) {
                            result = null;
                        }
                    }
                }
#endif
                else if (String.Equals(mimeTypeName, ResXResourceWriter.ByteArraySerializedObjectMimeType)) {
                    if (typeName != null && typeName.Length > 0) {
                        Type type = ResolveType(typeName, typeResolver);
                        if (type != null) {
                            TypeConverter tc = TypeDescriptor.GetConverter(type);
                            if (tc.CanConvertFrom(typeof(byte[]))) {
                                string text = dataNodeInfo.ValueData;
                                byte[] serializedData;
                                serializedData = FromBase64WrappedString(text);
        
                                if (serializedData != null) {
                                    result = tc.ConvertFrom(serializedData);
                                }
                            }
                        }
                        else {
                            string newMessage = SR.GetString(SR.TypeLoadException, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X);
                            XmlException xml = new XmlException(newMessage, null, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X);
                            TypeLoadException newTle = new TypeLoadException(newMessage, xml);
 
                            throw newTle;
                        }
                    }
                }
            }
            else if (typeName != null && typeName.Length > 0) {
                Type type = ResolveType(typeName, typeResolver);
                if (type != null) {
                    if (type == typeof(ResXNullRef)) {
                        result = null;
                    }
                    else if (typeName.IndexOf("System.Byte[]") != -1 && typeName.IndexOf("mscorlib") != -1) {
                        // Handle byte[]'s, which are stored as base-64 encoded strings.
                        // We can't hard-code byte[] type name due to version number
                        // updates & potential whitespace issues with ResX files.
                        result = FromBase64WrappedString(dataNodeInfo.ValueData);
                    }
                    else {                            
                        TypeConverter tc = TypeDescriptor.GetConverter(type);
                        if (tc.CanConvertFrom(typeof(string))) {
                            string text = dataNodeInfo.ValueData;
                            try {
                            result = tc.ConvertFromInvariantString(text);
                            } catch (NotSupportedException nse) {
                                string newMessage = SR.GetString(SR.NotSupported, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X, nse.Message);
                                XmlException xml = new XmlException(newMessage, nse, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X);
                                NotSupportedException newNse = new NotSupportedException(newMessage, xml);
                                throw newNse;
                            }
                        }
                        else {
                            Debug.WriteLine("Converter for " + type.FullName + " doesn't support string conversion");
                        }
                    }
                }
                else {
                    string newMessage = SR.GetString(SR.TypeLoadException, typeName, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X);
                    XmlException xml = new XmlException(newMessage, null, dataNodeInfo.ReaderPosition.Y, dataNodeInfo.ReaderPosition.X);
                    TypeLoadException newTle = new TypeLoadException(newMessage, xml);
 
                    throw newTle;
                }
            }
            else {
                // if mimeTypeName and typeName are not filled in, the value must be a string
                Debug.Assert(value is string, "Resource entries with no Type or MimeType must be encoded as strings");
            }
            return result;
        }
 
        internal DataNodeInfo GetDataNodeInfo() {
            bool shouldSerialize = true;
            if(nodeInfo != null) {
                shouldSerialize = false;
            } else {
                nodeInfo = new DataNodeInfo();
            }
            nodeInfo.Name = Name;
            nodeInfo.Comment = Comment;
 
            // We always serialize if this node represents a FileRef. This is because FileRef is a public property,
            // so someone could have modified it.
            if(shouldSerialize || FileRefFullPath != null) {
                // if we dont have a datanodeinfo it could be either
                // a direct object OR a fileref
                if(FileRefFullPath != null) {
                    nodeInfo.ValueData = FileRef.ToString();
                    nodeInfo.MimeType = null;
                    nodeInfo.TypeName = MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXFileRef), this.typeNameConverter);
                } else {
                    // serialize to string inside the nodeInfo
                    FillDataNodeInfoFromObject(nodeInfo, value);
                }
                
            }
            return nodeInfo;
        }
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.GetNodePosition"]/*' />
        /// <devdoc>
        ///    Might return the position in the resx file of the current node, if known
        ///    otherwise, will return Point(0,0) since point is a struct 
        /// </devdoc>
        public Point GetNodePosition() {
            if(nodeInfo == null) {
                return new Point();
            } else {
                return nodeInfo.ReaderPosition;
            }
        }
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.GetValueTypeName"]/*' />
        /// <devdoc>
        ///    Get the FQ type name for this datanode.
        ///    We return typeof(object) for ResXNullRef
        /// </devdoc>
        public string GetValueTypeName(ITypeResolutionService typeResolver) {
            // the type name here is always a FQN
            if(typeName != null && typeName.Length >0) {
                if (typeName.Equals(MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter))) {
                    return MultitargetUtil.GetAssemblyQualifiedName(typeof(object), this.typeNameConverter);
                } else {
                    return typeName;
                }
            }
            string result = FileRefType;
            Type objectType = null;
            // do we have a fileref?
            if(result != null) {
                // try to resolve this type
                objectType = ResolveType(FileRefType, typeResolver);
            } else if(nodeInfo != null) {
                
                // we dont have a fileref, try to resolve the type of the datanode
                result = nodeInfo.TypeName;
                // if typename is null, the default is just a string
                if(result == null || result.Length==0) {
                    // we still dont know... do we have a mimetype? if yes, our only option is to 
                    // deserialize to know what we're dealing with... very inefficient...
                    if(nodeInfo.MimeType != null && nodeInfo.MimeType.Length > 0) {
                        object insideObject = null;
 
                        try {
                            insideObject = GenerateObjectFromDataNodeInfo(nodeInfo, typeResolver);
                        } catch (Exception ex) { // it'd be better to catch SerializationException but the underlying type resolver
                                                // can throw things like FileNotFoundException which is kinda confusing, so I am catching all here..
                            if(ClientUtils.IsCriticalException(ex)) {
                                throw;
                            }
                            // something went wrong, type is not specified at all or stream is corrupted
                            // return system.object
                            result = MultitargetUtil.GetAssemblyQualifiedName(typeof(object), this.typeNameConverter);
                        }
 
                        if(insideObject != null) {
                            result = MultitargetUtil.GetAssemblyQualifiedName(insideObject.GetType(), this.typeNameConverter);
                        }
                    } else {
                        // no typename, no mimetype, we have a string...
                        result = MultitargetUtil.GetAssemblyQualifiedName(typeof(string), this.typeNameConverter);
                    }
                } else {
                    objectType = ResolveType(nodeInfo.TypeName, typeResolver);    
                }
            }
            if(objectType != null) {
                if(objectType == typeof(ResXNullRef)) {
                    result = MultitargetUtil.GetAssemblyQualifiedName(typeof(object), this.typeNameConverter);
                } else {
                    result = MultitargetUtil.GetAssemblyQualifiedName(objectType, this.typeNameConverter);
                }
            }
            return result;
        }
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.GetValueTypeName1"]/*' />
        /// <devdoc>
        ///    Get the FQ type name for this datanode
        /// </devdoc>
        public string GetValueTypeName(AssemblyName[] names) {
            return GetValueTypeName(new AssemblyNamesTypeResolutionService(names));
        }
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.GetValue"]/*' />
        /// <devdoc>
        ///    Get the value contained in this datanode
        /// </devdoc>
        public object GetValue(ITypeResolutionService typeResolver) {
 
            if(value != null) {
                return value;
            }
 
            object result = null;
            if(FileRefFullPath != null) {
                Type objectType = ResolveType(FileRefType , typeResolver);
                if(objectType != null) {
                    // we have the FQN for this type
                    if(FileRefTextEncoding != null) {
                        fileRef = new ResXFileRef(FileRefFullPath, FileRefType, Encoding.GetEncoding(FileRefTextEncoding));
                    } else {
                        fileRef = new ResXFileRef(FileRefFullPath, FileRefType);
                    }
                    TypeConverter tc = TypeDescriptor.GetConverter(typeof(ResXFileRef));
                    result = tc.ConvertFrom(fileRef.ToString());
                } else {
                    string newMessage = SR.GetString(SR.TypeLoadExceptionShort, FileRefType);
                    TypeLoadException newTle = new TypeLoadException(newMessage);
                    throw (newTle);
                }
            } else if(result == null && nodeInfo.ValueData!= null) {
                // it's embedded, we deserialize it
                result = GenerateObjectFromDataNodeInfo(nodeInfo, typeResolver);
            } else {
                // schema is wrong and say minOccur for Value is 0,
                // but it's too late to change it...
                // we need to return null here vswhidbey 180605
                return null;
            }    
            return result;
        }
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.GetValue1"]/*' />
        /// <devdoc>
        ///    Get the value contained in this datanode
        /// </devdoc>
        public object GetValue(AssemblyName[] names) {
            return GetValue(new AssemblyNamesTypeResolutionService(names));
        }
 
        private static byte[] FromBase64WrappedString(string text) {
 
            if (text.IndexOfAny(SpecialChars) != -1) {
                StringBuilder sb = new StringBuilder(text.Length);
                for (int i=0; i<text.Length; i++) {
                    switch (text[i]) {
                        case ' ':
                        case '\r':
                        case '\n':
                            break;
                        default:
                            sb.Append(text[i]);
                            break;
                    }
                }
                return Convert.FromBase64String(sb.ToString());
            }
            else {
                return Convert.FromBase64String(text);
            }
        }
 
 
        private Type ResolveType(string typeName, ITypeResolutionService typeResolver) {
            Type t = null;            
            if (typeResolver != null) {
 
                // If we cannot find the strong-named type, then try to see
                // if the TypeResolver can bind to partial names. For this, 
                // we will strip out the partial names and keep the rest of the
                // strong-name information to try again.
                //
 
                t = typeResolver.GetType(typeName, false);
                if (t == null) {
                    
                    string[] typeParts = typeName.Split(new char[] {','});
 
                    // Break up the type name from the rest of the assembly strong name.
                    //
                    if (typeParts != null && typeParts.Length >= 2) {
                        string partialName = typeParts[0].Trim();
                        string assemblyName = typeParts[1].Trim();                            
                        partialName = partialName + ", " + assemblyName;
                        t = typeResolver.GetType(partialName, false);
                    }
                }
            }
 
            if (t == null) {
                t = Type.GetType(typeName, false);
            }
            
            return t;
        }
                
 
        /// <include file='doc\ResXDataNode.uex' path='docs/doc[@for="ResXDataNode.GetObjectData"]/*' />
        /// <devdoc>
        ///    Get the value contained in this datanode
        /// </devdoc>        
        // NOTE: No LinkDemand for SerializationFormatter necessary here, since this class already  
        // has a FullTrust LinkDemand.
        void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) {
            DataNodeInfo nodeInfo = GetDataNodeInfo();
            si.AddValue("Name", nodeInfo.Name, typeof(string));
            si.AddValue("Comment", nodeInfo.Comment, typeof(string));
            si.AddValue("TypeName", nodeInfo.TypeName, typeof(string));
            si.AddValue("MimeType", nodeInfo.MimeType, typeof(string));
            si.AddValue("ValueData", nodeInfo.ValueData, typeof(string));
        }
 
        private ResXDataNode(SerializationInfo info, StreamingContext context) {
            DataNodeInfo nodeInfo = new DataNodeInfo();
            nodeInfo.Name = (string)info.GetValue("Name", typeof(string));
            nodeInfo.Comment = (string)info.GetValue("Comment", typeof(string));
            nodeInfo.TypeName = (string)info.GetValue("TypeName", typeof(string));
            nodeInfo.MimeType = (string)info.GetValue("MimeType", typeof(string));
            nodeInfo.ValueData = (string)info.GetValue("ValueData", typeof(string));
            this.nodeInfo = nodeInfo;
            InitializeDataNode(null);
        }
    }
 
    internal class DataNodeInfo {
        internal string Name;
        internal string Comment;
        internal string TypeName;
        internal string MimeType;
        internal string ValueData;
        internal Point ReaderPosition; //only used to track position in the reader
 
        internal DataNodeInfo Clone() {
            DataNodeInfo result  = new DataNodeInfo();
            result.Name = this.Name;
            result.Comment = this.Comment;
            result.TypeName = this.TypeName;
            result.MimeType = this.MimeType;
            result.ValueData = this.ValueData;
            result.ReaderPosition = new Point(this.ReaderPosition.X, this.ReaderPosition.Y);
            return result;
        }
    }
 
    // This class implements a partial type resolver for the BinaryFormatter.
    // This is needed to be able to read binary serialized content from older
    // NDP types and map them to newer versions.
    //
    internal class ResXSerializationBinder : SerializationBinder {
        private ITypeResolutionService typeResolver;
        private Func<Type, string> typeNameConverter;
 
        internal ResXSerializationBinder(ITypeResolutionService typeResolver) {
            this.typeResolver = typeResolver;
        }
 
        internal ResXSerializationBinder(Func<Type, string> typeNameConverter) {
            this.typeNameConverter = typeNameConverter;
        }
 
        public override Type BindToType(string assemblyName, string typeName) {
            if (typeResolver == null) {
                return null;
            }
 
            typeName = typeName + ", " + assemblyName;
 
            Type t = typeResolver.GetType(typeName);
            if (t == null) {
                string[] typeParts = typeName.Split(new char[] {','});
 
                // Break up the assembly name from the rest of the assembly strong name.
                // we try 1) FQN 2) FQN without a version 3) just the short name
                if (typeParts != null && typeParts.Length > 2) {
                    string partialName = typeParts[0].Trim();
 
                    for (int i = 1; i < typeParts.Length; ++i) {
                        string s = typeParts[i].Trim();
                        if (!s.StartsWith("Version=") && !s.StartsWith("version=")) {
                            partialName = partialName + ", " + s;
                        }
                    }
                    t = typeResolver.GetType(partialName);
                    if(t == null) {
                        t = typeResolver.GetType(typeParts[0].Trim());
                    }
                }
            }
 
            // Binder couldn't handle it, let the default loader take over.
            return t;
        }
 
        //
        // Get the multitarget-aware string representation for the give type.
        public override void BindToName(Type serializedType, out string assemblyName, out string typeName) {
            // Normally we don't change typeName when changing the target framework, 
            // only assembly version or assembly name might change, thus we are setting 
            // typeName only if it changed with the framework version.
            // If binder passes in a null, BinaryFormatter will use the original value or 
            // for un-serializable types will redirect to another type. 
            // For example:
            //
            // Encoding = Encoding.GetEncoding("shift_jis");
            // public Encoding Encoding { get; set; }
            // property type (Encoding) is abstract, but the value is instantiated to a specific class,
            // and should be serialized as a specific class in order to be able to instantiate the result.
            //
            // another example are singleton objects like DBNull.Value which are serialized by System.UnitySerializationHolder
            typeName = null;
            if (typeNameConverter != null) {
                string assemblyQualifiedTypeName = MultitargetUtil.GetAssemblyQualifiedName(serializedType, typeNameConverter);
                if (!string.IsNullOrEmpty(assemblyQualifiedTypeName)) {
                    int pos = assemblyQualifiedTypeName.IndexOf(',');
                    if (pos > 0 && pos < assemblyQualifiedTypeName.Length - 1) {
                        assemblyName = assemblyQualifiedTypeName.Substring(pos + 1).TrimStart();
                        string newTypeName = assemblyQualifiedTypeName.Substring(0, pos);
                        if (!string.Equals(newTypeName, serializedType.FullName, StringComparison.InvariantCulture)) {
                            typeName = newTypeName;
                        }
                        return;
                    }
                }
            }
 
            base.BindToName(serializedType, out assemblyName, out typeName);
        }
    }
 
 
    internal class AssemblyNamesTypeResolutionService : ITypeResolutionService {
        private AssemblyName[] names;
        private Hashtable cachedAssemblies;
        private Hashtable cachedTypes;
 
        private static string NetFrameworkPath = Path.Combine(Environment.GetEnvironmentVariable("SystemRoot"), "Microsoft.Net\\Framework");
 
        internal AssemblyNamesTypeResolutionService(AssemblyName[] names) {
            this.names = names;
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public Assembly GetAssembly(AssemblyName name) {
            return GetAssembly(name, true);
        }
 
        //
        [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public Assembly GetAssembly(AssemblyName name, bool throwOnError) {
 
            Assembly result = null;
 
            if (cachedAssemblies == null) {
                cachedAssemblies = Hashtable.Synchronized(new Hashtable());
            }
 
            if (cachedAssemblies.Contains(name)) {
                result = cachedAssemblies[name] as Assembly;
            }
            
            if (result == null) {
                // try to load it first from the gac
#pragma warning disable 0618
                //Although LoadWithPartialName is obsolete, we still have to call it: changing 
                //this would be breaking in cases where people edited their resource files by
                //hand.
                result = Assembly.LoadWithPartialName(name.FullName);
#pragma warning restore 0618                            
                if(result != null) {
                    cachedAssemblies[name] = result;
                } 
                else if (names != null) {
                    for(int i=0;i<names.Length; i++) {
                        if(name.Equals(names[i])) {
                            try {
                                result = Assembly.LoadFrom(GetPathOfAssembly(name));
                                if(result != null) {
                                    cachedAssemblies[name] = result;
                                }
                            }
                            catch {
                                if(throwOnError) {
                                    throw;
                                }
                            }
                        }   
                    }
                }
            }
            
            return result;
        }
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public string GetPathOfAssembly(AssemblyName name) {
            return name.CodeBase;
        }
 
      
        public Type GetType(string name) {
            return GetType(name, true);
        }
 
        public Type GetType(string name, bool throwOnError) {
            return GetType(name, throwOnError, false);
        }
 
        public Type GetType(string name, bool throwOnError, bool ignoreCase) {
            Type result = null;
 
            // Check type cache first
            if (cachedTypes == null) {
                cachedTypes = Hashtable.Synchronized(new Hashtable(StringComparer.Ordinal));
            }
 
            if (cachedTypes.Contains(name)) {
                result = cachedTypes[name] as Type;
                return result;
            }
 
            // Missed in cache, try to resolve the type. First try to resolve in the GAC
            if(name.IndexOf(',') != -1) {
                result = Type.GetType(name, false, ignoreCase);
            }
 
            //
            // Did not find it in the GAC, check the reference assemblies
            if(result == null && names != null) {
                //
                // If the type is assembly qualified name, we sort the assembly names
                // to put assemblies with same name in the front so that they can
                // be searched first.
                int pos = name.IndexOf(',');
                if (pos > 0 && pos < name.Length - 1) {
                    string fullName = name.Substring(pos + 1).Trim();
                    AssemblyName assemblyName = null;
                    try {
                        assemblyName = new AssemblyName(fullName);
                    }
                    catch {
                    }
 
                    if (assemblyName != null) {
                        List<AssemblyName> assemblyList = new List<AssemblyName>(names.Length);
                        for (int i = 0; i < names.Length; i++) {
                            if (string.Compare(assemblyName.Name, names[i].Name, StringComparison.OrdinalIgnoreCase) == 0) {
                                assemblyList.Insert(0, names[i]);
                            }
                            else {
                                assemblyList.Add(names[i]);
                            }
                        }
                        names = assemblyList.ToArray();
                    }
                }
 
                // Search each reference assembly
                for(int i = 0; i < names.Length; i++) {
                    Assembly a = GetAssembly(names[i], false);
                    if (a != null) {
                        result = a.GetType(name, false, ignoreCase);
                        if(result == null) {
                            int indexOfComma = name.IndexOf(",");
                            if(indexOfComma != -1) {
                                string shortName = name.Substring(0, indexOfComma );
                                result = a.GetType(shortName, false, ignoreCase);
                            }
                        }
                    }
 
                    if(result != null)
                        break;
                }
            }
 
            if(result == null && throwOnError) {
                throw new ArgumentException(SR.GetString(SR.InvalidResXNoType, name));
            }
 
            if(result != null) {
                // Only cache types from .Net framework or GAC because they don't need to update.
                // For simplicity, don't cache custom types
                if (result.Assembly.GlobalAssemblyCache || IsNetFrameworkAssembly(result.Assembly.Location)) {
                    cachedTypes[name] = result;
                }
            }
 
            return result;
        }
 
        /// <devdoc>
        /// This is matching %windir%\Microsoft.NET\Framework*, so both 32bit and 64bit framework will be covered.
        /// </devdoc>
        private bool IsNetFrameworkAssembly(string assemblyPath)
        {
            return assemblyPath != null && assemblyPath.StartsWith(NetFrameworkPath, StringComparison.OrdinalIgnoreCase);
        }
 
        public void ReferenceAssembly(AssemblyName name) {
            throw new NotSupportedException();
        }
        
    }
}