File: winforms\Managed\System\Resources\ResXResourceWriter.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="ResXResourceWriter.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.Reflection;
    using System;
    using System.Windows.Forms;    
    using Microsoft.Win32;
    using System.Drawing;
    using System.IO;
    using System.Text;
    using System.ComponentModel;
    using System.Collections;
    using System.Resources;
    using System.Xml;
    using System.Runtime.Serialization;
    using System.Diagnostics.CodeAnalysis;
#if SYSTEM_WEB
    using System.Web;   // This is needed to access the SR resource strings
#endif
 
    /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter"]/*' />
    /// <devdoc>
    ///     ResX resource writer. See the text in "ResourceSchema" for more 
    ///     information.
    /// </devdoc>
    [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 ResXResourceWriter : IResourceWriter {
#else
    public class ResXResourceWriter : IResourceWriter {
#endif
        internal const string TypeStr = "type";
        internal const string NameStr = "name";
        internal const string DataStr = "data";
        internal const string MetadataStr = "metadata";
        internal const string MimeTypeStr = "mimetype";
        internal const string ValueStr = "value";
        internal const string ResHeaderStr = "resheader";
        internal const string VersionStr = "version";
        internal const string ResMimeTypeStr = "resmimetype";
        internal const string ReaderStr = "reader";
        internal const string WriterStr = "writer";
        internal const string CommentStr = "comment";
        internal const string AssemblyStr ="assembly";
        internal const string AliasStr= "alias" ;
 
        private Hashtable cachedAliases;
 
        private static TraceSwitch ResValueProviderSwitch = new TraceSwitch("ResX", "Debug the resource value provider");
 
        // 
 
 
 
 
 
 
 
        internal static readonly string Beta2CompatSerializedObjectMimeType = "text/microsoft-urt/psuedoml-serialized/base64";
 
        // These two "compat" mimetypes are here. In Beta 2 and RTM we used the term "URT"
        // internally to refer to parts of the .NET Framework. Since these references
        // will be in Beta 2 ResX files, and RTM ResX files for customers that had 
        // early access to releases, we don't want to break that. We will read 
        // and parse these types correctly in version 1.0, but will always 
        // write out the new version. So, opening and editing a ResX file in VS will
        // update it to the new types.
        //
        internal static readonly string CompatBinSerializedObjectMimeType = "text/microsoft-urt/binary-serialized/base64";
        internal static readonly string CompatSoapSerializedObjectMimeType = "text/microsoft-urt/soap-serialized/base64";
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.BinSerializedObjectMimeType"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string BinSerializedObjectMimeType = "application/x-microsoft.net.object.binary.base64";
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.SoapSerializedObjectMimeType"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string SoapSerializedObjectMimeType = "application/x-microsoft.net.object.soap.base64";
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.DefaultSerializedObjectMimeType"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string DefaultSerializedObjectMimeType = BinSerializedObjectMimeType;
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.ByteArraySerializedObjectMimeType"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string ByteArraySerializedObjectMimeType = "application/x-microsoft.net.object.bytearray.base64";
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.ResMimeType"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string ResMimeType = "text/microsoft-resx";
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.Version"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string Version = "2.0";
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.ResourceSchema"]/*' />
        /// <internalonly/>
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public static readonly string ResourceSchema = @"
    <!-- 
    Microsoft ResX Schema 
    
    Version " + Version + @"
    
    The primary goals of this format is to allow a simple XML format 
    that is mostly human readable. The generation and parsing of the 
    various data types are done through the TypeConverter classes 
    associated with the data types.
    
    Example:
    
    ... ado.net/XML headers & schema ...
    <resheader name=""resmimetype"">text/microsoft-resx</resheader>
    <resheader name=""version"">" + Version + @"</resheader>
    <resheader name=""reader"">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    <resheader name=""writer"">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    <data name=""Name1""><value>this is my long string</value><comment>this is a comment</comment></data>
    <data name=""Color1"" type=""System.Drawing.Color, System.Drawing"">Blue</data>
    <data name=""Bitmap1"" mimetype=""" + BinSerializedObjectMimeType + @""">
        <value>[base64 mime encoded serialized .NET Framework object]</value>
    </data>
    <data name=""Icon1"" type=""System.Drawing.Icon, System.Drawing"" mimetype=""" + ByteArraySerializedObjectMimeType + @""">
        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
        <comment>This is a comment</comment>
    </data>
                
    There are any number of ""resheader"" rows that contain simple 
    name/value pairs.
    
    Each data row contains a name, and value. The row also contains a 
    type or mimetype. Type corresponds to a .NET class that support 
    text/value conversion through the TypeConverter architecture. 
    Classes that don't support this are serialized and stored with the 
    mimetype set.
    
    The mimetype is used for serialized objects, and tells the 
    ResXResourceReader how to depersist the object. This is currently not 
    extensible. For a given mimetype the value must be set accordingly:
    
    Note - " + BinSerializedObjectMimeType + @" is the format 
    that the ResXResourceWriter will generate, however the reader can 
    read any of the formats listed below.
    
    mimetype: " + BinSerializedObjectMimeType + @"
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
            : and then encoded with base64 encoding.
    
    mimetype: " + SoapSerializedObjectMimeType + @"
    value   : The object must be serialized with 
            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
            : and then encoded with base64 encoding.
 
    mimetype: " + ByteArraySerializedObjectMimeType + @"
    value   : The object must be serialized into a byte array 
            : using a System.ComponentModel.TypeConverter
            : and then encoded with base64 encoding.
    -->
    <xsd:schema id=""root"" xmlns="""" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:msdata=""urn:schemas-microsoft-com:xml-msdata"">
        <xsd:import namespace=""http://www.w3.org/XML/1998/namespace""/>
        <xsd:element name=""root"" msdata:IsDataSet=""true"">
            <xsd:complexType>
                <xsd:choice maxOccurs=""unbounded"">
                    <xsd:element name=""metadata"">
                        <xsd:complexType>
                            <xsd:sequence>
                            <xsd:element name=""value"" type=""xsd:string"" minOccurs=""0""/>
                            </xsd:sequence>
                            <xsd:attribute name=""name"" use=""required"" type=""xsd:string""/>
                            <xsd:attribute name=""type"" type=""xsd:string""/>
                            <xsd:attribute name=""mimetype"" type=""xsd:string""/>
                            <xsd:attribute ref=""xml:space""/>                            
                        </xsd:complexType>
                    </xsd:element>
                    <xsd:element name=""assembly"">
                      <xsd:complexType>
                        <xsd:attribute name=""alias"" type=""xsd:string""/>
                        <xsd:attribute name=""name"" type=""xsd:string""/>
                      </xsd:complexType>
                    </xsd:element>
                    <xsd:element name=""data"">
                        <xsd:complexType>
                            <xsd:sequence>
                                <xsd:element name=""value"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""1"" />
                                <xsd:element name=""comment"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""2"" />
                            </xsd:sequence>
                            <xsd:attribute name=""name"" type=""xsd:string"" use=""required"" msdata:Ordinal=""1"" />
                            <xsd:attribute name=""type"" type=""xsd:string"" msdata:Ordinal=""3"" />
                            <xsd:attribute name=""mimetype"" type=""xsd:string"" msdata:Ordinal=""4"" />
                            <xsd:attribute ref=""xml:space""/>
                        </xsd:complexType>
                    </xsd:element>
                    <xsd:element name=""resheader"">
                        <xsd:complexType>
                            <xsd:sequence>
                                <xsd:element name=""value"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""1"" />
                            </xsd:sequence>
                            <xsd:attribute name=""name"" type=""xsd:string"" use=""required"" />
                        </xsd:complexType>
                    </xsd:element>
                </xsd:choice>
            </xsd:complexType>
        </xsd:element>
        </xsd:schema>
        ";
        
        IFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter ();
        string fileName;
        Stream stream;
        TextWriter textWriter;
        XmlTextWriter xmlTextWriter;
        string basePath;
 
        bool hasBeenSaved;
        bool initialized;
 
        private Func<Type, string> typeNameConverter; // no public property to be consistent with ResXDataNode class.
        
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.BasePath"]/*' />
        /// <devdoc>
        ///     Base Path for ResXFileRefs.
        /// </devdoc>
        public string BasePath {
            get {
                return basePath;
            }
            set {
                basePath = value;
            }
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.ResXResourceWriter"]/*' />
        /// <devdoc>
        ///     Creates a new ResXResourceWriter that will write to the specified file.
        /// </devdoc>
        public ResXResourceWriter(string fileName) {
            this.fileName = fileName;
        }
        public ResXResourceWriter(string fileName, Func<Type, string> typeNameConverter) {
            this.fileName = fileName;
            this.typeNameConverter = typeNameConverter;
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.ResXResourceWriter1"]/*' />
        /// <devdoc>
        ///     Creates a new ResXResourceWriter that will write to the specified stream.
        /// </devdoc>
        public ResXResourceWriter(Stream stream) {
            this.stream = stream;
        }
        public ResXResourceWriter(Stream stream, Func<Type, string> typeNameConverter) {
            this.stream = stream;
            this.typeNameConverter = typeNameConverter;
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.ResXResourceWriter2"]/*' />
        /// <devdoc>
        ///     Creates a new ResXResourceWriter that will write to the specified TextWriter.
        /// </devdoc>
        public ResXResourceWriter(TextWriter textWriter) {
            this.textWriter = textWriter;
        }
        public ResXResourceWriter(TextWriter textWriter, Func<Type, string> typeNameConverter) {
            this.textWriter = textWriter;
            this.typeNameConverter = typeNameConverter;
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.Finalize"]/*' />
        ~ResXResourceWriter() {
            Dispose(false);
        }
 
        private void InitializeWriter() {
            if (xmlTextWriter == null) {
                // 
 
                bool writeHeaderHack = false;
 
                if (textWriter != null) {
                    textWriter.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                    writeHeaderHack = true;
 
                    xmlTextWriter = new XmlTextWriter(textWriter);
                }
                else if (stream != null) {
                    xmlTextWriter = new XmlTextWriter(stream, System.Text.Encoding.UTF8);
                }
                else {
                    Debug.Assert(fileName != null, "Nothing to output to");
                    xmlTextWriter = new XmlTextWriter(fileName, System.Text.Encoding.UTF8);
                }
                xmlTextWriter.Formatting = Formatting.Indented;
                xmlTextWriter.Indentation = 2;
 
                if (!writeHeaderHack) {
                    xmlTextWriter.WriteStartDocument(); // writes <?xml version="1.0" encoding="utf-8"?>
                }
            }
            else {
                xmlTextWriter.WriteStartDocument();
            }
 
            xmlTextWriter.WriteStartElement("root");
            XmlTextReader reader = new XmlTextReader(new StringReader(ResourceSchema));
            reader.WhitespaceHandling = WhitespaceHandling.None;
            xmlTextWriter.WriteNode(reader, true);
 
            xmlTextWriter.WriteStartElement(ResHeaderStr); {
                xmlTextWriter.WriteAttributeString(NameStr, ResMimeTypeStr);
                xmlTextWriter.WriteStartElement(ValueStr); {
                    xmlTextWriter.WriteString(ResMimeType);
                }
                xmlTextWriter.WriteEndElement();
            }
            xmlTextWriter.WriteEndElement();
            xmlTextWriter.WriteStartElement(ResHeaderStr); {
                xmlTextWriter.WriteAttributeString(NameStr, VersionStr);
                xmlTextWriter.WriteStartElement(ValueStr); {
                    xmlTextWriter.WriteString(Version);
                }
                xmlTextWriter.WriteEndElement();
            }
            xmlTextWriter.WriteEndElement();
            xmlTextWriter.WriteStartElement(ResHeaderStr); {
                xmlTextWriter.WriteAttributeString(NameStr, ReaderStr);
                xmlTextWriter.WriteStartElement(ValueStr); {
                    xmlTextWriter.WriteString(MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXResourceReader), this.typeNameConverter));
                }
                xmlTextWriter.WriteEndElement();
            }
            xmlTextWriter.WriteEndElement();
            xmlTextWriter.WriteStartElement(ResHeaderStr); {
                xmlTextWriter.WriteAttributeString(NameStr, WriterStr);
                xmlTextWriter.WriteStartElement(ValueStr); {
                    xmlTextWriter.WriteString(MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXResourceWriter), this.typeNameConverter));
                }
                xmlTextWriter.WriteEndElement();
            }
            xmlTextWriter.WriteEndElement();
 
            initialized = true;
        }
 
        private XmlWriter Writer {
            get {
                if (!initialized) {
                    InitializeWriter();
                }
                return xmlTextWriter;
            }
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddAlias"]/*' />
        /// <devdoc>
        ///    Adds aliases to the resource file...
        /// </devdoc>
        public virtual void AddAlias(string aliasName, AssemblyName assemblyName) {
           if (assemblyName == null) {
               throw new ArgumentNullException("assemblyName");
           }
 
           if (cachedAliases == null) {
               cachedAliases = new Hashtable();
           }
 
           cachedAliases[assemblyName.FullName] = aliasName; 
       }
 
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddMetadata"]/*' />
        /// <devdoc>
        ///    Adds the given value to the collection of metadata.  These name/value pairs 
        ///    will be emitted to the <metadata> elements in the .resx file.
        /// </devdoc>
        public void AddMetadata(string name, byte[] value) {
            AddDataRow(MetadataStr, name, value);
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddMetadata1"]/*' />
        /// <devdoc>
        ///    Adds the given value to the collection of metadata.  These name/value pairs 
        ///    will be emitted to the <metadata> elements in the .resx file.
        /// </devdoc>
        public void AddMetadata(string name, string value) {
            AddDataRow(MetadataStr, name, value);
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddMetadata2"]/*' />
        /// <devdoc>
        ///    Adds the given value to the collection of metadata.  These name/value pairs 
        ///    will be emitted to the <metadata> elements in the .resx file.
        /// </devdoc>
        public void AddMetadata(string name, object value) {
            AddDataRow(MetadataStr, name, value);
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddResource"]/*' />
        /// <devdoc>
        ///     Adds a blob resource to the resources.
        /// </devdoc>
        // NOTE: Part of IResourceWriter - not protected by class level LinkDemand.
        public void AddResource(string name, byte[] value) {
            AddDataRow(DataStr, name, value);
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddResource1"]/*' />
        /// <devdoc>
        ///     Adds a resource to the resources. If the resource is a string,
        ///     it will be saved that way, otherwise it will be serialized
        ///     and stored as in binary.
        /// </devdoc>
        // NOTE: Part of IResourceWriter - not protected by class level LinkDemand.
        public void AddResource(string name, object value) {
            if (value is ResXDataNode) {
                AddResource((ResXDataNode)value);
            }
            else {
                AddDataRow(DataStr, name, value);
            }
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddResource2"]/*' />
        /// <devdoc>
        ///     Adds a string resource to the resources.
        /// </devdoc>
        // NOTE: Part of IResourceWriter - not protected by class level LinkDemand.
        public void AddResource(string name, string value) {
            AddDataRow(DataStr, name, value);
        }
 
         /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.AddResource3"]/*' />
        /// <devdoc>
        ///     Adds a string resource to the resources.
        /// </devdoc>
        public void AddResource(ResXDataNode node) {
            // we're modifying the node as we're adding it to the resxwriter
            // this is BAD, so we clone it. adding it to a writer doesnt change it
            // we're messing with a copy
            ResXDataNode nodeClone = node.DeepClone();
            
            ResXFileRef fileRef = nodeClone.FileRef;
            string modifiedBasePath = BasePath;
            
            if (!String.IsNullOrEmpty(modifiedBasePath)) {
                if (!(modifiedBasePath.EndsWith("\\")))
                {
                    modifiedBasePath += "\\";
                }
                if (fileRef != null) {
                    fileRef.MakeFilePathRelative(modifiedBasePath);
                }
            }
            DataNodeInfo info = nodeClone.GetDataNodeInfo();
            AddDataRow(DataStr, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment);
        }
 
        /// <devdoc>
        ///     Adds a blob resource to the resources.
        /// </devdoc>
        private void AddDataRow(string elementName, string name, byte[] value) {
            AddDataRow(elementName, name, ToBase64WrappedString(value), TypeNameWithAssembly(typeof(byte[])), null, null);
        }
 
        /// <devdoc>
        ///     Adds a resource to the resources. If the resource is a string,
        ///     it will be saved that way, otherwise it will be serialized
        ///     and stored as in binary.
        /// </devdoc>
        private void AddDataRow(string elementName, string name, object value) {
            Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, "  resx: adding resource " + name);
            if (value is string) {
                AddDataRow(elementName, name, (string)value);
            }
            else if (value is byte[]) {
                AddDataRow(elementName, name, (byte[])value);
            }
            else if(value is ResXFileRef) {
                ResXFileRef fileRef = (ResXFileRef)value;
                ResXDataNode node = new ResXDataNode(name, fileRef, this.typeNameConverter);
                if (fileRef != null) {
                    fileRef.MakeFilePathRelative(BasePath);
                }
                DataNodeInfo info = node.GetDataNodeInfo();
                AddDataRow(elementName, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment);
            } else {
                ResXDataNode node = new ResXDataNode(name, value, this.typeNameConverter);
                DataNodeInfo info = node.GetDataNodeInfo();
                AddDataRow(elementName, info.Name, info.ValueData, info.TypeName, info.MimeType, info.Comment);
            }
        }        
 
        /// <devdoc>
        ///     Adds a string resource to the resources.
        /// </devdoc>
        private void AddDataRow(string elementName, string name, string value) {
            if(value == null) {
                // if it's a null string, set it here as a resxnullref
                AddDataRow(elementName, name, value, MultitargetUtil.GetAssemblyQualifiedName(typeof(ResXNullRef), this.typeNameConverter), null, null);                
            } else {
                AddDataRow(elementName, name, value, null, null, null);
            }
        }
 
        /// <devdoc>
        ///     Adds a new row to the Resources table. This helper is used because
        ///     we want to always late bind to the columns for greater flexibility.
        /// </devdoc>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private void AddDataRow(string elementName, string name, string value, string type, string mimeType, string comment) {
            if (hasBeenSaved)
                throw new InvalidOperationException(SR.GetString(SR.ResXResourceWriterSaved));
            
            string alias = null;
            if (!string.IsNullOrEmpty(type) && elementName == DataStr)
            {
                string assemblyName = GetFullName(type);
                if(string.IsNullOrEmpty(assemblyName)) {
                    try {
                        Type typeObject = Type.GetType(type);
                        if(typeObject == typeof(string)) {
                            type = null;
                        } else if(typeObject != null) {
                            assemblyName = GetFullName(MultitargetUtil.GetAssemblyQualifiedName(typeObject, this.typeNameConverter));
                            alias = GetAliasFromName(new AssemblyName(assemblyName));
                        }
                    } catch {
                    }
                } else {
                    alias = GetAliasFromName(new AssemblyName(GetFullName(type)));
                }
                //AddAssemblyRow(AssemblyStr, alias, GetFullName(type));
            }
            
            Writer.WriteStartElement(elementName); {
                Writer.WriteAttributeString(NameStr, name);
                
                if (!string.IsNullOrEmpty(alias) && !string.IsNullOrEmpty(type) && elementName == DataStr) {
                     // CHANGE: we still output version information. This might have
                    // to change in 3.2
                    string typeName = GetTypeName(type);
                    string typeValue = typeName + ", " + alias;
                    Writer.WriteAttributeString(TypeStr, typeValue);
                }
                else {
                     if (type != null)
                     {
                        Writer.WriteAttributeString(TypeStr, type);
                     }
                }
 
                if (mimeType != null) {
                    Writer.WriteAttributeString(MimeTypeStr, mimeType);
                }
                
                if((type == null && mimeType == null) || (type != null && type.StartsWith("System.Char", StringComparison.Ordinal))) {
                    Writer.WriteAttributeString("xml", "space", null, "preserve");
                }
                
                Writer.WriteStartElement(ValueStr); {
                    if(!string.IsNullOrEmpty(value)) {
                        Writer.WriteString(value);
                    }
                }
                Writer.WriteEndElement();
                if(!string.IsNullOrEmpty(comment)) {
                    Writer.WriteStartElement(CommentStr); {
                        Writer.WriteString(comment);
                    }
                    Writer.WriteEndElement();
                }
            }
            Writer.WriteEndElement();
        }
 
 
        private void AddAssemblyRow(string elementName, string alias, string name)
        {
 
            Writer.WriteStartElement(elementName); {
                if (!string.IsNullOrEmpty(alias)) {
                      Writer.WriteAttributeString(AliasStr, alias);
                }
            
                if (!string.IsNullOrEmpty(name)) {
                    Writer.WriteAttributeString(NameStr, name);
                }
                //Writer.WriteEndElement();
            }
            Writer.WriteEndElement();
        }
 
        private string GetAliasFromName(AssemblyName assemblyName)
        {
             if (cachedAliases == null)
            {
                cachedAliases = new Hashtable();
            }
            string alias =  (string) cachedAliases[assemblyName.FullName]; 
            if (string.IsNullOrEmpty(alias))
            {
                alias =  assemblyName.Name;
                AddAlias(alias, assemblyName);               
                AddAssemblyRow(AssemblyStr, alias, assemblyName.FullName);
            }
            return alias;
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.Close"]/*' />
        /// <devdoc>
        ///     Closes any files or streams locked by the writer.
        /// </devdoc>
        // NOTE: Part of IResourceWriter - not protected by class level LinkDemand.
        public void Close() {
            Dispose();
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.Dispose"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        // NOTE: Part of IDisposable - not protected by class level LinkDemand.
        public virtual void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.Dispose1"]/*' />
        protected virtual void Dispose(bool disposing) {
            if (disposing) {
                if (!hasBeenSaved) {
                    Generate();
                }
                if (xmlTextWriter != null) {
                    xmlTextWriter.Close();
                    xmlTextWriter = null;
                }
                if (stream != null) {
                    stream.Close();
                    stream = null;
                }
                if (textWriter != null) {
                    textWriter.Close();
                    textWriter = null;
                }
            }
        }
 
        private string GetTypeName(string typeName) {
             int indexStart = typeName.IndexOf(",");
             return ((indexStart == -1) ? typeName : typeName.Substring(0, indexStart));
        }
 
 
        private string GetFullName(string typeName) {
             int indexStart = typeName.IndexOf(",");
             if(indexStart == -1)
                return null;
             return typeName.Substring(indexStart + 2);
        }
 
#if UNUSED
        private string GetSimpleName(string typeName) {
             int indexStart = typeName.IndexOf(",");
             int indexEnd =  typeName.IndexOf(",", indexStart + 1);
             return typeName.Substring(indexStart + 2, indexEnd - indexStart  - 3); 
        }
 
        static string StripVersionInformation(string typeName) {
            int indexStart = typeName.IndexOf(" Version=");
            if(indexStart ==-1)
                indexStart = typeName.IndexOf("Version=");
            if(indexStart ==-1)
                indexStart = typeName.IndexOf("version=");
            int indexEnd = -1;
            string result = typeName;
            if(indexStart != -1) {
                // foudn version
                indexEnd = typeName.IndexOf(",", indexStart);
                if(indexEnd != -1) {
                    result = typeName.Remove(indexStart, indexEnd-indexStart+1);
                }
            }
            return result;
            
        }
#endif
        
 
        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 string TypeNameWithAssembly(Type type) {
            // 
 
 
 
 
 
 
 
 
 
 
 
            string result = MultitargetUtil.GetAssemblyQualifiedName(type, this.typeNameConverter);
            return result;
        }
 
        /// <include file='doc\ResXResourceWriter.uex' path='docs/doc[@for="ResXResourceWriter.Generate"]/*' />
        /// <devdoc>
        ///     Writes the resources out to the file or stream.
        /// </devdoc>
        // NOTE: Part of IResourceWriter - not protected by class level LinkDemand.
        public void Generate() {
            if (hasBeenSaved)
                throw new InvalidOperationException(SR.GetString(SR.ResXResourceWriterSaved));
 
            hasBeenSaved = true;
            Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, "writing XML");
 
            Writer.WriteEndElement();
            Writer.Flush();
 
            Debug.WriteLineIf(ResValueProviderSwitch.TraceVerbose, "done");
        }
    }
}