/*
 * Copyright 2007 Yusuke Yamamoto
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package twitter4j;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

final class HTMLEntity {

    static String escape(String original) {
        StringBuilder buf = new StringBuilder(original);
        escape(buf);
        return buf.toString();
    }

    static void escape(StringBuilder original) {
        int index = 0;
        String escaped;
        while (index < original.length()) {
            escaped = entityEscapeMap.get(original.substring(index, index + 1));
            if (escaped != null) {
                original.replace(index, index + 1, escaped);
                index += escaped.length();
            } else {
                index++;
            }
        }
    }

    static String unescape(String original) {
        String returnValue = null;
        if (original != null) {
            StringBuilder buf = new StringBuilder(original);
            unescape(buf);
            returnValue = buf.toString();
        }
        return returnValue;
    }

    static void unescape(StringBuilder original) {
        int index = 0;
        int semicolonIndex;
        String escaped;
        String entity;
        while (index < original.length()) {
            index = original.indexOf("&", index);
            if (-1 == index) {
                break;
            }
            semicolonIndex = original.indexOf(";", index);
            if (-1 != semicolonIndex) {
                escaped = original.substring(index, semicolonIndex + 1);
                entity = escapeEntityMap.get(escaped);
                if (entity != null) {
                    original.replace(index, semicolonIndex + 1, entity);
                }
                index++;
            } else {
                break;
            }
        }
    }

    /**
	 * @author Yusuke Yamamoto - yusuke at mac.com
	 * @author Philip Hachey - philip dot hachey at gmail dot com
	 */	
    static String unescapeAndSlideEntityIncdices(String text, UserMentionEntity[] userMentionEntities,
                                                 URLEntity[] urlEntities, HashtagEntity[] hashtagEntities,
                                                 MediaEntity[] mediaEntities) {
        
        int entityIndexesLength = 0;
        entityIndexesLength += userMentionEntities == null ? 0 : userMentionEntities.length;
        entityIndexesLength += urlEntities == null ? 0 : urlEntities.length;
        entityIndexesLength += hashtagEntities == null ? 0 : hashtagEntities.length;
        entityIndexesLength += mediaEntities == null ? 0 : mediaEntities.length;

        EntityIndex[] entityIndexes = new EntityIndex[entityIndexesLength];
        int copyStartIndex = 0;
        if (userMentionEntities != null) {
            System.arraycopy(userMentionEntities, 0, entityIndexes, copyStartIndex, userMentionEntities.length);
            copyStartIndex += userMentionEntities.length;
        }
        
        if (urlEntities != null) {
            System.arraycopy(urlEntities, 0, entityIndexes, copyStartIndex, urlEntities.length);
            copyStartIndex += urlEntities.length;
        }
        
        if (hashtagEntities != null) {
            System.arraycopy(hashtagEntities, 0, entityIndexes, copyStartIndex, hashtagEntities.length);
            copyStartIndex += hashtagEntities.length;
        }
        
        if (mediaEntities != null) {
            System.arraycopy(mediaEntities, 0, entityIndexes, copyStartIndex, mediaEntities.length);
        }

        Arrays.sort(entityIndexes);
        boolean handlingStart = true;
        int entityIndex = 0;

        int delta = 0;
        int semicolonIndex;
        String escaped;
        String entity;
        StringBuilder unescaped = new StringBuilder(text.length());
        
		/*
		 * Slide indices of twitter entities not only when replacing character
		 * entity references but also adjust the twitter code point based
		 * indexes with Java standard character indexes. See: HTMLEntityTest.
		 * testUnescapeAndSlideEntityIncdicesWithSurrogateCodePoints
		 */
		int textCodePointLength = text.codePointCount(0, text.length());
		int codePoint;
		for (int index = 0, twitterIndex = 0; index < text.length(); index +=
				Character.charCount(codePoint), twitterIndex++) {
			codePoint = text.codePointAt(index);
			if (codePoint == '&') {
				semicolonIndex = text.indexOf(";", index);
				if (-1 != semicolonIndex) {
					escaped = text.substring(index, semicolonIndex + 1);
					entity = escapeEntityMap.get(escaped);
					if (entity != null) {
						unescaped.append(entity);
						index = semicolonIndex;
						twitterIndex = text.codePointCount(0, semicolonIndex);
						delta = 1 - escaped.length();
					} else {
						unescaped.appendCodePoint(codePoint);
					}
				} else {
					unescaped.appendCodePoint(codePoint);
				}
			} else {
				unescaped.appendCodePoint(codePoint);
			}
			if (entityIndex < entityIndexes.length) {
				if (handlingStart) {
					if (entityIndexes[entityIndex].getStart() == (delta + twitterIndex)) {
						entityIndexes[entityIndex]
								.setStart(unescaped.length() - Character.charCount(text.codePointAt(index)));
						handlingStart = false;
					}
				} else if (entityIndexes[entityIndex].getEnd() == (delta + twitterIndex)) {
					entityIndexes[entityIndex]
							.setEnd(unescaped.length() - Character.charCount(text.codePointAt(index)));
					entityIndex++;
					handlingStart = true;
				}
			}
			delta = 0;
		}
		if (entityIndex < entityIndexes.length) {
			if (entityIndexes[entityIndex].getEnd() == textCodePointLength) {
				entityIndexes[entityIndex].setEnd(unescaped.length());
			}
		}

        return unescaped.toString();
    }

    private static final Map<String, String> entityEscapeMap = new HashMap<String, String>();
    private static final Map<String, String> escapeEntityMap = new HashMap<String, String>();

    static {
        String[][] entities =
                {{"&nbsp;", "&#160;"/* no-break space = non-breaking space */, "\u00A0"}
                        , {"&iexcl;", "&#161;"/* inverted exclamation mark */, "\u00A1"}
                        , {"&cent;", "&#162;"/* cent sign */, "\u00A2"}
                        , {"&pound;", "&#163;"/* pound sign */, "\u00A3"}
                        , {"&curren;", "&#164;"/* currency sign */, "\u00A4"}
                        , {"&yen;", "&#165;"/* yen sign = yuan sign */, "\u00A5"}
                        , {"&brvbar;", "&#166;"/* broken bar = broken vertical bar */, "\u00A6"}
                        , {"&sect;", "&#167;"/* section sign */, "\u00A7"}
                        , {"&uml;", "&#168;"/* diaeresis = spacing diaeresis */, "\u00A8"}
                        , {"&copy;", "&#169;"/* copyright sign */, "\u00A9"}
                        , {"&ordf;", "&#170;"/* feminine ordinal indicator */, "\u00AA"}
                        , {"&laquo;", "&#171;"/* left-pointing double angle quotation mark = left pointing guillemet */, "\u00AB"}
                        , {"&not;", "&#172;"/* not sign = discretionary hyphen */, "\u00AC"}
                        , {"&shy;", "&#173;"/* soft hyphen = discretionary hyphen */, "\u00AD"}
                        , {"&reg;", "&#174;"/* registered sign = registered trade mark sign */, "\u00AE"}
                        , {"&macr;", "&#175;"/* macron = spacing macron = overline = APL overbar */, "\u00AF"}
                        , {"&deg;", "&#176;"/* degree sign */, "\u00B0"}
                        , {"&plusmn;", "&#177;"/* plus-minus sign = plus-or-minus sign */, "\u00B1"}
                        , {"&sup2;", "&#178;"/* superscript two = superscript digit two = squared */, "\u00B2"}
                        , {"&sup3;", "&#179;"/* superscript three = superscript digit three = cubed */, "\u00B3"}
                        , {"&acute;", "&#180;"/* acute accent = spacing acute */, "\u00B4"}
                        , {"&micro;", "&#181;"/* micro sign */, "\u00B5"}
                        , {"&para;", "&#182;"/* pilcrow sign = paragraph sign */, "\u00B6"}
                        , {"&middot;", "&#183;"/* middle dot = Georgian comma = Greek middle dot */, "\u00B7"}
                        , {"&cedil;", "&#184;"/* cedilla = spacing cedilla */, "\u00B8"}
                        , {"&sup1;", "&#185;"/* superscript one = superscript digit one */, "\u00B9"}
                        , {"&ordm;", "&#186;"/* masculine ordinal indicator */, "\u00BA"}
                        , {"&raquo;", "&#187;"/* right-pointing double angle quotation mark = right pointing guillemet */, "\u00BB"}
                        , {"&frac14;", "&#188;"/* vulgar fraction one quarter = fraction one quarter */, "\u00BC"}
                        , {"&frac12;", "&#189;"/* vulgar fraction one half = fraction one half */, "\u00BD"}
                        , {"&frac34;", "&#190;"/* vulgar fraction three quarters = fraction three quarters */, "\u00BE"}
                        , {"&iquest;", "&#191;"/* inverted question mark = turned question mark */, "\u00BF"}
                        , {"&Agrave;", "&#192;"/* latin capital letter A with grave = latin capital letter A grave */, "\u00C0"}
                        , {"&Aacute;", "&#193;"/* latin capital letter A with acute */, "\u00C1"}
                        , {"&Acirc;", "&#194;"/* latin capital letter A with circumflex */, "\u00C2"}
                        , {"&Atilde;", "&#195;"/* latin capital letter A with tilde */, "\u00C3"}
                        , {"&Auml;", "&#196;"/* latin capital letter A with diaeresis */, "\u00C4"}
                        , {"&Aring;", "&#197;"/* latin capital letter A with ring above = latin capital letter A ring */, "\u00C5"}
                        , {"&AElig;", "&#198;"/* latin capital letter AE = latin capital ligature AE */, "\u00C6"}
                        , {"&Ccedil;", "&#199;"/* latin capital letter C with cedilla */, "\u00C7"}
                        , {"&Egrave;", "&#200;"/* latin capital letter E with grave */, "\u00C8"}
                        , {"&Eacute;", "&#201;"/* latin capital letter E with acute */, "\u00C9"}
                        , {"&Ecirc;", "&#202;"/* latin capital letter E with circumflex */, "\u00CA"}
                        , {"&Euml;", "&#203;"/* latin capital letter E with diaeresis */, "\u00CB"}
                        , {"&Igrave;", "&#204;"/* latin capital letter I with grave */, "\u00CC"}
                        , {"&Iacute;", "&#205;"/* latin capital letter I with acute */, "\u00CD"}
                        , {"&Icirc;", "&#206;"/* latin capital letter I with circumflex */, "\u00CE"}
                        , {"&Iuml;", "&#207;"/* latin capital letter I with diaeresis */, "\u00CF"}
                        , {"&ETH;", "&#208;"/* latin capital letter ETH */, "\u00D0"}
                        , {"&Ntilde;", "&#209;"/* latin capital letter N with tilde */, "\u00D1"}
                        , {"&Ograve;", "&#210;"/* latin capital letter O with grave */, "\u00D2"}
                        , {"&Oacute;", "&#211;"/* latin capital letter O with acute */, "\u00D3"}
                        , {"&Ocirc;", "&#212;"/* latin capital letter O with circumflex */, "\u00D4"}
                        , {"&Otilde;", "&#213;"/* latin capital letter O with tilde */, "\u00D5"}
                        , {"&Ouml;", "&#214;"/* latin capital letter O with diaeresis */, "\u00D6"}
                        , {"&times;", "&#215;"/* multiplication sign */, "\u00D7"}
                        , {"&Oslash;", "&#216;"/* latin capital letter O with stroke = latin capital letter O slash */, "\u00D8"}
                        , {"&Ugrave;", "&#217;"/* latin capital letter U with grave */, "\u00D9"}
                        , {"&Uacute;", "&#218;"/* latin capital letter U with acute */, "\u00DA"}
                        , {"&Ucirc;", "&#219;"/* latin capital letter U with circumflex */, "\u00DB"}
                        , {"&Uuml;", "&#220;"/* latin capital letter U with diaeresis */, "\u00DC"}
                        , {"&Yacute;", "&#221;"/* latin capital letter Y with acute */, "\u00DD"}
                        , {"&THORN;", "&#222;"/* latin capital letter THORN */, "\u00DE"}
                        , {"&szlig;", "&#223;"/* latin small letter sharp s = ess-zed */, "\u00DF"}
                        , {"&agrave;", "&#224;"/* latin small letter a with grave = latin small letter a grave */, "\u00E0"}
                        , {"&aacute;", "&#225;"/* latin small letter a with acute */, "\u00E1"}
                        , {"&acirc;", "&#226;"/* latin small letter a with circumflex */, "\u00E2"}
                        , {"&atilde;", "&#227;"/* latin small letter a with tilde */, "\u00E3"}
                        , {"&auml;", "&#228;"/* latin small letter a with diaeresis */, "\u00E4"}
                        , {"&aring;", "&#229;"/* latin small letter a with ring above = latin small letter a ring */, "\u00E5"}
                        , {"&aelig;", "&#230;"/* latin small letter ae = latin small ligature ae */, "\u00E6"}
                        , {"&ccedil;", "&#231;"/* latin small letter c with cedilla */, "\u00E7"}
                        , {"&egrave;", "&#232;"/* latin small letter e with grave */, "\u00E8"}
                        , {"&eacute;", "&#233;"/* latin small letter e with acute */, "\u00E9"}
                        , {"&ecirc;", "&#234;"/* latin small letter e with circumflex */, "\u00EA"}
                        , {"&euml;", "&#235;"/* latin small letter e with diaeresis */, "\u00EB"}
                        , {"&igrave;", "&#236;"/* latin small letter i with grave */, "\u00EC"}
                        , {"&iacute;", "&#237;"/* latin small letter i with acute */, "\u00ED"}
                        , {"&icirc;", "&#238;"/* latin small letter i with circumflex */, "\u00EE"}
                        , {"&iuml;", "&#239;"/* latin small letter i with diaeresis */, "\u00EF"}
                        , {"&eth;", "&#240;"/* latin small letter eth */, "\u00F0"}
                        , {"&ntilde;", "&#241;"/* latin small letter n with tilde */, "\u00F1"}
                        , {"&ograve;", "&#242;"/* latin small letter o with grave */, "\u00F2"}
                        , {"&oacute;", "&#243;"/* latin small letter o with acute */, "\u00F3"}
                        , {"&ocirc;", "&#244;"/* latin small letter o with circumflex */, "\u00F4"}
                        , {"&otilde;", "&#245;"/* latin small letter o with tilde */, "\u00F5"}
                        , {"&ouml;", "&#246;"/* latin small letter o with diaeresis */, "\u00F6"}
                        , {"&divide;", "&#247;"/* division sign */, "\u00F7"}
                        , {"&oslash;", "&#248;"/* latin small letter o with stroke = latin small letter o slash */, "\u00F8"}
                        , {"&ugrave;", "&#249;"/* latin small letter u with grave */, "\u00F9"}
                        , {"&uacute;", "&#250;"/* latin small letter u with acute */, "\u00FA"}
                        , {"&ucirc;", "&#251;"/* latin small letter u with circumflex */, "\u00FB"}
                        , {"&uuml;", "&#252;"/* latin small letter u with diaeresis */, "\u00FC"}
                        , {"&yacute;", "&#253;"/* latin small letter y with acute */, "\u00FD"}
                        , {"&thorn;", "&#254;"/* latin small letter thorn with */, "\u00FE"}
                        , {"&yuml;", "&#255;"/* latin small letter y with diaeresis */, "\u00FF"}
                        , {"&fnof;", "&#402;"/* latin small f with hook = function = florin */, "\u0192"}
/* Greek */
                        , {"&Alpha;", "&#913;"/* greek capital letter alpha */, "\u0391"}
                        , {"&Beta;", "&#914;"/* greek capital letter beta */, "\u0392"}
                        , {"&Gamma;", "&#915;"/* greek capital letter gamma */, "\u0393"}
                        , {"&Delta;", "&#916;"/* greek capital letter delta */, "\u0394"}
                        , {"&Epsilon;", "&#917;"/* greek capital letter epsilon */, "\u0395"}
                        , {"&Zeta;", "&#918;"/* greek capital letter zeta */, "\u0396"}
                        , {"&Eta;", "&#919;"/* greek capital letter eta */, "\u0397"}
                        , {"&Theta;", "&#920;"/* greek capital letter theta */, "\u0398"}
                        , {"&Iota;", "&#921;"/* greek capital letter iota */, "\u0399"}
                        , {"&Kappa;", "&#922;"/* greek capital letter kappa */, "\u039A"}
                        , {"&Lambda;", "&#923;"/* greek capital letter lambda */, "\u039B"}
                        , {"&Mu;", "&#924;"/* greek capital letter mu */, "\u039C"}
                        , {"&Nu;", "&#925;"/* greek capital letter nu */, "\u039D"}
                        , {"&Xi;", "&#926;"/* greek capital letter xi */, "\u039E"}
                        , {"&Omicron;", "&#927;"/* greek capital letter omicron */, "\u039F"}
                        , {"&Pi;", "&#928;"/* greek capital letter pi */, "\u03A0"}
                        , {"&Rho;", "&#929;"/* greek capital letter rho */, "\u03A1"}
/* there is no Sigmaf and no \u03A2 */
                        , {"&Sigma;", "&#931;"/* greek capital letter sigma */, "\u03A3"}
                        , {"&Tau;", "&#932;"/* greek capital letter tau */, "\u03A4"}
                        , {"&Upsilon;", "&#933;"/* greek capital letter upsilon */, "\u03A5"}
                        , {"&Phi;", "&#934;"/* greek capital letter phi */, "\u03A6"}
                        , {"&Chi;", "&#935;"/* greek capital letter chi */, "\u03A7"}
                        , {"&Psi;", "&#936;"/* greek capital letter psi */, "\u03A8"}
                        , {"&Omega;", "&#937;"/* greek capital letter omega */, "\u03A9"}
                        , {"&alpha;", "&#945;"/* greek small letter alpha */, "\u03B1"}
                        , {"&beta;", "&#946;"/* greek small letter beta */, "\u03B2"}
                        , {"&gamma;", "&#947;"/* greek small letter gamma */, "\u03B3"}
                        , {"&delta;", "&#948;"/* greek small letter delta */, "\u03B4"}
                        , {"&epsilon;", "&#949;"/* greek small letter epsilon */, "\u03B5"}
                        , {"&zeta;", "&#950;"/* greek small letter zeta */, "\u03B6"}
                        , {"&eta;", "&#951;"/* greek small letter eta */, "\u03B7"}
                        , {"&theta;", "&#952;"/* greek small letter theta */, "\u03B8"}
                        , {"&iota;", "&#953;"/* greek small letter iota */, "\u03B9"}
                        , {"&kappa;", "&#954;"/* greek small letter kappa */, "\u03BA"}
                        , {"&lambda;", "&#955;"/* greek small letter lambda */, "\u03BB"}
                        , {"&mu;", "&#956;"/* greek small letter mu */, "\u03BC"}
                        , {"&nu;", "&#957;"/* greek small letter nu */, "\u03BD"}
                        , {"&xi;", "&#958;"/* greek small letter xi */, "\u03BE"}
                        , {"&omicron;", "&#959;"/* greek small letter omicron */, "\u03BF"}
                        , {"&pi;", "&#960;"/* greek small letter pi */, "\u03C0"}
                        , {"&rho;", "&#961;"/* greek small letter rho */, "\u03C1"}
                        , {"&sigmaf;", "&#962;"/* greek small letter final sigma */, "\u03C2"}
                        , {"&sigma;", "&#963;"/* greek small letter sigma */, "\u03C3"}
                        , {"&tau;", "&#964;"/* greek small letter tau */, "\u03C4"}
                        , {"&upsilon;", "&#965;"/* greek small letter upsilon */, "\u03C5"}
                        , {"&phi;", "&#966;"/* greek small letter phi */, "\u03C6"}
                        , {"&chi;", "&#967;"/* greek small letter chi */, "\u03C7"}
                        , {"&psi;", "&#968;"/* greek small letter psi */, "\u03C8"}
                        , {"&omega;", "&#969;"/* greek small letter omega */, "\u03C9"}
                        , {"&thetasym;", "&#977;"/* greek small letter theta symbol */, "\u03D1"}
                        , {"&upsih;", "&#978;"/* greek upsilon with hook symbol */, "\u03D2"}
                        , {"&piv;", "&#982;"/* greek pi symbol */, "\u03D6"}
/* General Punctuation */
                        , {"&bull;", "&#8226;"/* bullet = black small circle */, "\u2022"}
/* bullet is NOT the same as bullet operator  ,"\u2219*/
                        , {"&hellip;", "&#8230;"/* horizontal ellipsis = three dot leader */, "\u2026"}
                        , {"&prime;", "&#8242;"/* prime = minutes = feet */, "\u2032"}
                        , {"&Prime;", "&#8243;"/* double prime = seconds = inches */, "\u2033"}
                        , {"&oline;", "&#8254;"/* overline = spacing overscore */, "\u203E"}
                        , {"&frasl;", "&#8260;"/* fraction slash */, "\u2044"}
/* Letterlike Symbols */
                        , {"&weierp;", "&#8472;"/* script capital P = power set = Weierstrass p */, "\u2118"}
                        , {"&image;", "&#8465;"/* blackletter capital I = imaginary part */, "\u2111"}
                        , {"&real;", "&#8476;"/* blackletter capital R = real part symbol */, "\u211C"}
                        , {"&trade;", "&#8482;"/* trade mark sign */, "\u2122"}
                        , {"&alefsym;", "&#8501;"/* alef symbol = first transfinite cardinal */, "\u2135"}
/* alef symbol is NOT the same as hebrew letter alef  ,"\u05D0"}*/
/* Arrows */
                        , {"&larr;", "&#8592;"/* leftwards arrow */, "\u2190"}
                        , {"&uarr;", "&#8593;"/* upwards arrow */, "\u2191"}
                        , {"&rarr;", "&#8594;"/* rightwards arrow */, "\u2192"}
                        , {"&darr;", "&#8595;"/* downwards arrow */, "\u2193"}
                        , {"&harr;", "&#8596;"/* left right arrow */, "\u2194"}
                        , {"&crarr;", "&#8629;"/* downwards arrow with corner leftwards = carriage return */, "\u21B5"}
                        , {"&lArr;", "&#8656;"/* leftwards double arrow */, "\u21D0"}
/* Unicode does not say that lArr is the same as the 'is implied by' arrow but also does not have any other character for that function. So ? lArr can be used for 'is implied by' as ISOtech suggests */
                        , {"&uArr;", "&#8657;"/* upwards double arrow */, "\u21D1"}
                        , {"&rArr;", "&#8658;"/* rightwards double arrow */, "\u21D2"}
/* Unicode does not say this is the 'implies' character but does not have another character with this function so ? rArr can be used for 'implies' as ISOtech suggests */
                        , {"&dArr;", "&#8659;"/* downwards double arrow */, "\u21D3"}
                        , {"&hArr;", "&#8660;"/* left right double arrow */, "\u21D4"}
/* Mathematical Operators */
                        , {"&forall;", "&#8704;"/* for all */, "\u2200"}
                        , {"&part;", "&#8706;"/* partial differential */, "\u2202"}
                        , {"&exist;", "&#8707;"/* there exists */, "\u2203"}
                        , {"&empty;", "&#8709;"/* empty set = null set = diameter */, "\u2205"}
                        , {"&nabla;", "&#8711;"/* nabla = backward difference */, "\u2207"}
                        , {"&isin;", "&#8712;"/* element of */, "\u2208"}
                        , {"&notin;", "&#8713;"/* not an element of */, "\u2209"}
                        , {"&ni;", "&#8715;"/* contains as member */, "\u220B"}
/* should there be a more memorable name than 'ni'? */
                        , {"&prod;", "&#8719;"/* n-ary product = product sign */, "\u220F"}
/* prod is NOT the same character as ,"\u03A0"}*/
                        , {"&sum;", "&#8721;"/* n-ary sumation */, "\u2211"}
/* sum is NOT the same character as ,"\u03A3"}*/
                        , {"&minus;", "&#8722;"/* minus sign */, "\u2212"}
                        , {"&lowast;", "&#8727;"/* asterisk operator */, "\u2217"}
                        , {"&radic;", "&#8730;"/* square root = radical sign */, "\u221A"}
                        , {"&prop;", "&#8733;"/* proportional to */, "\u221D"}
                        , {"&infin;", "&#8734;"/* infinity */, "\u221E"}
                        , {"&ang;", "&#8736;"/* angle */, "\u2220"}
                        , {"&and;", "&#8743;"/* logical and = wedge */, "\u2227"}
                        , {"&or;", "&#8744;"/* logical or = vee */, "\u2228"}
                        , {"&cap;", "&#8745;"/* intersection = cap */, "\u2229"}
                        , {"&cup;", "&#8746;"/* union = cup */, "\u222A"}
                        , {"&int;", "&#8747;"/* integral */, "\u222B"}
                        , {"&there4;", "&#8756;"/* therefore */, "\u2234"}
                        , {"&sim;", "&#8764;"/* tilde operator = varies with = similar to */, "\u223C"}
/* tilde operator is NOT the same character as the tilde  ,"\u007E"}*/
                        , {"&cong;", "&#8773;"/* approximately equal to */, "\u2245"}
                        , {"&asymp;", "&#8776;"/* almost equal to = asymptotic to */, "\u2248"}
                        , {"&ne;", "&#8800;"/* not equal to */, "\u2260"}
                        , {"&equiv;", "&#8801;"/* identical to */, "\u2261"}
                        , {"&le;", "&#8804;"/* less-than or equal to */, "\u2264"}
                        , {"&ge;", "&#8805;"/* greater-than or equal to */, "\u2265"}
                        , {"&sub;", "&#8834;"/* subset of */, "\u2282"}
                        , {"&sup;", "&#8835;"/* superset of */, "\u2283"}
/* note that nsup  'not a superset of  ,"\u2283"}*/
                        , {"&sube;", "&#8838;"/* subset of or equal to */, "\u2286"}
                        , {"&supe;", "&#8839;"/* superset of or equal to */, "\u2287"}
                        , {"&oplus;", "&#8853;"/* circled plus = direct sum */, "\u2295"}
                        , {"&otimes;", "&#8855;"/* circled times = vector product */, "\u2297"}
                        , {"&perp;", "&#8869;"/* up tack = orthogonal to = perpendicular */, "\u22A5"}
                        , {"&sdot;", "&#8901;"/* dot operator */, "\u22C5"}
/* dot operator is NOT the same character as ,"\u00B7"}
/* Miscellaneous Technical */
                        , {"&lceil;", "&#8968;"/* left ceiling = apl upstile */, "\u2308"}
                        , {"&rceil;", "&#8969;"/* right ceiling */, "\u2309"}
                        , {"&lfloor;", "&#8970;"/* left floor = apl downstile */, "\u230A"}
                        , {"&rfloor;", "&#8971;"/* right floor */, "\u230B"}
                        , {"&lang;", "&#9001;"/* left-pointing angle bracket = bra */, "\u2329"}
/* lang is NOT the same character as ,"\u003C"}*/
                        , {"&rang;", "&#9002;"/* right-pointing angle bracket = ket */, "\u232A"}
/* rang is NOT the same character as ,"\u003E"}*/
/* Geometric Shapes */
                        , {"&loz;", "&#9674;"/* lozenge */, "\u25CA"}
/* Miscellaneous Symbols */
                        , {"&spades;", "&#9824;"/* black spade suit */, "\u2660"}
/* black here seems to mean filled as opposed to hollow */
                        , {"&clubs;", "&#9827;"/* black club suit = shamrock */, "\u2663"}
                        , {"&hearts;", "&#9829;"/* black heart suit = valentine */, "\u2665"}
                        , {"&diams;", "&#9830;"/* black diamond suit */, "\u2666"}
                        , {"&quot;", "&#34;" /* quotation mark = APL quote */, "\""}
                        , {"&amp;", "&#38;" /* ampersand */, "\u0026"}
                        , {"&lt;", "&#60;" /* less-than sign */, "\u003C"}
                        , {"&gt;", "&#62;" /* greater-than sign */, "\u003E"}
/* Latin Extended-A */
                        , {"&OElig;", "&#338;" /* latin capital ligature OE */, "\u0152"}
                        , {"&oelig;", "&#339;" /* latin small ligature oe */, "\u0153"}
/* ligature is a misnomer  this is a separate character in some languages */
                        , {"&Scaron;", "&#352;" /* latin capital letter S with caron */, "\u0160"}
                        , {"&scaron;", "&#353;" /* latin small letter s with caron */, "\u0161"}
                        , {"&Yuml;", "&#376;" /* latin capital letter Y with diaeresis */, "\u0178"}
/* Spacing Modifier Letters */
                        , {"&circ;", "&#710;" /* modifier letter circumflex accent */, "\u02C6"}
                        , {"&tilde;", "&#732;" /* small tilde */, "\u02DC"}
/* General Punctuation */
                        , {"&ensp;", "&#8194;"/* en space */, "\u2002"}
                        , {"&emsp;", "&#8195;"/* em space */, "\u2003"}
                        , {"&thinsp;", "&#8201;"/* thin space */, "\u2009"}
                        , {"&zwnj;", "&#8204;"/* zero width non-joiner */, "\u200C"}
                        , {"&zwj;", "&#8205;"/* zero width joiner */, "\u200D"}
                        , {"&lrm;", "&#8206;"/* left-to-right mark */, "\u200E"}
                        , {"&rlm;", "&#8207;"/* right-to-left mark */, "\u200F"}
                        , {"&ndash;", "&#8211;"/* en dash */, "\u2013"}
                        , {"&mdash;", "&#8212;"/* em dash */, "\u2014"}
                        , {"&lsquo;", "&#8216;"/* left single quotation mark */, "\u2018"}
                        , {"&rsquo;", "&#8217;"/* right single quotation mark */, "\u2019"}
                        , {"&sbquo;", "&#8218;"/* single low-9 quotation mark */, "\u201A"}
                        , {"&ldquo;", "&#8220;"/* left double quotation mark */, "\u201C"}
                        , {"&rdquo;", "&#8221;"/* right double quotation mark */, "\u201D"}
                        , {"&bdquo;", "&#8222;"/* double low-9 quotation mark */, "\u201E"}
                        , {"&dagger;", "&#8224;"/* dagger */, "\u2020"}
                        , {"&Dagger;", "&#8225;"/* double dagger */, "\u2021"}
                        , {"&permil;", "&#8240;"/* per mille sign */, "\u2030"}
                        , {"&lsaquo;", "&#8249;"/* single left-pointing angle quotation mark */, "\u2039"}
/* lsaquo is proposed but not yet ISO standardized */
                        , {"&rsaquo;", "&#8250;"/* single right-pointing angle quotation mark */, "\u203A"}
/* rsaquo is proposed but not yet ISO standardized */
                        , {"&euro;", "&#8364;" /* euro sign */, "\u20AC"}};
        for (String[] entity : entities) {
            entityEscapeMap.put(entity[2], entity[0]);
            escapeEntityMap.put(entity[0], entity[2]);
            escapeEntityMap.put(entity[1], entity[2]);
        }
    }
}
