File: winforms\Managed\System\Resources\ResXFileRef.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="ResXFileRef.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;
    using System.Windows.Forms;
    using System.Reflection;
    using Microsoft.Win32;
    using System.Drawing;
    using System.IO;
    using System.ComponentModel;
    using System.Collections;
    using System.Resources;
    using System.Text;
    using System.Globalization;
    using System.Runtime.Serialization;
    using System.Runtime.Versioning;
 
    /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef"]/*' />
    /// <devdoc>
    ///     ResX File Reference class. This allows the developer to represent
    ///     a link to an external resource. When the resource manager asks
    ///     for the value of the resource item, the external resource is loaded.
    /// </devdoc>
    [TypeConverterAttribute(typeof(ResXFileRef.Converter)), Serializable]
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")]
    [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")]
#if SYSTEM_WEB
    internal class ResXFileRef {
#else
    public class ResXFileRef {
#endif
        private string fileName;
        private string typeName;
        [OptionalField(VersionAdded = 2)]
        private Encoding textFileEncoding;
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.ResXFileRef"]/*' />
        /// <devdoc>
        ///     Creates a new ResXFileRef that points to the specified file.
        ///     The type refered to by typeName must support a constructor
        ///     that accepts a System.IO.Stream as a parameter.
        /// </devdoc>
        public ResXFileRef(string fileName, string typeName) {
            if(fileName == null) {
                throw (new ArgumentNullException("fileName"));
            }
            if(typeName == null) {
                throw (new ArgumentNullException("typeName"));
            }
            this.fileName = fileName;
            this.typeName = typeName;
        }
 
        
        [OnDeserializing]      
        private void OnDeserializing(StreamingContext ctx) {
            textFileEncoding = null;
        }
 
        [SuppressMessage("Microsoft.Performance", "CA1822:MarkMethodsAsStatic")]
        [OnDeserialized]      
        private void OnDeserialized(StreamingContext ctx) {
        }
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.ResXFileRef"]/*' />
        /// <devdoc>
        ///     Creates a new ResXFileRef that points to the specified file.
        ///     The type refered to by typeName must support a constructor
        ///     that accepts a System.IO.Stream as a parameter.
        /// </devdoc>
        public ResXFileRef(string fileName, string typeName, Encoding textFileEncoding) : this(fileName, typeName)  {
            this.textFileEncoding = textFileEncoding;
        }
        
        internal ResXFileRef Clone() {
            return new ResXFileRef(fileName, typeName, textFileEncoding);
        }
 
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.FileName"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string FileName {
            get {
                return fileName;
            }
        }
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.TextFileEncoding"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public string TypeName {
            get {
                return typeName;
            }
        }
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.TextFileEncoding"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public Encoding TextFileEncoding {
            get {
                return textFileEncoding;
            }
        }
 
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.PathDifference"]/*' />
        /// <devdoc>
        ///    path1+result = path2
        ///   A string which is the relative path difference between path1 and
        ///  path2 such that if path1 and the calculated difference are used
        ///  as arguments to Combine(), path2 is returned
        /// </devdoc>
        private static string PathDifference(string path1, string path2, bool compareCase) {
            int i;
            int si = -1;
 
            for (i = 0; (i < path1.Length) && (i < path2.Length); ++i) {
                if ((path1[i] != path2[i]) && (compareCase || (Char.ToLower(path1[i], CultureInfo.InvariantCulture) != Char.ToLower(path2[i], CultureInfo.InvariantCulture))))
                {
                    break;
 
                } else if (path1[i] == Path.DirectorySeparatorChar) {
                    si = i;
                }
            }
 
            if (i == 0) {
                return path2;
            }
            if ((i == path1.Length) && (i == path2.Length)) {
                return String.Empty;
            }
 
            StringBuilder relPath = new StringBuilder();
 
            for (; i < path1.Length; ++i) {
                if (path1[i] == Path.DirectorySeparatorChar) {
                    relPath.Append(".."+Path.DirectorySeparatorChar);
                }
            }
            return relPath.ToString() + path2.Substring(si + 1);
        }
 
        
        internal void MakeFilePathRelative(string basePath) {
            
            if(basePath==null || basePath.Length == 0) {
                return;
            }
            fileName = PathDifference(basePath, fileName, false);
        }
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.ToString"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public override string ToString() {
            string result = "";
            
            if(fileName.IndexOf(";") != -1 || fileName.IndexOf("\"") != -1) {
                result += ("\""+ fileName + "\";");
            } else {
                result += (fileName + ";");
            }
            result += typeName;
            if(textFileEncoding != null) {
                result += (";" + textFileEncoding.WebName);
            }
            return result;
        }
 
       
 
 
        /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.Converter"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Name="FullTrust")]
        [System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Name="FullTrust")]
        public class Converter : TypeConverter {
            /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.Converter.CanConvertFrom"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public override bool CanConvertFrom(ITypeDescriptorContext context,
                                                Type sourceType) {
                if (sourceType == typeof(string)) {
                    return true;
                }
                return false;
            }
 
            /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.Converter.CanConvertTo"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public override bool CanConvertTo(ITypeDescriptorContext context, 
                                              Type destinationType) {
                if (destinationType == typeof(string)) {
                    return true;
                }
                return false;
            }
 
            /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.Converter.ConvertTo"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            public override Object ConvertTo(ITypeDescriptorContext context, 
                                             CultureInfo culture, 
                                             Object value, 
                                             Type destinationType) {
                Object created = null;
                if (destinationType == typeof(string)) {
                    created = ((ResXFileRef)value).ToString();
                }
                return created;
            }
 
            // "value" is the parameter name of ConvertFrom, which calls this method.
            [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")]
            internal static string[] ParseResxFileRefString(string stringValue) {
                string[] result = null;
                if(stringValue != null ) {
                    stringValue = stringValue.Trim();
                    string fileName;
                    string remainingString;
                    if(stringValue.StartsWith("\"")) {
                        int lastIndexOfQuote = stringValue.LastIndexOf("\"");
                        if (lastIndexOfQuote-1<0)
                            throw new ArgumentException("value");
                        fileName = stringValue.Substring(1, lastIndexOfQuote-1); // remove the quotes in" ..... " 
                        if(lastIndexOfQuote+2>stringValue.Length)
                            throw new ArgumentException("value");
                        remainingString = stringValue.Substring(lastIndexOfQuote+2);
                    } else {
                        int nextSemiColumn = stringValue.IndexOf(";");
                        if(nextSemiColumn == -1)
                            throw new ArgumentException("value");
                        fileName = stringValue.Substring(0,nextSemiColumn);
                        if(nextSemiColumn+1>stringValue.Length)
                            throw new ArgumentException("value");
                        remainingString = stringValue.Substring(nextSemiColumn+1);
                    }
                    string[] parts = remainingString.Split(new char[] {';'});
                    if(parts.Length > 1) {
                        result = new string[] { fileName, parts[0], parts[1] };
                    } else if(parts.Length > 0) {
                        result = new string[] { fileName, parts[0] };
                    } else {
                        result = new string[] { fileName };
                    }
                }
                return result;  
            }
 
            /// <include file='doc\ResXFileRef.uex' path='docs/doc[@for="ResXFileRef.Converter.ConvertFrom"]/*' />
            /// <devdoc>
            ///    <para>[To be supplied.]</para>
            /// </devdoc>
            [ResourceExposure(ResourceScope.Machine)]
            [ResourceConsumption(ResourceScope.Machine)]
            public override Object ConvertFrom(ITypeDescriptorContext context, 
                                               CultureInfo culture,
                                               Object value) {
                Object created = null;
                string stringValue = value as string;
                if (stringValue != null) {
                    string[] parts = ResXFileRef.Converter.ParseResxFileRefString(stringValue);
                    string fileName = parts[0];
                    Type toCreate = Type.GetType(parts[1], true);
 
                    // special case string and byte[]
                    if(toCreate.Equals(typeof(string))) {
                        // we have a string, now we need to check the encoding
                        Encoding textFileEncoding = Encoding.Default;
                        if(parts.Length > 2) {
                            textFileEncoding = Encoding.GetEncoding(parts[2]);
                        }
                        using (StreamReader sr = new StreamReader(fileName, textFileEncoding)) {
                            created = sr.ReadToEnd();
                        }
                    } else {
 
                        // this is a regular file, we call it's constructor with a stream as a parameter
                        // or if it's a byte array we just return that
                        byte[] temp = null;
 
                        using (FileStream s = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
                            Debug.Assert(s != null, "Couldn't open " + fileName);
                            temp = new byte[s.Length];
                            s.Read(temp, 0, (int)s.Length);
                        }
 
                        if(toCreate.Equals(typeof(byte[]))) {
                            created = temp;
                        } else {
                            MemoryStream memStream = new MemoryStream(temp);
                            if(toCreate.Equals(typeof(MemoryStream))) {
                                return memStream;
                            } else if(toCreate.Equals(typeof(System.Drawing.Bitmap)) && fileName.EndsWith(".ico")) {
                                // we special case the .ico bitmaps because GDI+ destroy the alpha channel component and
                                // we don't want that to happen
                                Icon ico = new Icon(memStream);
                                created = ico.ToBitmap();
                            } else {
                                created = Activator.CreateInstance(toCreate, BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, null, new Object[] {memStream}, null);
                            }
                        }
                    }
                }
                return created;
            }
 
        }
    }
}