//****************************************************************************** // // File: CoolString.java // Author: Jon Pugh // Home: http://www.seanet.com/~jonpugh/ // Date: February 14, 2000 // Version: 1.0.2 // //****************************************************************************** //package com.frostbitefalls.util; import java.io.*; /** * A mutable string class, suitable for slamming strings into and then munging * based on other strings. Insert, delete and replace operations are all supported, * as well as tokenization. Comparisons can be case sensitive or not. * * @author Jon Pugh * @version 1.0.2 */ public final class CoolString implements java.io.Serializable { protected char buffer[]; protected int count; protected boolean caseSensitive = false; /** * Construct an empty string. */ public CoolString() { buffer = new char[0]; count = 0; } /** * Construct a cool string from a Java String. */ public CoolString(String s) { set(s); } /** * Construct a cool string from a CoolString. */ public CoolString(CoolString s) { set(s); } /** * Construct a cool string from a char array. */ public CoolString(char[] s) { set(s); } /** * Construct a cool string from a file. */ public CoolString(File f) throws java.io.FileNotFoundException, java.io.IOException { set(f); } /** * Set this cool string from a file. */ public void set(File f) throws java.io.FileNotFoundException, java.io.IOException { FileInputStream fis = new FileInputStream(f); try { DataInputStream dis = new DataInputStream(fis); String temp = dis.readUTF(); set(temp); } finally { fis.close(); } } /** * Set this to a specified String, discarding any current string. */ public void set(String s) { buffer = s.toCharArray(); count = s.length(); } /** * Set this to a specified CoolString, discarding any current string. */ public void set(CoolString s) { buffer = s.toCharArray(); count = s.length(); } /** * Set this to a specified char array, discarding any current string. */ public void set(char[] s) { buffer = s; count = s.length; } /** * Convert to String. */ public String toString() { return String.valueOf(buffer); } /** * Return the raw char[] */ public char[] toCharArray() { char[] newBuffer = new char[count]; for ( int i = 0; i < count; i++ ) newBuffer[i] = buffer[i]; return newBuffer; } /** * Return the current string length. */ public int length() { return count; } /** * Return the offset to the first instance of the specified String. */ public int indexOf(String s) { return indexOf(s, 0); } /** * Return the offset to the specified String starting from the specified index. * Should replace this with Wirth's improved algorithm! */ public int indexOf(String s, int startingIndex) { int sCount = s.length(); // bail if substring is longer than ours if (sCount > count) return -1; // create temporary buffer char sub[] = s.toCharArray(); // search for offset int i = 0; int j = startingIndex; while ( i < sCount && j < count - sCount + 1 ) { i = 0; while ( i < sCount && equalChars(j+i, sub[i]) ) i++; if ( i < sCount ) j++; } // if we found all the chars from s, then j is the zero based offset if ( i == sCount ) return j; else return -1; } /** * Return the offset to the last instance of the specified String. */ public int lastIndexOf(String s) { return lastIndexOf(s, count); } /** * Return the offset to the specified String starting from the specified index. * Should replace this with Wirth's improved algorithm! */ public int lastIndexOf(String s, int startingIndex) { int sCount = s.length(); // bail if substring is longer than ours if (sCount > count) return -1; // create temporary buffer char sub[] = s.toCharArray(); // search for offset int i = 0; int j = Math.min(startingIndex, count - sCount); while ( i < sCount && j >= 0 ) { i = 0; while (i < sCount && equalChars(j+i, sub[i]) ) i++; if ( i < sCount ) --j; } // if we found all the chars from s, then j is the offset if ( i == sCount ) return j; else return -1; } /** * Set all comparisons to either consider or ignore case. When caseSensitive is true, then * case matters. Default is case insensitive (i.e. false). */ public void setCaseSensitive(boolean caseSensitive) { this.caseSensitive = caseSensitive; } /** * Return the case sensitive setting for this string. Default is case insensitive (i.e. false). */ public boolean isCaseSensitive() { return caseSensitive; } /** * Compare a character to a character by offset, using the caseSensitive setting. * @param loc The offset to the character in this CoolString. * @param c The character to compare against. */ public boolean equalChars(int index, char c) { if (index >= count) return false; else if (caseSensitive) return buffer[index] == c; else return Character.toLowerCase(buffer[index]) == Character.toLowerCase(c); } /** * Compare to a char array. */ public boolean equals(char[] c) { boolean same = c.length == count; int i = 0; while (same && i < count) { same = equalChars(i, c[i]); i++; } return same; } /** * Compare to anything by converting it to a string and then a char array. * This generates too much garbage though. */ public boolean equals(Object o) { char c[] = o.toString().toCharArray(); return equals(c); } /** * Is the substring present in this string? */ public boolean contains(String s) { return indexOf(s) != -1; } /** * Does the string start with the substring? */ public boolean startsWith(String s) { return indexOf(s) == 0; } /** * Does the string end with the substring */ public boolean endsWith(String s) { return lastIndexOf(s) == count - s.length(); } /** * Insert a string before the specified index. */ public void insertAt(int index, String insertThis) { // create temporaries int iCount = insertThis.length(); int newCount = count + iCount; char newBuffer[] = new char[ newCount ]; // copy first part of original string for (int i = 0; i < index; i++) newBuffer[i] = buffer[i]; // copy inserted string for (int i = 0; i < iCount; i++) newBuffer[index+i] = insertThis.charAt(i); // copy last part of original string for (int i = index; i < count; i++) newBuffer[iCount+i] = buffer[i]; // save new string buffer = newBuffer; count = newCount; } /** * Append a string to the end of this string. */ public void append(String appendThis) { insertAt(count, appendThis); } /** * Prepend a string in front of this string. */ public void prepend(String prependThis) { insertAt(0, prependThis); } /** * Insert a string before first instance of a string. */ public void insertBefore(String beforeThis, String insertThis) { int o = indexOf(beforeThis); if (o == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); insertAt(o, insertThis); } /** * Insert a string before last instance of a string. */ public void insertBeforeLast(String beforeThis, String insertThis) { int o = lastIndexOf(beforeThis); if (o == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); insertAt(o, insertThis); } /** * Insert a string after first instance of a string. */ public void insertAfter(String afterThis, String insertThis) { int o = indexOf(afterThis); if (o == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); insertAt(o+afterThis.length(), insertThis); } /** * Insert a string after last instance of a string. */ public void insertAfterLast(String afterThis, String insertThis) { int o = lastIndexOf(afterThis); if (o == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); insertAt(o+afterThis.length(), insertThis); } /** * Return the substring starting at beginIndex and extending to the end of the string. */ public String substring(int beginIndex) { return substring(beginIndex, count); } /** * Return the substring between the specified indicies. */ public String substring(int beginIndex, int endIndex) { int newSize = endIndex - beginIndex; if (newSize < 0) newSize = 0; char newBuffer[] = new char[ newSize ]; for (int i = 0; i < newSize; i++) newBuffer[i] = buffer[beginIndex + i]; return String.valueOf(newBuffer); } /** * Return the text which appears before the specified String. * If the String does not appear, then this returns "". */ public String before(String beforeThis) { int o = indexOf(beforeThis); if (o == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); return substring(0, o); } /** * Return the text which follows the specified String. * If the String does not appear, then this returns "". */ public String after(String afterThis) { int o = indexOf(afterThis); if (o == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); return substring(o+afterThis.length(), count); } /** * Return the string between the two specified tags. * If either tag is not present then "" is returned. */ public String between(String afterThis, String beforeThis) { int startingIndex = indexOf(afterThis); if (startingIndex == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); startingIndex += afterThis.length(); int endingIndex = indexOf(beforeThis, startingIndex); if (endingIndex == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); return substring(startingIndex, endingIndex); } /** * Replace the first occurance of a string with another string. * Special case replacements of same length and other "in buffer" operations? */ public void replaceFirst(String replaceThis, String withThis) { int o = indexOf(replaceThis); if (o == -1) throw new RuntimeException("String \"" + replaceThis + "\" not found" ); // create temporaries int rCount = replaceThis.length(); int wCount = withThis.length(); int nCount = count - rCount + wCount; char newBuffer[] = new char[nCount]; char temp[] = withThis.toCharArray(); // copy first part for (int i = 0; i < o; i++) newBuffer[i] = buffer[i]; // copy withThis for (int j = 0; j < wCount; j++) newBuffer[j + o] = temp[j]; // copy last part for (int k = o + rCount; k < count; k++) newBuffer[k + wCount - rCount] = buffer[k]; // save new string buffer = newBuffer; count = nCount; } /** * Replace the last occurance of a string with another string. */ public void replaceLast(String replaceThis, String withThis) { int o = lastIndexOf(replaceThis); if (o == -1) throw new RuntimeException("String \"" + replaceThis + "\" not found" ); // create temporaries int rCount = replaceThis.length(); int wCount = withThis.length(); int nCount = count - rCount + wCount; char newBuffer[] = new char[nCount]; char temp[] = withThis.toCharArray(); // copy first part for (int i = 0; i < o; i++) newBuffer[i] = buffer[i]; // copy withThis for (int j = 0; j < wCount; j++) newBuffer[j + o] = temp[j]; // copy last part for (int k = o + rCount; k < count; k++) newBuffer[k + wCount - rCount] = buffer[k]; // save new string buffer = newBuffer; count = nCount; } /** * Replace all instances of a string with another string. */ public void replaceAll(String replaceThis, String withThis) { int o; int startAt = 0; while ((o = indexOf(replaceThis, startAt)) != -1) { // create temporaries int rCount = replaceThis.length(); int wCount = withThis.length(); int nCount = count - rCount + wCount; char newBuffer[] = new char[nCount]; char temp[] = withThis.toCharArray(); // copy first part for (int i = 0; i < o; i++) newBuffer[i] = buffer[i]; // copy withThis for (int j = 0; j < wCount; j++) newBuffer[j + o] = temp[j]; // copy last part for (int k = o + rCount; k < count; k++) newBuffer[k + wCount - rCount] = buffer[k]; // set new starting point for next offset search startAt = o + wCount; // save new string buffer = newBuffer; count = nCount; } } /** * Replace the text between two strings with another string. */ public void replaceBetween(String afterThis, String beforeThis, String withThis) { int startingIndex = indexOf(afterThis); if (startingIndex == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); startingIndex += afterThis.length(); int endingIndex = indexOf(beforeThis, startingIndex); if (endingIndex == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); // create temporaries int rCount = endingIndex - startingIndex; int wCount = withThis.length(); int nCount = count - rCount + wCount; char newBuffer[] = new char[nCount]; char temp[] = withThis.toCharArray(); // copy first part for (int i = 0; i < startingIndex; i++) newBuffer[i] = buffer[i]; // copy withThis for (int j = 0; j < wCount; j++) newBuffer[j + startingIndex] = temp[j]; // copy last part for (int k = startingIndex + rCount; k < count; k++) newBuffer[k + wCount - rCount] = buffer[k]; // save new string buffer = newBuffer; count = nCount; } /** * Delete a specified range of characters. */ public void delete(int startingIndex, int endingIndex) { // examine error conditions if (startingIndex > count) throw new RuntimeException( "Bad startingIndex specified." ); if (endingIndex > count) throw new RuntimeException( "Bad endingIndex specified." ); // ignore negative ranges - don't throw if (startingIndex > endingIndex) return; // create temporaries int dCount = endingIndex - startingIndex + 1; char newBuffer[] = new char[count-dCount]; // copy first part for (int i = 0; i < startingIndex; i++) newBuffer[i] = buffer[i]; // copy last part for (int i = endingIndex + 1; i < count; i++) newBuffer[i-dCount] = buffer[i]; // save new string buffer = newBuffer; count -= dCount; } /** * Delete the first occurance of a substring. */ public void deleteFirst(String deleteThis) { replaceFirst(deleteThis, ""); } /** * Delete the last occurance of the specified string. */ public void deleteLast(String deleteThis) { replaceLast(deleteThis, ""); } /** * Delete every occurance of the specified string. */ public void deleteAll(String deleteThis) { replaceAll(deleteThis, ""); } /** * Delete everything before the specified string. */ public void deleteBefore(String beforeThis) { int o = indexOf(beforeThis); if (o == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); delete(0, o - 1); } /** * Delete everything after the specified string. */ public void deleteAfter(String afterThis) { int o = indexOf(afterThis); if (o == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); delete(o + afterThis.length(), count - 1); } /** * Delete the string between two strings. */ public void deleteBetween(String afterThis, String beforeThis) { int startingIndex = indexOf(afterThis); if (startingIndex == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); startingIndex += afterThis.length(); int endingIndex = indexOf(beforeThis, startingIndex); if (endingIndex == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); delete(startingIndex, endingIndex-1); } /** * Keep on the text before a string (or delete the string and everything after it). */ public void keepBefore(String beforeThis) { int o = indexOf(beforeThis); if (o == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); delete(o, count-1); } /** * Keep the text after a string (or delete the string and everything before it). */ public void keepAfter(String afterThis) { int o = indexOf(afterThis); if (o == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); delete(0, o + afterThis.length() - 1); } /** * Keep only the text between two strings. */ public void keepBetween(String afterThis, String beforeThis) { int startingIndex = indexOf(afterThis); if (startingIndex == -1) throw new RuntimeException("String \"" + afterThis + "\" not found" ); startingIndex += afterThis.length(); int endingIndex = indexOf(beforeThis, startingIndex); if (endingIndex == -1) throw new RuntimeException("String \"" + beforeThis + "\" not found" ); // create temporaries int nCount = endingIndex - startingIndex; char newBuffer[] = new char[ nCount ]; // copy characters which remain for (int i = startingIndex; i < endingIndex; i++) newBuffer[i - startingIndex] = buffer[i]; // save new buffer count = nCount; buffer = newBuffer; } /** * Return the number of strings (tokens) seperated by the specified token. */ public int countTokens(String token) { // count the number of tokens int tLength = token.length(); int tCount = 0; int startAt = 0; while (true) { int o = indexOf(token, startAt); if (o == -1) break; tCount++; startAt = o + tLength; } return tCount+1; } /** * Return an array of strings seperated by the specified token. * The token will not appear in the strings. */ public String[] tokens(String token) { int tCount = countTokens(token); // create array for tokens String tokens[] = new String[tCount]; // make a temporary CoolString to peel apart CoolString temp = new CoolString(this); // get all the substrings for (int j = 0; j < tCount; j++) { // the last token won't have a token terminator if (j == tCount-1) tokens[j] = temp.toString(); else { tokens[j] = temp.before(token); temp.keepAfter(token); } } return tokens; } /** * Return the string before the first token. */ public String firstToken(String token) { return before(token); } /** * Return the string after the last token. */ public String lastToken(String token) { int o = lastIndexOf(token); return substring(o+1, count); } /** * Return the Nth string from a tokenized string. N is zero based. */ public String NthToken(String token, int n) { String[] temp = tokens(token); if (n >= temp.length) throw new RuntimeException("Only " + temp.length + " tokens present (try a zero based index)." ); return temp[n]; } }