/* * Static String formatting and query routines. * Copyright (C) 2001-2005 Stephen Ostermiller * http://ostermiller.org/contact.pl?regarding=Java+Utilities * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See COPYING.TXT for details. */ package com.Ostermiller.util; import java.util.HashMap; import java.util.regex.Pattern; /** * Utilities for String formatting, manipulation, and queries. * More information about this class is available from <a target="_top" href= * "http://ostermiller.org/utils/StringHelper.html">ostermiller.org</a>. * * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities * @since ostermillerutils 1.00.00 */ public class StringHelper { /** * Pad the beginning of the given String with spaces until * the String is of the given length. * <p> * If a String is longer than the desired length, * it will not be truncated, however no padding * will be added. * * @param s String to be padded. * @param length desired length of result. * @return padded String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String prepad(String s, int length){ return prepad(s, length, ' '); } /** * Pre-pend the given character to the String until * the result is the desired length. * <p> * If a String is longer than the desired length, * it will not be truncated, however no padding * will be added. * * @param s String to be padded. * @param length desired length of result. * @param c padding character. * @return padded String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String prepad(String s, int length, char c){ int needed = length - s.length(); if (needed <= 0){ return s; } char padding[] = new char[needed]; java.util.Arrays.fill(padding, c); StringBuffer sb = new StringBuffer(length); sb.append(padding); sb.append(s); return sb.toString(); } /** * Pad the end of the given String with spaces until * the String is of the given length. * <p> * If a String is longer than the desired length, * it will not be truncated, however no padding * will be added. * * @param s String to be padded. * @param length desired length of result. * @return padded String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String postpad(String s, int length){ return postpad(s, length, ' '); } /** * Append the given character to the String until * the result is the desired length. * <p> * If a String is longer than the desired length, * it will not be truncated, however no padding * will be added. * * @param s String to be padded. * @param length desired length of result. * @param c padding character. * @return padded String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String postpad(String s, int length, char c){ int needed = length - s.length(); if (needed <= 0){ return s; } char padding[] = new char[needed]; java.util.Arrays.fill(padding, c); StringBuffer sb = new StringBuffer(length); sb.append(s); sb.append(padding); return sb.toString(); } /** * Pad the beginning and end of the given String with spaces until * the String is of the given length. The result is that the original * String is centered in the middle of the new string. * <p> * If the number of characters to pad is even, then the padding * will be split evenly between the beginning and end, otherwise, * the extra character will be added to the end. * <p> * If a String is longer than the desired length, * it will not be truncated, however no padding * will be added. * * @param s String to be padded. * @param length desired length of result. * @return padded String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String midpad(String s, int length){ return midpad(s, length, ' '); } /** * Pad the beginning and end of the given String with the given character * until the result is the desired length. The result is that the original * String is centered in the middle of the new string. * <p> * If the number of characters to pad is even, then the padding * will be split evenly between the beginning and end, otherwise, * the extra character will be added to the end. * <p> * If a String is longer than the desired length, * it will not be truncated, however no padding * will be added. * * @param s String to be padded. * @param length desired length of result. * @param c padding character. * @return padded String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String midpad(String s, int length, char c){ int needed = length - s.length(); if (needed <= 0){ return s; } int beginning = needed / 2; int end = beginning + needed % 2; char prepadding[] = new char[beginning]; java.util.Arrays.fill(prepadding, c); char postpadding[] = new char[end]; java.util.Arrays.fill(postpadding, c); StringBuffer sb = new StringBuffer(length); sb.append(prepadding); sb.append(s); sb.append(postpadding); return sb.toString(); } /** * Split the given String into tokens. * <P> * This method is meant to be similar to the split * function in other programming languages but it does * not use regular expressions. Rather the String is * split on a single String literal. * <P> * Unlike java.util.StringTokenizer which accepts * multiple character tokens as delimiters, the delimiter * here is a single String literal. * <P> * Each null token is returned as an empty String. * Delimiters are never returned as tokens. * <P> * If there is no delimiter because it is either empty or * null, the only element in the result is the original String. * <P> * StringHelper.split("1-2-3", "-");<br> * result: {"1","2","3"}<br> * StringHelper.split("-1--2-", "-");<br> * result: {"","1","","2",""}<br> * StringHelper.split("123", "");<br> * result: {"123"}<br> * StringHelper.split("1-2---3----4", "--");<br> * result: {"1-2","-3","","4"}<br> * * @param s String to be split. * @param delimiter String literal on which to split. * @return an array of tokens. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String[] split(String s, String delimiter){ int delimiterLength; // the next statement has the side effect of throwing a null pointer // exception if s is null. int stringLength = s.length(); if (delimiter == null || (delimiterLength = delimiter.length()) == 0){ // it is not inherently clear what to do if there is no delimiter // On one hand it would make sense to return each character because // the null String can be found between each pair of characters in // a String. However, it can be found many times there and we don' // want to be returning multiple null tokens. // returning the whole String will be defined as the correct behavior // in this instance. return new String[] {s}; } // a two pass solution is used because a one pass solution would // require the possible resizing and copying of memory structures // In the worst case it would have to be resized n times with each // resize having a O(n) copy leading to an O(n^2) algorithm. int count; int start; int end; // Scan s and count the tokens. count = 0; start = 0; while((end = s.indexOf(delimiter, start)) != -1){ count++; start = end + delimiterLength; } count++; // allocate an array to return the tokens, // we now know how big it should be String[] result = new String[count]; // Scan s again, but this time pick out the tokens count = 0; start = 0; while((end = s.indexOf(delimiter, start)) != -1){ result[count] = (s.substring(start, end)); count++; start = end + delimiterLength; } end = stringLength; result[count] = s.substring(start, end); return (result); } /** * Split the given String into tokens. Delimiters will * be returned as tokens. * <P> * This method is meant to be similar to the split * function in other programming languages but it does * not use regular expressions. Rather the String is * split on a single String literal. * <P> * Unlike java.util.StringTokenizer which accepts * multiple character tokens as delimiters, the delimiter * here is a single String literal. * <P> * Each null token is returned as an empty String. * Delimiters are never returned as tokens. * <P> * If there is no delimiter because it is either empty or * null, the only element in the result is the original String. * <P> * StringHelper.split("1-2-3", "-");<br> * result: {"1","-","2","-","3"}<br> * StringHelper.split("-1--2-", "-");<br> * result: {"","-","1","-","","-","2","-",""}<br> * StringHelper.split("123", "");<br> * result: {"123"}<br> * StringHelper.split("1-2--3---4----5", "--");<br> * result: {"1-2","--","3","--","-4","--","","--","5"}<br> * * @param s String to be split. * @param delimiter String literal on which to split. * @return an array of tokens. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.05.00 */ public static String[] splitIncludeDelimiters(String s, String delimiter){ int delimiterLength; // the next statement has the side effect of throwing a null pointer // exception if s is null. int stringLength = s.length(); if (delimiter == null || (delimiterLength = delimiter.length()) == 0){ // it is not inherently clear what to do if there is no delimiter // On one hand it would make sense to return each character because // the null String can be found between each pair of characters in // a String. However, it can be found many times there and we don' // want to be returning multiple null tokens. // returning the whole String will be defined as the correct behavior // in this instance. return new String[] {s}; } // a two pass solution is used because a one pass solution would // require the possible resizing and copying of memory structures // In the worst case it would have to be resized n times with each // resize having a O(n) copy leading to an O(n^2) algorithm. int count; int start; int end; // Scan s and count the tokens. count = 0; start = 0; while((end = s.indexOf(delimiter, start)) != -1){ count+=2; start = end + delimiterLength; } count++; // allocate an array to return the tokens, // we now know how big it should be String[] result = new String[count]; // Scan s again, but this time pick out the tokens count = 0; start = 0; while((end = s.indexOf(delimiter, start)) != -1){ result[count] = (s.substring(start, end)); count++; result[count] = delimiter; count++; start = end + delimiterLength; } end = stringLength; result[count] = s.substring(start, end); return (result); } /** * Join all the elements of a string array into a single * String. * <p> * If the given array empty an empty string * will be returned. Null elements of the array are allowed * and will be treated like empty Strings. * * @param array Array to be joined into a string. * @return Concatenation of all the elements of the given array. * @throws NullPointerException if array is null. * * @since ostermillerutils 1.05.00 */ public static String join(String[] array){ return join(array, ""); } /** * Join all the elements of a string array into a single * String. * <p> * If the given array empty an empty string * will be returned. Null elements of the array are allowed * and will be treated like empty Strings. * * @param array Array to be joined into a string. * @param delimiter String to place between array elements. * @return Concatenation of all the elements of the given array with the the delimiter in between. * @throws NullPointerException if array or delimiter is null. * * @since ostermillerutils 1.05.00 */ public static String join(String[] array, String delimiter){ // Cache the length of the delimiter // has the side effect of throwing a NullPointerException if // the delimiter is null. int delimiterLength = delimiter.length(); // Nothing in the array return empty string // has the side effect of throwing a NullPointerException if // the array is null. if (array.length == 0) return ""; // Only one thing in the array, return it. if (array.length == 1){ if (array[0] == null) return ""; return array[0]; } // Make a pass through and determine the size // of the resulting string. int length = 0; for (int i=0; i<array.length; i++){ if (array[i] != null) length+=array[i].length(); if (i<array.length-1) length+=delimiterLength; } // Make a second pass through and concatenate everything // into a string buffer. StringBuffer result = new StringBuffer(length); for (int i=0; i<array.length; i++){ if (array[i] != null) result.append(array[i]); if (i<array.length-1) result.append(delimiter); } return result.toString(); } /** * Replace occurrences of a substring. * * StringHelper.replace("1-2-3", "-", "|");<br> * result: "1|2|3"<br> * StringHelper.replace("-1--2-", "-", "|");<br> * result: "|1||2|"<br> * StringHelper.replace("123", "", "|");<br> * result: "123"<br> * StringHelper.replace("1-2---3----4", "--", "|");<br> * result: "1-2|-3||4"<br> * StringHelper.replace("1-2---3----4", "--", "---");<br> * result: "1-2----3------4"<br> * * @param s String to be modified. * @param find String to find. * @param replace String to replace. * @return a string with all the occurrences of the string to find replaced. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String replace(String s, String find, String replace){ int findLength; // the next statement has the side effect of throwing a null pointer // exception if s is null. int stringLength = s.length(); if (find == null || (findLength = find.length()) == 0){ // If there is nothing to find, we won't try and find it. return s; } if (replace == null){ // a null string and an empty string are the same // for replacement purposes. replace = ""; } int replaceLength = replace.length(); // We need to figure out how long our resulting string will be. // This is required because without it, the possible resizing // and copying of memory structures could lead to an unacceptable runtime. // In the worst case it would have to be resized n times with each // resize having a O(n) copy leading to an O(n^2) algorithm. int length; if (findLength == replaceLength){ // special case in which we don't need to count the replacements // because the count falls out of the length formula. length = stringLength; } else { int count; int start; int end; // Scan s and count the number of times we find our target. count = 0; start = 0; while((end = s.indexOf(find, start)) != -1){ count++; start = end + findLength; } if (count == 0){ // special case in which on first pass, we find there is nothing // to be replaced. No need to do a second pass or create a string buffer. return s; } length = stringLength - (count * (findLength - replaceLength)); } int start = 0; int end = s.indexOf(find, start); if (end == -1){ // nothing was found in the string to replace. // we can get this if the find and replace strings // are the same length because we didn't check before. // in this case, we will return the original string return s; } // it looks like we actually have something to replace // *sigh* allocate memory for it. StringBuffer sb = new StringBuffer(length); // Scan s and do the replacements while (end != -1){ sb.append(s.substring(start, end)); sb.append(replace); start = end + findLength; end = s.indexOf(find, start); } end = stringLength; sb.append(s.substring(start, end)); return (sb.toString()); } /** * Replaces characters that may be confused by a HTML * parser with their equivalent character entity references. * <p> * Any data that will appear as text on a web page should * be be escaped. This is especially important for data * that comes from untrusted sources such as Internet users. * A common mistake in CGI programming is to ask a user for * data and then put that data on a web page. For example:<pre> * Server: What is your name? * User: <b>Joe<b> * Server: Hello <b>Joe</b>, Welcome</pre> * If the name is put on the page without checking that it doesn't * contain HTML code or without sanitizing that HTML code, the user * could reformat the page, insert scripts, and control the the * content on your web server. * <p> * This method will replace HTML characters such as > with their * HTML entity reference (&gt;) so that the html parser will * be sure to interpret them as plain text rather than HTML or script. * <p> * This method should be used for both data to be displayed in text * in the html document, and data put in form elements. For example:<br> * <code><html><body><i>This in not a &lt;tag&gt; * in HTML</i></body></html></code><br> * and<br> * <code><form><input type="hidden" name="date" value="<i>This data could * be &quot;malicious&quot;</i>"></form></code><br> * In the second example, the form data would be properly be resubmitted * to your CGI script in the URLEncoded format:<br> * <code><i>This data could be %22malicious%22</i></code> * * @param s String to be escaped * @return escaped String * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String escapeHTML(String s){ int length = s.length(); int newLength = length; boolean someCharacterEscaped = false; // first check for characters that might // be dangerous and calculate a length // of the string that has escapes. for (int i=0; i<length; i++){ char c = s.charAt(i); int cint = 0xffff & c; if (cint < 32){ switch(c){ case '\r': case '\n': case '\t': case '\f':{ // Leave whitespace untouched } break; default: { newLength -= 1; someCharacterEscaped = true; } } } else { switch(c){ case '\"':{ newLength += 5; someCharacterEscaped = true; } break; case '&': case '\'':{ newLength += 4; someCharacterEscaped = true; } break; case '<': case '>':{ newLength += 3; someCharacterEscaped = true; } break; } } } if (!someCharacterEscaped){ // nothing to escape in the string return s; } StringBuffer sb = new StringBuffer(newLength); for (int i=0; i<length; i++){ char c = s.charAt(i); int cint = 0xffff & c; if (cint < 32){ switch(c){ case '\r': case '\n': case '\t': case '\f':{ sb.append(c); } break; default: { // Remove this character } } } else { switch(c){ case '\"':{ sb.append("""); } break; case '\'':{ sb.append("'"); } break; case '&':{ sb.append("&"); } break; case '<':{ sb.append("<"); } break; case '>':{ sb.append(">"); } break; default: { sb.append(c); } } } } return sb.toString(); } /** * Replaces characters that may be confused by an SQL * parser with their equivalent escape characters. * <p> * Any data that will be put in an SQL query should * be be escaped. This is especially important for data * that comes from untrusted sources such as Internet users. * <p> * For example if you had the following SQL query:<br> * <code>"SELECT * FROM addresses WHERE name='" + name + "' AND private='N'"</code><br> * Without this function a user could give <code>" OR 1=1 OR ''='"</code> * as their name causing the query to be:<br> * <code>"SELECT * FROM addresses WHERE name='' OR 1=1 OR ''='' AND private='N'"</code><br> * which will give all addresses, including private ones.<br> * Correct usage would be:<br> * <code>"SELECT * FROM addresses WHERE name='" + StringHelper.escapeSQL(name) + "' AND private='N'"</code><br> * <p> * Another way to avoid this problem is to use a PreparedStatement * with appropriate place holders. * * @param s String to be escaped * @return escaped String * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String escapeSQL(String s){ int length = s.length(); int newLength = length; // first check for characters that might // be dangerous and calculate a length // of the string that has escapes. for (int i=0; i<length; i++){ char c = s.charAt(i); switch(c){ case '\\': case '\"': case '\'': case '\0':{ newLength += 1; } break; } } if (length == newLength){ // nothing to escape in the string return s; } StringBuffer sb = new StringBuffer(newLength); for (int i=0; i<length; i++){ char c = s.charAt(i); switch(c){ case '\\':{ sb.append("\\\\"); } break; case '\"':{ sb.append("\\\""); } break; case '\'':{ sb.append("\\\'"); } break; case '\0':{ sb.append("\\0"); } break; default: { sb.append(c); } } } return sb.toString(); } /** * Replaces characters that are not allowed in a Java style * string literal with their escape characters. Specifically * quote ("), single quote ('), new line (\n), carriage return (\r), * and backslash (\), and tab (\t) are escaped. * * @param s String to be escaped * @return escaped String * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String escapeJavaLiteral(String s){ int length = s.length(); int newLength = length; // first check for characters that might // be dangerous and calculate a length // of the string that has escapes. for (int i=0; i<length; i++){ char c = s.charAt(i); switch(c){ case '\"': case '\'': case '\n': case '\r': case '\t': case '\\':{ newLength += 1; } break; } } if (length == newLength){ // nothing to escape in the string return s; } StringBuffer sb = new StringBuffer(newLength); for (int i=0; i<length; i++){ char c = s.charAt(i); switch(c){ case '\"':{ sb.append("\\\""); } break; case '\'':{ sb.append("\\\'"); } break; case '\n':{ sb.append("\\n"); } break; case '\r':{ sb.append("\\r"); } break; case '\t':{ sb.append("\\t"); } break; case '\\':{ sb.append("\\\\"); } break; default: { sb.append(c); } } } return sb.toString(); } /** * Trim any of the characters contained in the second * string from the beginning and end of the first. * * @param s String to be trimmed. * @param c list of characters to trim from s. * @return trimmed String. * @throws NullPointerException if s is null. * * @since ostermillerutils 1.00.00 */ public static String trim(String s, String c){ int length = s.length(); if (c == null){ return s; } int cLength = c.length(); if (c.length() == 0){ return s; } int start = 0; int end = length; boolean found; // trim-able character found. int i; // Start from the beginning and find the // first non-trim-able character. found = false; for (i=0; !found && i<length; i++){ char ch = s.charAt(i); found = true; for (int j=0; found && j<cLength; j++){ if (c.charAt(j) == ch) found = false; } } // if all characters are trim-able. if (!found) return ""; start = i-1; // Start from the end and find the // last non-trim-able character. found = false; for (i=length-1; !found && i>=0; i--){ char ch = s.charAt(i); found = true; for (int j=0; found && j<cLength; j++){ if (c.charAt(j) == ch) found = false; } } end = i+2; return s.substring(start, end); } private static HashMap<String,Integer> htmlEntities = new HashMap<String,Integer>(); static { htmlEntities.put("n"+"b"+"s"+"p", new Integer(160)); htmlEntities.put("i"+"e"+"x"+"c"+"l", new Integer(161)); htmlEntities.put("cent", new Integer(162)); htmlEntities.put("pound", new Integer(163)); htmlEntities.put("c"+"u"+"r"+"r"+"e"+"n", new Integer(164)); htmlEntities.put("y"+"e"+"n", new Integer(165)); htmlEntities.put("b"+"r"+"v"+"b"+"a"+"r", new Integer(166)); htmlEntities.put("sect", new Integer(167)); htmlEntities.put("u"+"m"+"l", new Integer(168)); htmlEntities.put("copy", new Integer(169)); htmlEntities.put("o"+"r"+"d"+"f", new Integer(170)); htmlEntities.put("l"+"a"+"quo", new Integer(171)); htmlEntities.put("not", new Integer(172)); htmlEntities.put("shy", new Integer(173)); htmlEntities.put("r"+"e"+"g", new Integer(174)); htmlEntities.put("m"+"a"+"c"+"r", new Integer(175)); htmlEntities.put("d"+"e"+"g", new Integer(176)); htmlEntities.put("plus"+"m"+"n", new Integer(177)); htmlEntities.put("sup2", new Integer(178)); htmlEntities.put("sup3", new Integer(179)); htmlEntities.put("acute", new Integer(180)); htmlEntities.put("m"+"i"+"c"+"r"+"o", new Integer(181)); htmlEntities.put("par"+"a", new Integer(182)); htmlEntities.put("mid"+"dot", new Integer(183)); htmlEntities.put("c"+"e"+"d"+"i"+"l", new Integer(184)); htmlEntities.put("sup1", new Integer(185)); htmlEntities.put("o"+"r"+"d"+"m", new Integer(186)); htmlEntities.put("r"+"a"+"quo", new Integer(187)); htmlEntities.put("frac14", new Integer(188)); htmlEntities.put("frac12", new Integer(189)); htmlEntities.put("frac34", new Integer(190)); htmlEntities.put("i"+"quest", new Integer(191)); htmlEntities.put("A"+"grave", new Integer(192)); htmlEntities.put("A"+"a"+"cute", new Integer(193)); htmlEntities.put("A"+"c"+"i"+"r"+"c", new Integer(194)); htmlEntities.put("A"+"tilde", new Integer(195)); htmlEntities.put("A"+"u"+"m"+"l", new Integer(196)); htmlEntities.put("A"+"ring", new Integer(197)); htmlEntities.put("A"+"E"+"l"+"i"+"g", new Integer(198)); htmlEntities.put("C"+"c"+"e"+"d"+"i"+"l", new Integer(199)); htmlEntities.put("E"+"grave", new Integer(200)); htmlEntities.put("E"+"a"+"cute", new Integer(201)); htmlEntities.put("E"+"c"+"i"+"r"+"c", new Integer(202)); htmlEntities.put("E"+"u"+"m"+"l", new Integer(203)); htmlEntities.put("I"+"grave", new Integer(204)); htmlEntities.put("I"+"a"+"cute", new Integer(205)); htmlEntities.put("I"+"c"+"i"+"r"+"c", new Integer(206)); htmlEntities.put("I"+"u"+"m"+"l", new Integer(207)); htmlEntities.put("ETH", new Integer(208)); htmlEntities.put("N"+"tilde", new Integer(209)); htmlEntities.put("O"+"grave", new Integer(210)); htmlEntities.put("O"+"a"+"cute", new Integer(211)); htmlEntities.put("O"+"c"+"i"+"r"+"c", new Integer(212)); htmlEntities.put("O"+"tilde", new Integer(213)); htmlEntities.put("O"+"u"+""+"m"+"l", new Integer(214)); htmlEntities.put("times", new Integer(215)); htmlEntities.put("O"+"slash", new Integer(216)); htmlEntities.put("U"+"grave", new Integer(217)); htmlEntities.put("U"+"a"+"cute", new Integer(218)); htmlEntities.put("U"+"c"+"i"+"r"+"c", new Integer(219)); htmlEntities.put("U"+"u"+"m"+"l", new Integer(220)); htmlEntities.put("Y"+"a"+"cute", new Integer(221)); htmlEntities.put("THORN", new Integer(222)); htmlEntities.put("s"+"z"+"l"+"i"+"g", new Integer(223)); htmlEntities.put("a"+"grave", new Integer(224)); htmlEntities.put("a"+"a"+"cute", new Integer(225)); htmlEntities.put("a"+"c"+"i"+"r"+"c", new Integer(226)); htmlEntities.put("a"+"tilde", new Integer(227)); htmlEntities.put("a"+"u"+"m"+"l", new Integer(228)); htmlEntities.put("a"+"ring", new Integer(229)); htmlEntities.put("a"+"e"+"l"+"i"+"g", new Integer(230)); htmlEntities.put("c"+"c"+"e"+"d"+"i"+"l", new Integer(231)); htmlEntities.put("e"+"grave", new Integer(232)); htmlEntities.put("e"+"a"+"cute", new Integer(233)); htmlEntities.put("e"+"c"+"i"+"r"+"c", new Integer(234)); htmlEntities.put("e"+"u"+"m"+"l", new Integer(235)); htmlEntities.put("i"+"grave", new Integer(236)); htmlEntities.put("i"+"a"+"cute", new Integer(237)); htmlEntities.put("i"+"c"+"i"+"r"+"c", new Integer(238)); htmlEntities.put("i"+"u"+""+"m"+"l", new Integer(239)); htmlEntities.put("e"+"t"+"h", new Integer(240)); htmlEntities.put("n"+"tilde", new Integer(241)); htmlEntities.put("o"+