﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UnicodeCharacterEncoder.cs" company="Microsoft Corporation">
//   Copyright (c) 2008, 2009, 2010 All Rights Reserved, Microsoft Corporation
//
//   This source is subject to the Microsoft Permissive License.
//   Please see the License.txt file for more information.
//   All other rights reserved.
//
//   THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
//   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//   PARTICULAR PURPOSE.
//
// </copyright>
// <summary>
//   Provides HTML Encoding methods.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using LinqToTwitter.Common;

namespace LinqToTwitter.Security.Application
{
    /// <summary>
    /// Provides HTML encoding methods.
    /// </summary>
    public static class UnicodeCharacterEncoder
    {
        /// <summary>
        /// A lock object to use when performing safe listing.
        /// </summary>
        private static readonly ReaderWriterLock SyncLock = new ReaderWriterLock();

        /// <summary>
        /// The HTML escaped value for a space, used in attribute encoding.
        /// </summary>
        private static readonly char[] UnicodeSpace = "&#32;".ToCharArray();

        /// <summary>
        /// The Unicode value for an apostrophe, used in attribute encoding.
        /// </summary>
        private static readonly char[] UnicodeApostrophe = "&#39;".ToCharArray();

        /// <summary>
        /// The XML named entity for an apostrophe, used in XML encoding.
        /// </summary>
        private static readonly char[] XmlApostrophe = "&apos;".ToCharArray();
        
        /// <summary>
        /// The current lower code chart settings.
        /// </summary>
        private static LowerCodeCharts currentLowerCodeChartSettings = LowerCodeCharts.None;

        /// <summary>
        /// The current lower middle code chart settings.
        /// </summary>
        private static LowerMidCodeCharts currentLowerMidCodeChartSettings = LowerMidCodeCharts.None;

        /// <summary>
        /// The current middle code chart settings.
        /// </summary>
        private static MidCodeCharts currentMidCodeChartSettings = MidCodeCharts.None;

        /// <summary>
        /// The current upper middle code chart settings.
        /// </summary>
        private static UpperMidCodeCharts currentUpperMidCodeChartSettings = UpperMidCodeCharts.None;

        /// <summary>
        /// The current upper code chart settings.
        /// </summary>
        private static UpperCodeCharts currentUpperCodeChartSettings = UpperCodeCharts.None;

        /// <summary>
        /// The values to output for each character.
        /// </summary>
        private static char[][] characterValues;

        /// <summary>
        /// The values to output for HTML named entities.
        /// </summary>
        private static char[][] namedEntities;

        /// <summary>
        /// Provides method specific encoding of characters.
        /// </summary>
        /// <param name="input">The character to encode</param>
        /// <param name="output">The encoded character, if it has been encoded.</param>
        /// <returns>True if the character has been encoded, otherwise false.</returns>
        private delegate bool MethodSpecificEncoder(char input, out char[] output);

        /// <summary>
        /// Marks characters from the specified languages as safe.
        /// </summary>
        /// <param name="lowerCodeCharts">The combination of lower code charts to use.</param>
        /// <param name="lowerMidCodeCharts">The combination of lower mid code charts to use.</param>
        /// <param name="midCodeCharts">The combination of mid code charts to use.</param>
        /// <param name="upperMidCodeCharts">The combination of upper mid code charts to use.</param>
        /// <param name="upperCodeCharts">The combination of upper code charts to use.</param>
        /// <remarks>The safe list affects all HTML and XML encoding functions.</remarks>
        public static void MarkAsSafe(
            LowerCodeCharts lowerCodeCharts,
            LowerMidCodeCharts lowerMidCodeCharts,
            MidCodeCharts midCodeCharts,
            UpperMidCodeCharts upperMidCodeCharts,
            UpperCodeCharts upperCodeCharts)
        {
            if (lowerCodeCharts == currentLowerCodeChartSettings &&
                lowerMidCodeCharts == currentLowerMidCodeChartSettings &&
                midCodeCharts == currentMidCodeChartSettings &&
                upperMidCodeCharts == currentUpperMidCodeChartSettings &&
                upperCodeCharts == currentUpperCodeChartSettings)
            {
                return;
            }

            SyncLock.EnterWriteLock();
            try
            {
                if (characterValues == null)
                {
                    characterValues = SafeList.Generate(65536, SafeList.HashThenValueGenerator);
                }

                SafeList.PunchUnicodeThrough(
                    ref characterValues,
                    lowerCodeCharts,
                    lowerMidCodeCharts,
                    midCodeCharts,
                    upperMidCodeCharts,
                    upperCodeCharts);

                ApplyHtmlSpecificValues();

                currentLowerCodeChartSettings = lowerCodeCharts;
                currentLowerMidCodeChartSettings = lowerMidCodeCharts;
                currentMidCodeChartSettings = midCodeCharts;
                currentUpperMidCodeChartSettings = upperMidCodeCharts;
                currentUpperCodeChartSettings = upperCodeCharts;
            }
            finally
            {
                SyncLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Encodes input strings for use in XML.
        /// </summary>
        /// <param name="input">String to be encoded</param>
        /// <returns>
        /// Encoded string for use in XML.
        /// </returns>
        internal static string XmlEncode(string input)
        {
            return HtmlEncode(input, false, XmlTweak);
        }

        /// <summary>
        /// Encodes input strings for use in XML.
        /// </summary>
        /// <param name="input">String to be encoded</param>
        /// <returns>
        /// Encoded string for use in XML.
        /// </returns>
        internal static string XmlAttributeEncode(string input)
        {
            return HtmlEncode(input, false, XmlAttributeTweak);
        }

        /// <summary>
        /// Encodes input strings for use in HTML attributes.
        /// </summary>
        /// <param name="input">String to be encoded</param>
        /// <returns>
        /// Encoded string for use in HTML attributes.
        /// </returns>
        internal static string HtmlAttributeEncode(string input)
        {
            return HtmlEncode(input, false, HtmlAttributeTweak);
        }

        /// <summary>
        /// Encodes input strings for use in HTML.
        /// </summary>
        /// <param name="input">String to be encoded</param>
        /// <param name="useNamedEntities">Value indicating if the HTML 4.0 named entities should be used.</param>
        /// <returns>
        /// Encoded string for use in HTML.
        /// </returns>
        internal static string HtmlEncode(string input, bool useNamedEntities)
        {
            return HtmlEncode(input, useNamedEntities, null);
        }

        /// <summary>
        /// HTML Attribute Encoding specific tweaks.
        /// </summary>
        /// <param name="input">The character to potentially encode.</param>
        /// <param name="output">The encoded character, if any.</param>
        /// <returns>True if encoding took place, otherwise false.</returns>
        private static bool HtmlAttributeTweak(char input, out char[] output)
        {
            if (input == '\'')
            {
                output = UnicodeApostrophe;
                return true;
            }

            if (input == ' ')
            {
                output = UnicodeSpace;
                return true;
            }

            output = null;
            return false;
        }

        /// <summary>
        /// XML specific tweaks.
        /// </summary>
        /// <param name="input">The character to potentially encode.</param>
        /// <param name="output">The encoded character, if any.</param>
        /// <returns>True if encoding took place, otherwise false.</returns>
        private static bool XmlTweak(char input, out char[] output)
        {
            if (input == '\'')
            {
                output = XmlApostrophe;
                return true;
            }

            output = null;
            return false;
        }

        /// <summary>
        /// XML Attribute Encoding specific tweaks.
        /// </summary>
        /// <param name="input">The character to potentially encode.</param>
        /// <param name="output">The encoded character, if any.</param>
        /// <returns>True if encoding took place, otherwise false.</returns>
        private static bool XmlAttributeTweak(char input, out char[] output)
        {
            if (input == '\'')
            {
                output = XmlApostrophe;
                return true;
            }

            if (input == ' ')
            {
                output = UnicodeSpace;
                return true;
            }

            output = null;
            return false;
        }

        /// <summary>
        /// Encodes input strings for use in HTML.
        /// </summary>
        /// <param name="input">String to be encoded</param>
        /// <param name="useNamedEntities">Value indicating if the HTML 4.0 named entities should be used.</param>
        /// <param name="encoderTweak">A <see cref="MethodSpecificEncoder"/> function, if needed.</param>
        /// <returns>
        /// Encoded string for use in HTML.
        /// </returns>
        /// <exception cref="InvalidUnicodeValueException">Thrown if a character with an invalid Unicode value is encountered within the input string.</exception>
        /// <exception cref="InvalidSurrogatePairException">Thrown if a high surrogate code point is encoded without a following low surrogate code point, or a 
        /// low surrogate code point is encounter without having been preceded by a high surrogate code point.</exception>
        private static string HtmlEncode(string input, bool useNamedEntities, MethodSpecificEncoder encoderTweak)
        {
            if (string.IsNullOrEmpty(input))
            {
                return input;
            }

            if (characterValues == null)
            {
                InitialiseSafeList();
            }

            if (useNamedEntities && namedEntities == null)
            {
                InitialiseNamedEntityList();
            }

            // Setup a new character array for output.
            char[] inputAsArray = input.ToCharArray();
            int outputLength = 0;
            int inputLength = inputAsArray.Length;
            char[] encodedInput = new char[inputLength * 10]; // Worse case scenario - the longest entity name, thetasym is 10 characters, including the & and ;.

            SyncLock.EnterReadLock();
            try
            {
                for (int i = 0; i < inputLength; i++)
                {
                    char currentCharacter = inputAsArray[i];
                    int currentCodePoint = inputAsArray[i];
                    char[] tweekedValue;

                    // Check for invalid values
                    if (currentCodePoint == 0xFFFE ||
                        currentCodePoint == 0xFFFF)
                    {
                        throw new InvalidUnicodeValueException(currentCodePoint);
                    }
                    else if (currentCharacter.IsHighSurrogate()) 
                    {
                        if (i + 1 == inputLength)
                        {
                            throw new InvalidSurrogatePairException(currentCharacter, '\0');                            
                        }

                        // Now peak ahead and check if the following character is a low surrogate.
                        char nextCharacter = inputAsArray[i + 1];
                        char nextCodePoint = inputAsArray[i + 1];
                        if (!nextCharacter.IsLowSurrogate())
                        {
                            throw new InvalidSurrogatePairException(currentCharacter, nextCharacter);
                        }

                        // Look-ahead was good, so skip.
                        i++;

                        // Calculate the combined code point
                        long combinedCodePoint =
                            0x10000 + ((currentCodePoint - 0xD800) * 0x400) + (nextCodePoint - 0xDC00);
                        char[] encodedCharacter = SafeList.HashThenValueGenerator(combinedCodePoint);
                        encodedInput[outputLength++] = '&';

                        for (int j = 0; j < encodedCharacter.Length; j++)
                        {
                            encodedInput[outputLength++] = encodedCharacter[j];
                        }

                        encodedInput[outputLength++] = ';';
                    }
                    else if (currentCharacter.IsLowSurrogate())
                    {
                        throw new InvalidSurrogatePairException('\0', currentCharacter);    
                    }
                    else if (encoderTweak != null && encoderTweak(currentCharacter, out tweekedValue))
                    {
                        for (int j = 0; j < tweekedValue.Length; j++)
                        {
                            encodedInput[outputLength++] = tweekedValue[j];
                        }
                    }
                    else if (useNamedEntities && namedEntities[currentCodePoint] != null)
                    {
                        char[] encodedCharacter = namedEntities[currentCodePoint];
                        encodedInput[outputLength++] = '&';

                        for (int j = 0; j < encodedCharacter.Length; j++)
                        {
                            encodedInput[outputLength++] = encodedCharacter[j];
                        }

                        encodedInput[outputLength++] = ';';
                    }
                    else if (characterValues[currentCodePoint] != null)
                    {
                        // character needs to be encoded
                        char[] encodedCharacter = characterValues[currentCodePoint];
                        encodedInput[outputLength++] = '&';

                        for (int j = 0; j < encodedCharacter.Length; j++)
                        {
                            encodedInput[outputLength++] = encodedCharacter[j];
                        }

                        encodedInput[outputLength++] = ';';
                    }
                    else
                    {
                        // character does not need encoding
                        encodedInput[outputLength++] = currentCharacter;
                    }
                }
            }
            finally
            {
                SyncLock.ExitReadLock();
            }

            return new string(encodedInput, 0, outputLength);
        }

        /// <summary>
        /// Initializes the HTML safe list.
        /// </summary>
        private static void InitialiseSafeList()
        {
            SyncLock.EnterWriteLock();
            try
            {
                if (characterValues == null)
                {
                    characterValues = SafeList.Generate(0xFFFF, SafeList.HashThenValueGenerator);
                    SafeList.PunchUnicodeThrough(
                        ref characterValues,
                        LowerCodeCharts.Default,
                        LowerMidCodeCharts.None,
                        MidCodeCharts.None,
                        UpperMidCodeCharts.None,
                        UpperCodeCharts.None);
                    ApplyHtmlSpecificValues();
                }
            }
            finally
            {
                SyncLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Applies Html specific values to the internal value list.
        /// </summary>
        private static void ApplyHtmlSpecificValues()
        {
            characterValues['<'] = "lt".ToCharArray();
            characterValues['>'] = "gt".ToCharArray();
            characterValues['&'] = "amp".ToCharArray();
            characterValues['"'] = "quot".ToCharArray();
        }

        /// <summary>
        /// Initialises the HTML named entities list.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage(
            "Microsoft.Maintainability", 
            "CA1505:AvoidUnmaintainableCode", 
            Justification = "Splitting or initialising via lookups has too large a performance increase.")]
        private static void InitialiseNamedEntityList()
        {
            namedEntities = new char[65536][];
            namedEntities[160] = "nbsp".ToCharArray();
            namedEntities[161] = "iexcl".ToCharArray();
            namedEntities[162] = "cent".ToCharArray();
            namedEntities[163] = "pound".ToCharArray();
            namedEntities[164] = "curren".ToCharArray();
            namedEntities[165] = "yen".ToCharArray();
            namedEntities[166] = "brvbar".ToCharArray();
            namedEntities[167] = "sect".ToCharArray();
            namedEntities[168] = "uml".ToCharArray();
            namedEntities[169] = "copy".ToCharArray();
            namedEntities[170] = "ordf".ToCharArray();
            namedEntities[171] = "laquo".ToCharArray();
            namedEntities[172] = "not".ToCharArray();
            namedEntities[173] = "shy".ToCharArray();
            namedEntities[174] = "reg".ToCharArray();
            namedEntities[175] = "macr".ToCharArray();
            namedEntities[176] = "deg".ToCharArray();
            namedEntities[177] = "plusmn".ToCharArray();
            namedEntities[178] = "sup2".ToCharArray();
            namedEntities[179] = "sup3".ToCharArray();
            namedEntities[180] = "acute".ToCharArray();
            namedEntities[181] = "micro".ToCharArray();
            namedEntities[182] = "para".ToCharArray();
            namedEntities[183] = "middot".ToCharArray();
            namedEntities[184] = "cedil".ToCharArray();
            namedEntities[185] = "sup1".ToCharArray();
            namedEntities[186] = "ordm".ToCharArray();
            namedEntities[187] = "raquo".ToCharArray();
            namedEntities[188] = "frac14".ToCharArray();
            namedEntities[189] = "frac12".ToCharArray();
            namedEntities[190] = "frac34".ToCharArray();
            namedEntities[191] = "iquest".ToCharArray();
            namedEntities[192] = "Agrave".ToCharArray();
            namedEntities[193] = "Aacute".ToCharArray();
            namedEntities[194] = "Acirc".ToCharArray();
            namedEntities[195] = "Atilde".ToCharArray();
            namedEntities[196] = "Auml".ToCharArray();
            namedEntities[197] = "Aring".ToCharArray();
            namedEntities[198] = "AElig".ToCharArray();
            namedEntities[199] = "Ccedil".ToCharArray();
            namedEntities[200] = "Egrave".ToCharArray();
            namedEntities[201] = "Eacute".ToCharArray();
            namedEntities[202] = "Ecirc".ToCharArray();
            namedEntities[203] = "Euml".ToCharArray();
            namedEntities[204] = "Igrave".ToCharArray();
            namedEntities[205] = "Iacute".ToCharArray();
            namedEntities[206] = "Icirc".ToCharArray();
            namedEntities[207] = "Iuml".ToCharArray();
            namedEntities[208] = "ETH".ToCharArray();
            namedEntities[209] = "Ntilde".ToCharArray();
            namedEntities[210] = "Ograve".ToCharArray();
            namedEntities[211] = "Oacute".ToCharArray();
            namedEntities[212] = "Ocirc".ToCharArray();
            namedEntities[213] = "Otilde".ToCharArray();
            namedEntities[214] = "Ouml".ToCharArray();
            namedEntities[215] = "times".ToCharArray();
            namedEntities[216] = "Oslash".ToCharArray();
            namedEntities[217] = "Ugrave".ToCharArray();
            namedEntities[218] = "Uacute".ToCharArray();
            namedEntities[219] = "Ucirc".ToCharArray();
            namedEntities[220] = "Uuml".ToCharArray();
            namedEntities[221] = "Yacute".ToCharArray();
            namedEntities[222] = "THORN".ToCharArray();
            namedEntities[223] = "szlig".ToCharArray();
            namedEntities[224] = "agrave".ToCharArray();
            namedEntities[225] = "aacute".ToCharArray();
            namedEntities[226] = "acirc".ToCharArray();
            namedEntities[227] = "atilde".ToCharArray();
            namedEntities[228] = "auml".ToCharArray();
            namedEntities[229] = "aring".ToCharArray();
            namedEntities[230] = "aelig".ToCharArray();
            namedEntities[231] = "ccedil".ToCharArray();
            namedEntities[232] = "egrave".ToCharArray();
            namedEntities[233] = "eacute".ToCharArray();
            namedEntities[234] = "ecirc".ToCharArray();
            namedEntities[235] = "euml".ToCharArray();
            namedEntities[236] = "igrave".ToCharArray();
            namedEntities[237] = "iacute".ToCharArray();
            namedEntities[238] = "icirc".ToCharArray();
            namedEntities[239] = "iuml".ToCharArray();
            namedEntities[240] = "eth".ToCharArray();
            namedEntities[241] = "ntilde".ToCharArray();
            namedEntities[242] = "ograve".ToCharArray();
            namedEntities[243] = "oacute".ToCharArray();
            namedEntities[244] = "ocirc".ToCharArray();
            namedEntities[245] = "otilde".ToCharArray();
            namedEntities[246] = "ouml".ToCharArray();
            namedEntities[247] = "divide".ToCharArray();
            namedEntities[248] = "oslash".ToCharArray();
            namedEntities[249] = "ugrave".ToCharArray();
            namedEntities[250] = "uacute".ToCharArray();
            namedEntities[251] = "ucirc".ToCharArray();
            namedEntities[252] = "uuml".ToCharArray();
            namedEntities[253] = "yacute".ToCharArray();
            namedEntities[254] = "thorn".ToCharArray();
            namedEntities[255] = "yuml".ToCharArray();
                    
            namedEntities[338] = "OElig".ToCharArray();
            namedEntities[339] = "oelig".ToCharArray();
            namedEntities[352] = "Scaron".ToCharArray();
            namedEntities[353] = "scaron".ToCharArray();
            namedEntities[376] = "Yuml".ToCharArray();
            namedEntities[402] = "fnof".ToCharArray();
            namedEntities[710] = "circ".ToCharArray();
            namedEntities[732] = "tilde".ToCharArray();

            namedEntities[913] = "Alpha".ToCharArray();
            namedEntities[914] = "Beta".ToCharArray();
            namedEntities[915] = "Gamma".ToCharArray();
            namedEntities[916] = "Delta".ToCharArray();
            namedEntities[917] = "Epsilon".ToCharArray();
            namedEntities[918] = "Zeta".ToCharArray();
            namedEntities[919] = "Eta".ToCharArray();
            namedEntities[920] = "Theta".ToCharArray();
            namedEntities[921] = "Iota".ToCharArray();
            namedEntities[922] = "Kappa".ToCharArray();
            namedEntities[923] = "Lambda".ToCharArray();
            namedEntities[924] = "Mu".ToCharArray();
            namedEntities[925] = "Nu".ToCharArray();
            namedEntities[926] = "Xi".ToCharArray();
            namedEntities[927] = "Omicron".ToCharArray();
            namedEntities[928] = "Pi".ToCharArray();
            namedEntities[929] = "Rho".ToCharArray();
            namedEntities[931] = "Sigma".ToCharArray();
            namedEntities[932] = "Tau".ToCharArray();
            namedEntities[933] = "Upsilon".ToCharArray();
            namedEntities[934] = "Phi".ToCharArray();
            namedEntities[935] = "Chi".ToCharArray();
            namedEntities[936] = "Psi".ToCharArray();
            namedEntities[937] = "Omega".ToCharArray();
            namedEntities[945] = "alpha".ToCharArray();
            namedEntities[946] = "beta".ToCharArray();
            namedEntities[947] = "gamma".ToCharArray();
            namedEntities[948] = "delta".ToCharArray();
            namedEntities[949] = "epsilon".ToCharArray();
            namedEntities[950] = "zeta".ToCharArray();
            namedEntities[951] = "eta".ToCharArray();
            namedEntities[952] = "theta".ToCharArray();
            namedEntities[953] = "iota".ToCharArray();
            namedEntities[954] = "kappa".ToCharArray();
            namedEntities[955] = "lambda".ToCharArray();
            namedEntities[956] = "mu".ToCharArray();
            namedEntities[957] = "nu".ToCharArray();
            namedEntities[958] = "xi".ToCharArray();
            namedEntities[959] = "omicron".ToCharArray();
            namedEntities[960] = "pi".ToCharArray();
            namedEntities[961] = "rho".ToCharArray();
            namedEntities[962] = "sigmaf".ToCharArray();
            namedEntities[963] = "sigma".ToCharArray();
            namedEntities[964] = "tau".ToCharArray();
            namedEntities[965] = "upsilon".ToCharArray();
            namedEntities[966] = "phi".ToCharArray();
            namedEntities[967] = "chi".ToCharArray();
            namedEntities[968] = "psi".ToCharArray();
            namedEntities[969] = "omega".ToCharArray();
            namedEntities[977] = "thetasym".ToCharArray();
            namedEntities[978] = "upsih".ToCharArray();
            namedEntities[982] = "piv".ToCharArray();
            
            namedEntities[0x2002] = "ensp".ToCharArray();
            namedEntities[0x2003] = "emsp".ToCharArray();
            namedEntities[0x2009] = "thinsp".ToCharArray();
            namedEntities[0x200C] = "zwnj".ToCharArray();
            namedEntities[0x200D] = "zwj".ToCharArray();
            namedEntities[0x200E] = "lrm".ToCharArray();
            namedEntities[0x200F] = "rlm".ToCharArray();
            namedEntities[0x2013] = "ndash".ToCharArray();
            namedEntities[0x2014] = "mdash".ToCharArray();
            namedEntities[0x2018] = "lsquo".ToCharArray();
            namedEntities[0x2019] = "rsquo".ToCharArray();
            namedEntities[0x201A] = "sbquo".ToCharArray();
            namedEntities[0x201C] = "ldquo".ToCharArray();
            namedEntities[0x201D] = "rdquo".ToCharArray();
            namedEntities[0x201E] = "bdquo".ToCharArray();
            namedEntities[0x2020] = "dagger".ToCharArray();
            namedEntities[0x2021] = "Dagger".ToCharArray();
            namedEntities[0x2022] = "bull".ToCharArray();
            namedEntities[0x2026] = "hellip".ToCharArray();
            namedEntities[0x2030] = "permil".ToCharArray();
            namedEntities[0x2032] = "prime".ToCharArray();
            namedEntities[0x2033] = "Prime".ToCharArray();
            namedEntities[0x2039] = "lsaquo".ToCharArray();
            namedEntities[0x203A] = "rsaquo".ToCharArray();
            namedEntities[0x203E] = "oline".ToCharArray();
            namedEntities[0x2044] = "frasl".ToCharArray();
            namedEntities[0x20AC] = "euro".ToCharArray();
            namedEntities[0x2111] = "image".ToCharArray();
            namedEntities[0x2118] = "weierp".ToCharArray();
            namedEntities[0x211C] = "real".ToCharArray();
            namedEntities[0x2122] = "trade".ToCharArray();
            namedEntities[0x2135] = "alefsym".ToCharArray();
            namedEntities[0x2190] = "larr".ToCharArray();
            namedEntities[0x2191] = "uarr".ToCharArray();
            namedEntities[0x2192] = "rarr".ToCharArray();
            namedEntities[0x2193] = "darr".ToCharArray();
            namedEntities[0x2194] = "harr".ToCharArray();
            namedEntities[0x21B5] = "crarr".ToCharArray();
            namedEntities[0x21D0] = "lArr".ToCharArray();
            namedEntities[0x21D1] = "uArr".ToCharArray();
            namedEntities[0x21D2] = "rArr".ToCharArray();
            namedEntities[0x21D3] = "dArr".ToCharArray();
            namedEntities[0x21D4] = "hArr".ToCharArray();
            namedEntities[0x2200] = "forall".ToCharArray();
            namedEntities[0x2202] = "part".ToCharArray();
            namedEntities[0x2203] = "exist".ToCharArray();
            namedEntities[0x2205] = "empty".ToCharArray();
            namedEntities[0x2207] = "nabla".ToCharArray();
            namedEntities[0x2208] = "isin".ToCharArray();
            namedEntities[0x2209] = "notin".ToCharArray();
            namedEntities[0x220B] = "ni".ToCharArray();
            namedEntities[0x220F] = "prod".ToCharArray();
            namedEntities[0x2211] = "sum".ToCharArray();
            namedEntities[0x2212] = "minus".ToCharArray();
            namedEntities[0x2217] = "lowast".ToCharArray();
            namedEntities[0x221A] = "radic".ToCharArray();
            namedEntities[0x221D] = "prop".ToCharArray();
            namedEntities[0x221E] = "infin".ToCharArray();
            namedEntities[0x2220] = "ang".ToCharArray();
            namedEntities[0x2227] = "and".ToCharArray();
            namedEntities[0x2228] = "or".ToCharArray();
            namedEntities[0x2229] = "cap".ToCharArray();
            namedEntities[0x222A] = "cup".ToCharArray();
            namedEntities[0x222B] = "int".ToCharArray();
            namedEntities[0x2234] = "there4".ToCharArray();
            namedEntities[0x223C] = "sim".ToCharArray();
            namedEntities[0x2245] = "cong".ToCharArray();
            namedEntities[0x2248] = "asymp".ToCharArray();
            namedEntities[0x2260] = "ne".ToCharArray();
            namedEntities[0x2261] = "equiv".ToCharArray();
            namedEntities[0x2264] = "le".ToCharArray();
            namedEntities[0x2265] = "ge".ToCharArray();
            namedEntities[0x2282] = "sub".ToCharArray();
            namedEntities[0x2283] = "sup".ToCharArray();
            namedEntities[0x2284] = "nsub".ToCharArray();
            namedEntities[0x2286] = "sube".ToCharArray();
            namedEntities[0x2287] = "supe".ToCharArray();
            namedEntities[0x2295] = "oplus".ToCharArray();
            namedEntities[0x2297] = "otimes".ToCharArray();
            namedEntities[0x22A5] = "perp".ToCharArray();
            namedEntities[0x22C5] = "sdot".ToCharArray();
            namedEntities[0x2308] = "lceil".ToCharArray();
            namedEntities[0x2309] = "rceil".ToCharArray();
            namedEntities[0x230A] = "lfloor".ToCharArray();
            namedEntities[0x230B] = "rfloor".ToCharArray();
            namedEntities[0x2329] = "lang".ToCharArray();
            namedEntities[0x232A] = "rang".ToCharArray();
            namedEntities[0x25CA] = "loz".ToCharArray();
            namedEntities[0x2660] = "spades".ToCharArray();
            namedEntities[0x2663] = "clubs".ToCharArray();
            namedEntities[0x2665] = "hearts".ToCharArray();
            namedEntities[0x2666] = "diams".ToCharArray();
        }
    }
}
