/* * @(#)StringTools.java * * Summary: Miscellaneous static methods for dealing with Strings in JDK 1.1+. * * Copyright: (c) 1997-2010 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.1+ * * Created with: IntelliJ IDEA IDE. * * Version History: * 1.5 2005-07-14 - split off from Misc, allow for compilation with old compiler. * 1.6 2006-01-01 * 1.7 2006-03-04 - format with IntelliJ and prepare Javadoc * 1.8 2006-10-15 - add condense method. * 1.9 2008-03-10 - add StringTools.firstWord * 2.0 2008-04-01 * 2.1 2008-12-16 - new methods in BigDate * 2.2 2009-04-19 - add countLeading, countTrailing, TrimLeading, TrimTrailing that take multiple trim chars. * 2.3 2009-04-29 - add countInstances( String page, char lookFor ) * 2.4 2009-04-30 - fix but in countLeading ( String text, String leads ). * add pruneExcessBlankLines * 2.5 2009-11-14 - add haveCommonChar, isLetter, isDigit * 2.6 2010-02-11 - removed removeHead (redundant - same as chopLeadingString). * renamed chopLeading to chopLeadingString to clearify does not trim a variety of chars. */ package com.mindprod.common11; import java.awt.Color; import java.util.ArrayList; import java.util.BitSet; /** * Miscellaneous static methods for dealing with Strings in JDK 1.1+. * <p/> * Augmented by com.mindprod.common15.StringTools for JDK 1.5+. * <p/> * * @author Roedy Green, Canadian Mind Products * @version 2.6 2010-02-11 - removed removeHead (redundant - same as chopLeadingString). renamed chopLeading to * chopLeadingString to clearify does not trim a variety of chars. * @noinspection WeakerAccess * @since 2003-05-15 */ public class StringTools { // ------------------------------ CONSTANTS ------------------------------ /** * true if you want extra debugging output and test code */ private static final boolean DEBUGGING = false; /** * used to efficiently generate Strings of spaces of varying length */ private static final String SOMESPACES = " "; /** * track which chars in range 0..017F are vowels. Lookup table takes only 48 bytes. */ private static final BitSet vt = new BitSet(0x0180); // -------------------------- PUBLIC STATIC METHODS -------------------------- /** * makeshift system beep if awt.Toolkit.beep is not available. Works also in JDK 1.02. */ public static void beep() { System.out.print("\007"); System.out.flush(); }// end beep /** * Convert String to canonical standard form. null -> "". Trims lead trail blanks. Never null. * * @param s String to be converted. * @return String in canonical form. */ public static String canonical(String s) { if (s == null) { return ""; } else { return s.trim(); } } /** * remove leading string if present * * @param text text with possible leading string, possibly empty or null. * @param toChop the leading string of interest. Not a list of possible chars to chop, order matters. * @return string with to toChop string removed if the text starts with it, otherwise the original string unmodified. * @see #trimLeading(String,String) * @see #chopTrailingString(String,String) */ public static String chopLeadingString(String text, String toChop) { if (text != null && text.startsWith(toChop)) { return text.substring(toChop.length()); } else { return text; } } /** * remove trailing string if present * * @param text text with possible trailing string, possibly empty, but not null. * @param toChop the trailing string of interest. Not a list of possible chars to chop, order matters. * @return string with to toChop string removed if the text ends with it, otherwise the original string unmodified. * @see #trimTrailing(String,String) * @see #chopLeadingString(String,String) */ public static String chopTrailingString(String text, String toChop) { if (text != null && text.endsWith(toChop)) { return text.substring(0, text.length() - toChop.length()); } else { return text; } } /** * Collapse multiple spaces in string down to a single space. Remove lead and trailing spaces. Does not collapse * other whitespace. * * @param s String to strip of blanks. * @return String with all blanks, lead/trail/embedded removed. * @noinspection WeakerAccess,SameParameterValue * @see #squish(String) */ public static String condense(String s) { if (s == null) { return null; } s = s.trim(); if (s.indexOf(" ") < 0) { return s; } int len = s.length(); // have to use StringBuffer for JDK 1.1 StringBuffer b = new StringBuffer(len - 1); boolean suppressSpaces = false; for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c == ' ') { if (suppressSpaces) { // subsequent space } else { // first space b.append(c); suppressSpaces = true; } } else { // was not a space b.append(c); suppressSpaces = false; } }// end for return b.toString(); } /** * Count how many times a String occurs on a page. * * @param page big String to look in. * @param lookFor small String to look for and count instances. * @return number of times the String appears non-overlapping. */ public static int countInstances(String page, String lookFor) { int count = 0; for (int start = 0; (start = page.indexOf(lookFor, start)) >= 0; start += lookFor.length()) { count++; } return count; } /** * Count how many times a char occurs in a String. * * @param page big String to look in. * @param lookFor char to lookfor count instances. * @return number of times the char appears. */ public static int countInstances(String page, char lookFor) { int count = 0; for (int i = 0; i < page.length(); i++) { if (page.charAt(i) == lookFor) { count++; } } return count; } /** * count of how many leading characters there are on a string matching a given character. It does not remove them. * * @param text text with possible leading characters, possibly empty, but not null. * @param c the leading character of interest, usually ' ' or '\n' * @return count of leading matching characters, possibly 0. * @noinspection WeakerAccess */ public static int countLeading(String text, char c) { int count; for (count = 0; count < text.length() && text.charAt(count) == c; count++) { // empty loop } return count; } /** * count of how many leading characters there are on a string matching a given character. It does not remove them. * * @param text text with possible leading characters, possibly empty, but not null. * @param possibleChars the leading characters of interest, usually ' ' or '\n' * @return count of leading matching characters, possibly 0. * @noinspection WeakerAccess */ public static int countLeading(String text, String possibleChars) { int count; for (count = 0; count < text.length() && possibleChars.indexOf(text.charAt(count)) >= 0; count++) { // empty loop. } return count; } /** * count of how many trailing characters there are on a string matching a given character. It does not remove them. * * @param text text with possible trailing characters, possibly empty, but not null. * @param c the trailing character of interest, usually ' ' or '\n' * @return count of trailing matching characters, possibly 0. * @noinspection WeakerAccess */ public static int countTrailing(String text, char c) { int length = text.length(); // need defined outside the for loop. int count; for (count = 0; count < length && text.charAt(length - 1 - count) == c; count++) { // empty loop } return count; } /** * count of how many trailing characters there are on a string matching a given character. It does not remove them. * * @param text text with possible trailing characters, possibly empty, but not null. * @param possibleChars the trailing characters of interest, usually ' ' or '\n' * @return count of trailing matching characters, possibly 0. * @noinspection WeakerAccess */ public static int countTrailing(String text, String possibleChars) { int length = text.length(); // need defined outside the for loop. int count; for (count = 0; count < length && possibleChars.indexOf(text.charAt(length - 1 - count)) >= 0; count++) { // empty loop } return count; } /** * gets the first word of a String, delimited by space or the end of the string. \n will not delimit a word. If * there are no blanks in the string, the result is the entire string. * * @param s the input String * @return the first word of the String. * @see #lastWord(String) */ public static String firstWord(String s) { s = s.trim(); final int place = s.indexOf(' '); return (place < 0) ? s : s.substring(0, place); } /** * Returns true if strings a and b have one or more characters in common, not necessarily at the same offset. If you * think of a and b as sets of chars, returns true if the intersection of those sets in not null. It can also me * though of as like an indexOf that scans for multiple characters at once. * * @param a first string * @param b second string * @return true if the strings have one or more characters in common. */ public static boolean haveCommonChar(String a, String b) { for (int i = 0; i < b.length(); i++) { if (a.indexOf(b.charAt(i)) >= 0) { return true; } } return false; } /** * find the first instance of whitespace (space, \n, \r, \t in a string. * * @param s string to scan * @return -1 if not found, offset relative to start of string where found */ public static int indexOfWhiteSpace(String s) { return indexOfWhiteSpace(s, 0); } /** * find the first instance of whitespace (space, \n, \r, \t in a string. * * @param s string to scan * @param startOffset where in string to start looking * @return -1 if not found, offset relative to start of string where found, not relative to startOffset. */ public static int indexOfWhiteSpace(String s, int startOffset) { final int length = s.length(); for (int i = startOffset; i < length; i++) { switch (s.charAt(i)) { case ' ': case '\n': case '\r': case '\t': return i; default: // keep looking } }// end for return -1; } /** * Check if char is plain ASCII digit. * * @param c char to check. * @return true if char is in range 0-9 * @see Character#isLetter(char) */ public static boolean isDigit(char c) { return '0' <= c && c <= '9'; } /** * Is this string empty? In Java 1.6 + isEmpty is build in. Sun's version being an instance method cannot test for * null. * * @param s String to be tested for emptiness. * @return true if the string is null or equal to the "" null string. or just blanks */ public static boolean isEmpty(String s) { return (s == null) || s.trim().length() == 0; } /** * Ensure the string contains only legal characters. * * @param candidate string to test. * @param legalChars characters than are legal for candidate. * @return true if candidate is formed only of chars from the legal set. */ public static boolean isLegal(String candidate, String legalChars) { for (int i = 0; i < candidate.length(); i++) { if (legalChars.indexOf(candidate.charAt(i)) < 0) { return false; } } return true; } /** * Ensure the char is only one a set of legal characters. * * @param candidate char to test. * @param legalChars characters than are legal for candidate. * @return true if candidate is one of the legallegal set. */ public static boolean isLegal(char candidate, String legalChars) { return legalChars.indexOf(candidate) >= 0; } /** * Check if char is plain ASCII letter lower or upper case. * * @param c char to check. * @return true if char is in range a..z A..Z * @see Character#isLowerCase(char) * @see Character#isUpperCase(char) * @see Character#isDigit(char) */ public static boolean isLetter(char c) { return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'; } /** * Check if char is plain ASCII lower case. * * @param c char to check. * @return true if char is in range a..z. * @see Character#isLowerCase(char) * @see Character#isLetter(char) */ public static boolean isUnaccentedLowerCase(char c) { return 'a' <= c && c <= 'z'; } /** * Check if char is plain ASCII upper case. * * @param c char to check. * @return true if char is in range A..Z. * @see Character#isUpperCase(char) * @see Character#isLetter(char) */ public static boolean isUnaccentedUpperCase(char c) { return 'A' <= c && c <= 'Z'; } /** * is this character a vowel? * * @param c the character, any char upper or lower case, punctuation or symbol * @return true if char is aeiou or AEIOU, or vowel accented in any way or ligature ae AE oe OE ij IJ */ public static boolean isVowel(char c) { return c < 0x0180 && vt.get(c); } /** * gets the last word of a String, delimited by space or the end of the string. * * @param s the input String * @return the last word of the String. * @see #firstWord(String) */ public static String lastWord(String s) { s = s.trim(); return s.substring(s.lastIndexOf(' ') + 1); } /** * Pads the string value out to the given length by applying blanks on the right, left justifying the value. * * @param value value to be converted to string String to be padded/chopped. * @param newLen length of new String desired. * @param chop true if Strings longer than newLen should be truncated to newLen chars. * @return String padded on right/chopped to the desired length. Spaces are inserted on the right. * @see #toLZ */ public static String leftJustified(int value, int newLen, boolean chop) { return rightPad(Integer.toString(value), newLen, chop); } /** * Pads the string out to the given length by applying blanks on the left. * * @param s String to be padded/chopped. * @param newLen length of new String desired. * @param chop true if Strings longer than newLen should be truncated to newLen chars. * @return String padded on left/chopped to the desired length. Spaces are inserted on the left. * @see #toLZ */ public static String leftPad(String s, int newLen, boolean chop) { int grow = newLen - s.length(); if (grow <= 0) { if (chop) { return s.substring(0, newLen); } else { return s; } } else { return spaces(grow) + s; } } /** * convert a String to a long. The routine is very forgiving. It ignores invalid chars, lead trail, embedded spaces, * decimal points etc. Dash is treated as a minus sign. * * @param numStr String to be parsed. * @return long value of String with junk characters stripped. * @throws NumberFormatException if the number is too big to fit in a long. */ public static long parseDirtyLong(String numStr) { numStr = numStr.trim(); // strip commas, spaces, decimals + etc StringBuffer b = new StringBuffer(numStr.length()); boolean negative = false; for (int i = 0, n = numStr.length(); i < n; i++) { char c = numStr.charAt(i); if (c == '-') { negative = true; } else if ('0' <= c && c <= '9') { b.append(c); } }// end for numStr = b.toString(); if (numStr.length() == 0) { return 0; } long num = Long.parseLong(numStr); if (negative) { num = -num; } return num; } /** * convert a String into long pennies. It ignores invalid chars, lead trail, embedded spaces. Dash is treated as a * minus sign. 0 or 2 decimal places are permitted. * * @param numStr String to be parsed. * @return long pennies. * @throws NumberFormatException if the number is too big to fit in a long. * @noinspection WeakerAccess */ public static long parseLongPennies(String numStr) { numStr = numStr.trim(); // strip commas, spaces, + etc StringBuffer b = new StringBuffer(numStr.length()); boolean negative = false; int decpl = -1; for (int i = 0, n = numStr.length(); i < n; i++) { char c = numStr.charAt(i); switch (c) { case '-': negative = true; break; case '.': if (decpl == -1) { decpl = 0; } else { throw new NumberFormatException("more than one decimal point"); } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (decpl != -1) { decpl++; } b.append(c); break; default: // ignore junk chars break; }// end switch }// end for if (numStr.length() != b.length()) { numStr = b.toString(); } if (numStr.length() == 0) { return 0; } long num = Long.parseLong(numStr); if (decpl == -1 || decpl == 0) { num *= 100; } else if (decpl == 2) {/* it is fine as is */ } else { throw new NumberFormatException("wrong number of decimal places."); } if (negative) { num = -num; } return num; } /** * Print dollar currency, stored internally as scaled int. convert pennies to a string with a decorative decimal * point. * * @param pennies long amount in pennies. * @return amount with decorative decimal point, but no lead $. * @noinspection WeakerAccess */ public static String penniesToString(long pennies) { boolean negative; if (pennies < 0) { pennies = -pennies; negative = true; } else { negative = false; } String s = Long.toString(pennies); int len = s.length(); switch (len) { case 1: s = "0.0" + s; break; case 2: s = "0." + s; break; default: s = s.substring(0, len - 2) + "." + s.substring(len - 2, len); break; }// end switch if (negative) { s = "-" + s; } return s; } /** * Extracts a number from a string, returns 0 if malformed. * * @param s String containing the integer. * @return binary integer. */ public static int pluck(String s) { int result = 0; try { result = Integer.parseInt(s); } catch (NumberFormatException e) { // leave result at 0 } return result; } /** * Collapse multiple blank lines down to one. Discards lead and trail blank lines. Blank lines are lines that when * trimmed have length 0. Enhanced version available in com.mindprod.common11.StringTools for JDK 1.5+. * * @param lines array of lines to tidy. * @param minBlankLinesToKeep usually 1 meaning 1+ consecutive blank lines become 1, effectively collapsing runs of * blank lines down to 1. if 2, 1 blank line is removed, and 2+ consecutive blanks lines become 1, * effectively undouble spacing. if zero, non-blank lines will be separated by one blank line, even if * there was not one there to begin with, completely independent of preexisting blank lines, effectively * double spacing.. 9999 effectively removes all blank lines. * @return array of lines with lead and trail blank lines removed, and excess blank lines collapsed down to one or 0. * The results are NOT trimmed. */ public static String[] pruneExcessBlankLines(String[] lines, int minBlankLinesToKeep) { int firstNonBlankLine = lines.length; for (int i = 0; i < lines.length; i++) { if (lines[i].trim().length() > 0) { firstNonBlankLine = i; break; } } int lastNonBlankLine = -1; for (int i = lines.length - 1; i > 0; i--) { if (lines[i].trim().length() > 0) { lastNonBlankLine = i; break; } } if (firstNonBlankLine > lastNonBlankLine) { return new String[0]; } // collapse blank lines in the middle chunk ArrayList<String> keep = new ArrayList<String>(lastNonBlankLine - firstNonBlankLine + 1); int pendingBlankLines = 0; for (int i = firstNonBlankLine; i <= lastNonBlankLine; i++) { if (lines[i].trim().length() == 0) { pendingBlankLines++; } else { if (pendingBlankLines >= minBlankLinesToKeep) { keep.add(""); } keep.add(lines[i]); // we don't trim. That is up to caller. pendingBlankLines = 0; } } return keep.toArray(new String[keep.size()]); } /** * used to prepare SQL string literals by doubling each embedded ' and wrapping in ' at each end. Further quoting is * required to use the results in Java String literals. If you use PreparedStatement, then this method is not * needed. The ' quoting is automatically handled for you. * * @param sql Raw SQL string literal * @return sql String literal enclosed in ' * @noinspection WeakerAccess,SameParameterValue */ public static String quoteSQL(String sql) { StringBuffer sb = new StringBuffer(sql.length() + 5); sb.append('\''); for (int i = 0; i < sql.length(); i++) { char c = sql.charAt(i); if (c == '\'') { sb.append("\'\'"); } else { sb.append(c); } } sb.append('\''); return sb.toString(); } /** * Produce a String of a given repeating character. * * @param c the character to repeat * @param count the number of times to repeat * @return String, e.g. rep('*',4) returns "****" * @noinspection WeakerAccess,SameParameterValue */ public static String rep(char c, int count) { if (c == ' ' && count <= SOMESPACES.length()) { return SOMESPACES.substring(0, count); } char[] s = new char[count]; for (int i = 0; i < count; i++) { s[i] = c; } return new String(s).intern(); } /** * Pads the string value out to the given length by applying blanks on the left, right justifying the value. * * @param value value to be converted to string String to be padded/chopped. * @param newLen length of new String desired. * @param chop true if Strings longer than newLen should be truncated to newLen chars. * @return String padded on left/chopped to the desired length. Spaces are inserted on the left. * @see #toLZ */ public static String rightJustified(int value, int newLen, boolean chop) { return leftPad(Integer.toString(value), newLen, chop); } /** * Pads the string out to the given length by applying blanks on the right. * * @param s String to be padded/chopped. * @param newLen length of new String desired. * @param chop true if Strings longer than newLen should be truncated to newLen chars. * @return String padded on right/chopped to the desired length. Spaces are inserted on the right. * @noinspection WeakerAccess,SameParameterValue */ public static String rightPad(String s, int newLen, boolean chop) { int grow = newLen - s.length(); if (grow <= 0) { if (chop) { return s.substring(0, newLen); } else { return s; } } else { return s + spaces(grow); } } /** * Generate a string of spaces n chars long. * * @param n how many spaces long * @return a string of spaces n chars long. */ public static String spaces(int n) { if (n <= SOMESPACES.length()) { return SOMESPACES.substring(0, n); } else { return rep(' ', n); } } /** * Remove all spaces from a String. Does not touch other whitespace. * * @param s String to strip of blanks. * @return String with all blanks, lead/trail/embedded removed. * @see #condense(String) */ public static String squish(String s) { if (s == null) { return null; } s = s.trim(); if (s.indexOf(' ') < 0) { return s; } int len = s.length(); // have to use StringBuffer for JDK 1.1 StringBuffer b = new StringBuffer(len - 1); for (int i = 0; i < len; i++) { char c = s.charAt(i); if (c != ' ') { b.append(c); } }// end for return b.toString(); } /** * convert to Book Title case, with first letter of each word capitalised. e.g. "handbook to HIGHER consciousness" * -> "Handbook to Higher Consciousness" e.g. "THE HISTORY OF THE U.S.A." -> "The History of the U.S.A." e.g. "THE * HISTORY OF THE USA" -> "The History of the Usa" (sorry about that.) Don't confuse this with Character.isTitleCase * which concerns ligatures. * * @param s String to convert. May be any mixture of case. * @return String with each word capitalised, except embedded words "the" "of" "to" * @noinspection WeakerAccess */ public static String toBookTitleCase(String s) { char[] ca = s.toCharArray(); // Track if we changed anything so that // we can avoid creating a duplicate String // object if the String is already in Title case. boolean changed = false; boolean capitalise = true; boolean firstCap = true; for (int i = 0; i < ca.length; i++) { char oldLetter = ca[i]; if (oldLetter != '\'' && (oldLetter <= '/' || ':' <= oldLetter && oldLetter <= '?' || ']' <= oldLetter && oldLetter <= '`')) { /* whitespace, control chars or punctuation */ /* Next normal char should be capitalised */ /* apostrophe treated as letter so Molly's won't come out Molly'S */ capitalise = true; } else { if (capitalise && !firstCap) { // might be the_ of_ or to_ capitalise = !(s.substring(i, Math.min(i + 4, s.length())).equalsIgnoreCase("the ") || s.substring(i, Math.min(i + 3, s.length())).equalsIgnoreCase("of ") || s.substring(i, Math.min(i + 3, s.length())).equalsIgnoreCase("to ")); }// end if char newLetter = capitalise ? Character.toUpperCase(oldLetter) : Character.toLowerCase(oldLetter); ca[i] = newLetter; changed |= (newLetter != oldLetter); capitalise = false; firstCap = false; }// end if }// end for if (changed) { s = new String(ca); } return s; } /** * Convert int to hex with lead zeroes * * @param h number you want to convert to hex * @return 0x followed by unsigned hex 8-digit representation * @noinspection WeakerAccess * @see #toString(Color) */ public static String toHexString(int h) { String s = Integer.toHexString(h); if (s.length() < 8) {// pad on left with zeros s = "00000000".substring(0, 8 - s.length()) + s; } return "0x" + s; } /** * Convert an integer to a String, with left zeroes. * * @param i the integer to be converted * @param len the length of the resulting string. Warning. It will chop the result on the left if it is too long. * @return String representation of the int e.g. 007 * @see #leftPad */ public static String toLZ(int i, int len) { // Since String is final, we could not add this method there. String s = Integer.toString(i); if (s.length() > len) {/* return rightmost len chars */ return s.substring(s.length() - len); } else if (s.length() < len) // pad on left with zeros { return "000000000000000000000000000000".substring(0, len - s.length()) + s; } else { return s; } } /** * convert an integer value to unsigned hex with leading zeroes. * * @param value integer to convert. * @param len how many characters you want in the result. * @return value in hex, padded to len chars with 0s on the left. */ public static String toLZHexString(int value, int len) { // Since String is final, we could not add this method there. final String s = Integer.toHexString(value); if (s.length() > len) {/* return rightmost len chars */ return s.substring(s.length() - len); } else if (s.length() < len) // pad on left with zeros. at most 7 will be prepended { return "0000000".substring(0, len - s.length()) + s; } else { return s; } } /** * Quick replacement for Character.toLowerCase for use with English-only. It does not deal with accented characters. * * @param c character to convert * @return character converted to lower case */ public static char toLowerCase(char c) { return 'A' <= c && c <= 'Z' ? (char) (c + ('a' - 'A')) : c; } /** * Quick replacement for Character.toLowerCase for use with English-only. It does not deal with accented characters. * * @param s String to convert * @return String converted to lower case */ public static String toLowerCase(String s) { final char[] ca = s.toCharArray(); final int length = ca.length; boolean changed = false; // can't use for:each since we need the index to set. for (int i = 0; i < length; i++) { final char c = ca[i]; if ('A' <= c && c <= 'Z') { // found a char that needs conversion. ca[i] = (char) (c + ('a' - 'A')); changed = true; } } // give back same string if unchanged. return changed ? new String(ca) : s; } /** * Quick replacement for Character.toUpperCase for use with English-only. It does not deal with accented characters. * * @param c character to convert * @return character converted to upper case */ public static char toUpperCase(char c) { return 'a' <= c && c <= 'z' ? (char) (c + ('A' - 'a')) : c; } /** * Quick replacement for Character.toUpperCase for use with English-only. It does not deal with accented characters. * * @param s String to convert * @return String converted to upper case */ public static String toUpperCase(String s) { final char[] ca = s.toCharArray(); final int length = ca.length; boolean changed = false; // can't use for:each since we need the index to set. for (int i = 0; i < length; i++) { final char c = ca[i]; if ('a' <= c && c <= 'z') { // found a char that needs conversion. ca[i] = (char) (c + ('A' - 'a')); changed = true; } } // give back same string if unchanged. return changed ? new String(ca) : s; } /** * Removes white space from beginning this string. * * @param s String to process. As always the original in unchanged. * @return this string, with leading white space removed * @noinspection WeakerAccess,WeakerAccess,WeakerAccess,SameParameterValue,WeakerAccess * @see #trimLeading(String,char) <p/> * All characters that have codes less than or equal to <code>'\u0020'</code> (the space character) are * considered to be white space. */ public static String trimLeading(String s) { if (s == null) { return null; } int len = s.length(); int st = 0; while ((st < len) && (s.charAt(st) <= ' ')) { st++; } return (st > 0) ? s.substring(st, len) : s; } /** * trim leading characters there are on a string matching a given character. * * @param text text with possible trailing characters, possibly empty, but not null. * @param c the trailing character of interest, usually ' ' or '\n' * @return string with any of those trailing characters removed. * @see #trimLeading(String) */ public static String trimLeading(String text, char c) { int count = countLeading(text, c); // substring will optimise the 0 case. return text.substring(count); } /** * trim leading characters there are on a string matching a given characters * * @param text text with possible trailing characters, possibly empty, but not null. * @param toTrim the leading characters of interest, usually ' ' or '\n' * @return string with any of those leading characters removed. * @see #trimTrailing(String) * @see #chopLeadingString(String,String) */ public static String trimLeading(String text, String toTrim) { int count = countLeading(text, toTrim); // substring will optimise the 0 case. return text.substring(count); } /** * Removes white space from end this string. * * @param s String to process. As always the original in unchanged. * @return this string, with trailing white space removed * @see #trimTrailing(String,char) <p/> * All characters that have codes less than or equal to <code>'\u0020'</code> (the space character) are * considered to be white space. */ public static String trimTrailing(String s) { if (s == null) { return null; } int len = s.length(); int origLen = len; while ((len > 0) && (s.charAt(len - 1) <= ' ')) { len--; } return (len != origLen) ? s.substring(0, len) : s; } /** * trim trailing characters there are on a string matching a given character. * * @param text text with possible trailing characters, possibly empty, but not null. * @param c the trailing character of interest, usually ' ' or '\n' * @return string with any of those trailing characters removed. * @see #trimTrailing(String) */ public static String trimTrailing(String text, char c) { int count = countTrailing(text, c); // substring will optimise the 0 case. return text.substring(0, text.length() - count); } /** * trim trailing characters there are on a string matching given characters. * * @param text text with possible trailing characters, possibly empty, but not null. * @param toTrim the trailing characters of interest, usually ' ' or '\n' * @return string with any of those trailing characters removed. ".com" would not only chop .com, but any combination * of those letters e.g. mc.moc * @see #trimTrailing(String) * @see #chopTrailingString(String,String) */ public static String trimTrailing(String text, String toTrim) { int count = countTrailing(text, toTrim); // substring will optimise the 0 case. return text.substring(0, text.length() - count); } // -------------------------- STATIC METHODS -------------------------- static { // initialize vt Vowel Table vt.set('A'); vt.set('E'); vt.set('I'); vt.set('O'); vt.set('U'); vt.set('a'); vt.set('e'); vt.set('i'); vt.set('o'); vt.set('u'); vt.set('\u00c0', '\u00c6'); vt.set('\u00c8', '\u00cf'); vt.set('\u00d2', '\u00d6'); vt.set('\u00d8', '\u00dc'); vt.set('\u00e0', '\u00e6'); vt.set('\u00e8', '\u00ef'); vt.set('\u00f2', '\u00f6'); vt.set('\u00f8', '\u00fc'); vt.set('\u0100', '\u0105'); vt.set('\u0112', '\u011b'); vt.set('\u0128', '\u012f'); vt.set('\u0130'); vt.set('\u0132', '\u0133'); vt.set('\u014c', '\u014f'); vt.set('\u0150', '\u0153'); vt.set('\u0168', '\u016f'); vt.set('\u0170', '\u0173'); } // --------------------------- CONSTRUCTORS --------------------------- /** * Dummy constructor StringTools contains only static methods. */ protected StringTools() { } // --------------------------- main() method --------------------------- /** * Test harness, used in debugging * * @param args not used */ public static void main(String[] args) { if (DEBUGGING) { System.out.println(">>condense"); System.out.println(condense(" this is spaced. ")); System.out.println(">> trimLeading"); System.out.println(trimLeading("*****t*r*i*m****", '*')); System.out.println(trimLeading(" trim ")); System.out.println(">> trimTrailing"); System.out.println(trimTrailing(" trim ")); System.out.println(trimTrailing("*****t*r*i*m****", '*')); System.out.println(">> chopLeadingString"); System.out.println(chopLeadingString("abcdefg", "abc")); System.out.println(">> chopTrailingString"); System.out.println(chopTrailingString("say!", "!")); System.out.println(">> toHexString"); System.out.println(toHexString(-3)); System.out.println(toHexString(3)); System.out.println(">> countLeading"); System.out.println(countLeading("none", ' ')); System.out.println(countLeading("*one***", '*')); System.out.println(countLeading(" abc ", " *")); System.out.println(countLeading(" * * abc ", " *")); System.out.println(countLeading(" * * abc ", "* ")); System.out.println(countLeading("\n\ntw\n\n\no\n\n\n\n", '\n')); System.out.println(">> countTrailing"); System.out.println(countTrailing("none", ' ')); System.out.println(countTrailing("***one*", '*')); System.out.println(countTrailing("\n\n\n\nt\n\n\n\nwo\n\n", '\n')); System.out.println(countTrailing(" abc * * ", " *")); System.out.println(countTrailing(" * * abc * * ", " *")); System.out.println(countTrailing(" * * abc * * ", "* ")); System.out.println(">> quoteSQL"); System.out.println(quoteSQL("Judy's Place")); System.out.println(">> parseLongPennies"); System.out.println(parseLongPennies("$5.00")); System.out.println(parseLongPennies("$50")); System.out.println(parseLongPennies("50")); System.out.println(parseLongPennies("$50-")); System.out.println(">> penniesToString"); System.out.println(penniesToString(0)); System.out.println(penniesToString(-1)); System.out.println(penniesToString(20)); System.out.println(penniesToString(302)); System.out.println(penniesToString(-100000)); System.out.println(">> toBookTitleCase"); System.out.println(toBookTitleCase("handbook to HIGHER consciousness")); System.out.println(toBookTitleCase("THE HISTORY OF THE U.S.A.")); System.out.println(toBookTitleCase("THE HISTORY OF THE USA")); System.out.println(">> rightPad"); System.out.println(rightPad("abc", 6, true) + "*"); System.out.println(rightPad("abc", 2, true) + "*"); System.out.println(rightPad("abc", 2, false) + "*"); System.out.println(rightPad("abc", 3, true) + "*"); System.out.println(rightPad("abc", 3, false) + "*"); System.out.println(rightPad("abc", 0, true) + "*"); System.out.println(rightPad("abc", 20, true) + "*"); System.out.println(rightPad("abc", 29, true) + "*"); System.out.println(rightPad("abc", 30, true) + "*"); System.out.println(rightPad("abc", 31, true) + "*"); System.out.println(rightPad("abc", 40, true) + "*"); System.out.println(">> toUpperCase"); System.out.println(toUpperCase('q')); System.out.println(toUpperCase('Q')); System.out.println(toUpperCase("The quick brown fox was 10 feet tall.")); System.out.println(toUpperCase("THE QUICK BROWN FOX WAS 10 FEET TALL.")); System.out.println(toUpperCase("the quick brown fox was 10 feet tall.")); System.out.println(">> toLowerCase"); System.out.println(toLowerCase('q')); System.out.println(toLowerCase('Q')); System.out.println(toLowerCase("The quick brown fox was 10 feet tall.")); System.out.println(toLowerCase("THE QUICK BROWN FOX WAS 10 FEET TALL.")); System.out.println(toLowerCase("the quick brown fox was 10 feet tall.")); System.out.println(">> countInstances"); System.out.println("count instances should be 4: " + countInstances(" abab abcdefgab", "ab")); } } }