/* * @(#)String.java 1.60 96/03/28 * * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. Please refer to the file "copyright.html" * for further important copyright and licensing information. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ package java.lang; import java.util.Hashtable; /** * A general class of objects to represent character Strings. * Strings are constant, their values cannot be changed after creation. * The compiler makes sure that each String constant actually results * in a String object. Because String objects are immutable they can * be shared. For example: *
* String str = "abc"; ** is equivalent to: *
* char data[] = {'a', 'b', 'c'}; * String str = new String(data); ** Here are some more examples of how strings can be used: *
* System.out.println("abc"); * String cde = "cde"; * System.out.println("abc" + cde); * String c = "abc".substring(2,3); * String d = cde.substring(1, 2); ** @see StringBuffer * @version 1.60, 28 Mar 1996 * @author Lee Boynton * @author Arthur van Hoff */ public final class String { /** The value is used for character storage. */ private char value[]; /** The offset is the first index of the storage that is used. */ private int offset; /** The count is the number of characters in the String. */ private int count; /** * Constructs a new empty String. */ public String() { value = new char[0]; } /** * Constructs a new String that is a copy of the specified String. * @param value the initial value of the String */ public String(String value) { count = value.length(); this.value = new char[count]; value.getChars(0, count, this.value, 0); } /** * Constructs a new String whose initial value is the specified array * of characters. * @param value the initial value of the String */ public String(char value[]) { this.count = value.length; this.value = new char[count]; System.arraycopy(value, 0, this.value, 0, count); } /** * Constructs a new String whose initial value is the specified sub array of characters. * The length of the new string will be count characters * starting at offset within the specified character array. * @param value the initial value of the String, an array of characters * @param offset the offset into the value of the String * @param count the length of the value of the String * @exception StringIndexOutOfBoundsException If the offset and count arguments are invalid. */ public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } if (offset + count > value.length) { throw new StringIndexOutOfBoundsException(offset + count); } this.value = new char[count]; this.count = count; System.arraycopy(value, offset, this.value, 0, count); } /** * Constructs a new String whose initial value is the specified subarray of bytes. * The high-byte of each character can be specified, it should usually be 0. * The length of the new String will be count characters * starting at offset within the specified character array. * @param ascii the bytes that will be converted to characters * @param hibyte the high byte of each Unicode character * @param offset the offset into the ascii array * @param count the length of the String * @exception StringIndexOutOfBoundsException If the offset and count arguments are invalid. */ public String(byte ascii[], int hibyte, int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } if (offset + count > ascii.length) { throw new StringIndexOutOfBoundsException(offset + count); } char value[] = new char[count]; this.count = count; this.value = value; if (hibyte == 0) { for (int i = count ; i-- > 0 ;) { value[i] = (char) (ascii[i + offset] & 0xff); } } else { hibyte <<= 8; for (int i = count ; i-- > 0 ;) { value[i] = (char) (hibyte | (ascii[i + offset] & 0xff)); } } } /** * Constructs a new String whose value is the specified array of bytes. * The byte array is transformed into Unicode chars using hibyte * as the upper byte of each character. * @param ascii the byte that will be converted to characters * @param hibyte the top 8 bits of each 16 bit Unicode character */ public String(byte ascii[], int hibyte) { this(ascii, hibyte, 0, ascii.length); } /** * Construct a new string whose value is the current contents of the * given string buffer * @param buffer the stringbuffer to be converted */ public String (StringBuffer buffer) { synchronized(buffer) { buffer.setShared(); this.value = buffer.getValue(); this.offset = 0; this.count = buffer.length(); } } // Private constructor which shares value array for speed. private String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; } /** * Returns the length of the String. * The length of the String is equal to the number of 16 bit * Unicode characters in the String. */ public int length() { return count; } /** * Returns the character at the specified index. An index ranges * from 0 to length() - 1. * @param index the index of the desired character * @exception StringIndexOutOfBoundsException If the index is not * in the range 0 to length()-1. */ public char charAt(int index) { if ((index < 0) || (index >= count)) { throw new StringIndexOutOfBoundsException(index); } return value[index + offset]; } /** * Copies characters from this String into the specified character array. * The characters of the specified substring (determined by * srcBegin and srcEnd) are copied into the character array, * starting at the array's dstBegin location. * @param srcBegin index of the first character in the string * @param srcEnd end of the characters that are copied * @param dst the destination array * @param dstBegin the start offset in the destination array */ public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { System.arraycopy(value, offset + srcBegin, dst, dstBegin, srcEnd - srcBegin); } /** * Copies characters from this String into the specified byte array. * Copies the characters of the specified substring (determined by * srcBegin and srcEnd) into the byte array, starting at the * array's dstBegin location. * @param srcBegin index of the first character in the String * @param srcEnd end of the characters that are copied * @param dst the destination array * @param dstBegin the start offset in the destination array */ public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) { int j = dstBegin; int n = offset + srcEnd; int i = offset + srcBegin; while (i < n) { dst[j++] = (byte)value[i++]; } } /** * Compares this String to the specified object. * Returns true if the object is equal to this String; that is, * has the same length and the same characters in the same sequence. * @param anObject the object to compare this String against * @return true if the Strings are equal; false otherwise. */ public boolean equals(Object anObject) { if ((anObject != null) && (anObject instanceof String)) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value;; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) { return false; } } return true; } } return false; } /** * Compares this String to another object. * Returns true if the object is equal to this String; that is, * has the same length and the same characters in the same sequence. * Upper case characters are folded to lower case before * they are compared. * @param anotherString the String to compare this String against * @return true if the Strings are equal, ignoring case; false otherwise. */ public boolean equalsIgnoreCase(String anotherString) { return (anotherString != null) && (anotherString.count == count) && regionMatches(true, 0, anotherString, 0, count); } /** * Compares this String to another specified String. * Returns an integer that is less than, equal to, or greater than zero. * The integer's value depends on whether this String is less than, equal to, or greater * than anotherString. * @param anotherString the String to be compared */ public int compareTo(String anotherString) { int len1 = count; int len2 = anotherString.count; int n = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { char c1 = v1[i++]; char c2 = v2[j++]; if (c1 != c2) { return c1 - c2; } } return len1 - len2; } /** * Determines whether a region of this String matches the specified region * of the specified String. * @param toffset where to start looking in this String * @param other the other String * @param ooffset where to start looking in the other String * @param len the number of characters to compare * @return true if the region matches with the other; false otherwise. */ public boolean regionMatches(int toffset, String other, int ooffset, int len) { char ta[] = value; int to = offset + toffset; int tlim = offset + count; char pa[] = other.value; int po = other.offset + ooffset; int plim = po + other.count; if ((ooffset < 0) || (toffset < 0) || (to + len > tlim) || (po + len > plim)) { return false; } while (len-- > 0) { if (ta[to++] != pa[po++]) { return false; } } return true; } /** * Determines whether a region of this String matches the specified region * of the specified String. If the boolean ignoreCase is true, upper case characters are * considered equivalent to lower case letters. * @param ignoreCase if true, case is ignored * @param toffset where to start looking in this String * @param other the other String * @param ooffset where to start looking in the other String * @param len the number of characters to compare * @return true if the region matches with the other; false otherwise. */ public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { char ta[] = value; int to = offset + toffset; int tlim = offset + count; char pa[] = other.value; int po = other.offset + ooffset; int plim = po + other.count; if ((ooffset < 0) || (toffset < 0) || (to + len > tlim) || (po + len > plim)) { return false; } while (len-- > 0) { char c1 = ta[to++]; char c2 = pa[po++]; if (c1 == c2) continue; if (ignoreCase) { // If characters don't match but case may be ignored, // try converting both characters to uppercase. // If the results match, then the comparison scan should // continue. char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); if (u1 == u2) continue; // Unfortunately, conversion to uppercase does not work properly // for the Georgian alphabet, which has strange rules about case // conversion. So we need to make one last check before // exiting. if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) continue; } return false; } return true; } /** * Determines whether this String starts with some prefix. * @param prefix the prefix * @param toffset where to begin looking in the the String * @return true if the String starts with the specified prefix; false otherwise. */ public boolean startsWith(String prefix, int toffset) { char ta[] = value; int to = offset + toffset; int tlim = offset + count; char pa[] = prefix.value; int po = prefix.offset; int pc = prefix.count; int plim = po + pc; if ((toffset < 0) || (to + pc > tlim)) { return false; } while (--pc >= 0) { if (ta[to++] != pa[po++]) { return false; } } return true; } /** * Determines whether this String starts with some prefix. * @param prefix the prefix * @return true if the String starts with the specified prefix; false otherwise. */ public boolean startsWith(String prefix) { return startsWith(prefix, 0); } /** * Determines whether the String ends with some suffix. * @param suffix the suffix * @return true if the String ends with the specified suffix; false otherwise. */ public boolean endsWith(String suffix) { return startsWith(suffix, count - suffix.count); } /** * Returns a hashcode for this String. This is a large * number composed of the character values in the String. */ public int hashCode() { int h = 0; int off = offset; char val[] = value; int len = count; if (len < 16) { for (int i = len ; i > 0; i--) { h = (h * 37) + val[off++]; } } else { // only sample some characters int skip = len / 8; for (int i = len ; i > 0; i -= skip, off += skip) { h = (h * 39) + val[off]; } } return h; } /** * Returns the index within this String of the first occurrence of the specified * character. This method returns -1 if the index is not found. * @param ch the character to search for */ public int indexOf(int ch) { return indexOf(ch, 0); } /** * Returns the index within this String of the first occurrence of the specified * character, starting the search at fromIndex. This method * returns -1 if the index is not found. * @param ch the character to search for * @param fromIndex the index to start the search from */ public int indexOf(int ch, int fromIndex) { int max = offset + count; char v[] = value; for (int i = offset + fromIndex ; i < max ; i++) { if (v[i] == ch) { return i - offset; } } return -1; } /** * Returns the index within this String of the last occurrence of the specified character. * The String is searched backwards starting at the last character. * This method returns -1 if the index is not found. * @param ch the character to search for */ public int lastIndexOf(int ch) { return lastIndexOf(ch, count - 1); } /** * Returns the index within this String of the last occurrence of the specified character. * The String is searched backwards starting at fromIndex. * This method returns -1 if the index is not found. * @param ch the character to search for * @param fromIndex the index to start the search from */ public int lastIndexOf(int ch, int fromIndex) { int min = offset; char v[] = value; for (int i = offset + ((fromIndex >= count) ? count - 1 : fromIndex) ; i >= min ; i--) { if (v[i] == ch) { return i - offset; } } return -1; } /** * Returns the index within this String of the first occurrence of the specified substring. * This method returns -1 if the index is not found. * @param str the substring to search for */ public int indexOf(String str) { return indexOf(str, 0); } /** * Returns the index within this String of the first occurrence of the specified substring. * The search is started at fromIndex. * This method returns -1 if the index is not found. * @param str the substring to search for * @param fromIndex the index to start the search from */ public int indexOf(String str, int fromIndex) { char v1[] = value; char v2[] = str.value; int max = offset + (count - str.count); test: for (int i = offset + ((fromIndex < 0) ? 0 : fromIndex); i <= max ; i++) { int n = str.count; int j = i; int k = str.offset; while (n-- != 0) { if (v1[j++] != v2[k++]) { continue test; } } return i - offset; } return -1; } /** * Returns the index within this String of the rightmost occurrence * of the specified substring. The rightmost empty string "" is * considered to occur at the index value this.length(). * This method returns -1 if the index is not found. * @param str the substring to search for */ public int lastIndexOf(String str) { return lastIndexOf(str, count); } /** * Returns the index within this String of the last occurrence of * the specified substring. * The returned index indicates the start of the substring, and it * must be equal to or less than fromIndex. * This method returns -1 if the index is not found. * @param str the substring to search for * @param fromIndex the index to start the search from */ public int lastIndexOf(String str, int fromIndex) { /* Check arguments; return immediately where possible. * Deliberately not checking for null str, to be consistent with * with other String methods. */ if (fromIndex < 0) { return -1; } else if (fromIndex > count - str.count) { fromIndex = count - str.count; } /* Empty string always matches. */ if (str.count == 0) { return fromIndex; } /* Find the rightmost substring match. */ char v1[] = value; char v2[] = str.value; for (int i = offset + fromIndex; i >= offset; --i) { int n = str.count; int thisIndex = i; int strIndex = str.offset; while (v1[thisIndex++] == v2[strIndex++]) { if (--n <= 0) { return i - offset; } } } return -1; } /** * Returns the substring of this String. The substring is specified * by a beginIndex (inclusive) and the end of the string. * @param beginIndex the beginning index, inclusive */ public String substring(int beginIndex) { return substring(beginIndex, length()); } /** * Returns the substring of a String. The substring is specified * by a beginIndex (inclusive) and an endIndex (exclusive). * @param beginIndex the beginning index, inclusive * @param endIndex the ending index, exclusive * @exception StringIndexOutOfBoundsException If the beginIndex or the endIndex is out * of range. */ public String substring(int beginIndex, int endIndex) { if (beginIndex > endIndex) { int tmp = beginIndex; beginIndex = endIndex; endIndex = tmp; } if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > count) { throw new StringIndexOutOfBoundsException(endIndex); } return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); } /** * Concatenates the specified string to the end of this String and * returns a new String object representing the concatenation. * @param str the String which is concatenated to the end of this String * @return the concatenated String */ public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } char buf[] = new char[count + otherLen]; getChars(0, count, buf, 0); str.getChars(0, otherLen, buf, count); return new String(0, count + otherLen, buf); } /** * Converts this String by replacing all occurences of oldChar with newChar. * @param oldChar the old character * @param newChar the new character */ public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = count; int i = -1; while (++i < len) { if (value[offset + i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0 ; j < i ; j++) { buf[j] = value[offset+j]; } while (i < len) { char c = value[offset + i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(0, len, buf); } } return this; } /** * Converts all of the characters in this String to lower case. * @return the String, converted to lowercase. * @see Character#toLowerCase * @see String#toUpperCase */ public String toLowerCase() { int len = count; int i, j; char z; scan: { for (i = 0 ; i < len ; i++) { char c = value[offset+i]; z = Character.toLowerCase(c); if (c != z) break scan; } return this; } char buf[] = new char[len]; for (j = 0; j < i; ++j) { buf[j] = value[offset+j]; } buf[j++] = z; for (; j < len ; ++j) { buf[j] = Character.toLowerCase(value[offset+j]); } return new String(0, len, buf); } /** * Converts all of the characters in this String to upper case. * @return the String, converted to uppercase. * @see Character#toUpperCase * @see String#toLowerCase */ public String toUpperCase() { int len = count; int i, j; char z; scan: { for (i = 0 ; i < len ; i++) { char c = value[offset+i]; z = Character.toUpperCase(c); if (c != z) break scan; } return this; } char buf[] = new char[len]; for (j = 0; j < i; ++j) { buf[j] = value[offset+j]; } buf[j++] = z; for (; j < len ; ++j) { buf[j] = Character.toUpperCase(value[offset+j]); } return new String(0, len, buf); } /** * Trims leading and trailing whitespace from this String. * @return the String, with whitespace removed. */ public String trim() { int len = count; int st = 0; while ((st < len) && (value[offset + st] <= ' ')) { st++; } while ((st < len) && (value[offset + len - 1] <= ' ')) { len--; } return ((st > 0) || (len < count)) ? substring(st, len) : this; } /** * Converts the object (in this case already a String) to a * String. Otherwise this class would inherit class Object's * version of tostring(). * @return the String itself. */ public String toString() { return this; } /** * Converts this String to a character array. This creates a new array. * @return an array of characters. */ public char[] toCharArray() { int i, max = length(); char result[] = new char[max]; getChars(0, max, result, 0); return result; } /** * Returns a String that represents the String value of the object. * The object may choose how to represent itself by implementing * the toString() method. * @param obj the object to be converted */ public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); } /** * Returns a String that is equivalent to the specified character array. * Uses the original array as the body of the String (ie. it does not * copy it to a new array). * @param data the character array */ public static String valueOf(char data[]) { return new String(data); } /** * Returns a String that is equivalent to the specified character array. * @param data the character array * @param offset the offset into the value of the String * @param count the length of the value of the String */ public static String valueOf(char data[], int offset, int count) { return new String(data, offset, count); } /** * Returns a String that is equivalent to the specified character array. * It creates a new array and copies the characters into it. * @param data the character array * @param offset the offset into the value of the String * @param count the length of the value of the String */ public static String copyValueOf(char data[], int offset, int count) { // All public String constructors now copy the data. return new String(data, offset, count); } /** * Returns a String that is equivalent to the specified character array. * It creates a new array and copies the characters into it. * @param data the character array */ public static String copyValueOf(char data[]) { return copyValueOf(data, 0, data.length); } /** * Returns a String object that represents the state of the specified boolean. * @param b the boolean */ public static String valueOf(boolean b) { return b ? "true" : "false"; } /** * Returns a String object that contains a single character * @param c the character * @return the resulting String. */ public static String valueOf(char c) { char data[] = {c}; return new String(0, 1, data); } /** * Returns a String object that represents the value of the specified integer. * @param i the integer */ public static String valueOf(int i) { return Integer.toString(i, 10); } /** * Returns a String object that represents the value of the specified long. * @param l the long */ public static String valueOf(long l) { return Long.toString(l, 10); } /** * Returns a String object that represents the value of the specified float. * @param f the float */ public static String valueOf(float f) { return Float.toString(f); } /** * Returns a String object that represents the value of the specified double. * @param d the double */ public static String valueOf(double d) { return Double.toString(d); } /** * The set of internalized Strings. */ private static Hashtable InternSet; /** * Returns a String that is equal to this String * but which is guaranteed to be from the unique String pool. For example: *
s1.intern() == s2.intern() <=> s1.equals(s2).*/ public String intern() { if (InternSet == null) { InternSet = new Hashtable(); } String s = (String) InternSet.get(this); if (s != null) { return s; } InternSet.put(this, this); return this; } /** * Computes the length of this string's UTF encoded form. */ int utfLength() { int limit = offset + count; int utflen = 0; for (int i = offset; i < limit; i++) { int c = value[i]; if ((c >= 0x0001) && (c <= 0x007F)) { utflen++; } else if (c > 0x07FF) { utflen += 3; } else { utflen += 2; } } return utflen; } }