38 |
|
|
39 |
package java.lang; |
package java.lang; |
40 |
|
|
41 |
|
import java.lang.ref.WeakReference; |
42 |
import java.util.Comparator; |
import java.util.Comparator; |
43 |
import java.util.Hashtable; |
import java.util.WeakHashMap; |
44 |
import java.util.Locale; |
import java.util.Locale; |
45 |
import java.util.regex.Pattern; |
import java.util.regex.Pattern; |
|
import gnu.java.io.EncodingManager; |
|
46 |
import java.io.Serializable; |
import java.io.Serializable; |
47 |
import java.io.UnsupportedEncodingException; |
import java.io.UnsupportedEncodingException; |
48 |
import java.io.CharConversionException; |
import java.io.CharConversionException; |
49 |
|
import gnu.java.io.EncodingManager; |
50 |
|
import gnu.java.lang.CharData; |
51 |
|
|
52 |
/** |
/** |
53 |
* Strings represent an immutable set of characters. All String literals |
* Strings represent an immutable set of characters. All String literals |
72 |
* @author Paul N. Fisher |
* @author Paul N. Fisher |
73 |
* @author Eric Blake <ebb9@email.byu.edu> |
* @author Eric Blake <ebb9@email.byu.edu> |
74 |
* @since 1.0 |
* @since 1.0 |
75 |
* @status updated to 1.3, waiting on java.util.regex, and could use better |
* @status updated to 1.4; but could use better data sharing via offset field |
|
* data sharing |
|
76 |
*/ |
*/ |
77 |
public final class String implements Serializable, Comparable, CharSequence |
public final class String implements Serializable, Comparable, CharSequence |
78 |
{ |
{ |
83 |
private static final long serialVersionUID = -6849794470754667710L; |
private static final long serialVersionUID = -6849794470754667710L; |
84 |
|
|
85 |
/** |
/** |
86 |
* Holds the references for each intern()'d String. |
* Holds the references for each intern()'d String. If all references to |
87 |
* Once a String has been intern()'d it cannot be GC'd. |
* the string disappear, and the VM properly supports weak references, |
88 |
* |
* the String will be GC'd. |
89 |
* @XXX Replace with a weak reference structure for 1.2 |
*/ |
90 |
|
private static final WeakHashMap internTable = new WeakHashMap(); |
91 |
|
|
92 |
|
/** |
93 |
|
* Stores unicode multi-character uppercase expansion table. |
94 |
|
* @see #toUpperCase(char) |
95 |
|
* @see CharData#UPPER_EXPAND |
96 |
*/ |
*/ |
97 |
private static final Hashtable internTable = new Hashtable(); |
private static final char[] upperExpand = CharData.UPPER_EXPAND.value; |
98 |
|
|
99 |
|
/** |
100 |
|
* Stores unicode multi-character uppercase special casing table. |
101 |
|
* @see #upperCaseExpansion(char) |
102 |
|
* @see CharData#UPPER_SPECIAL |
103 |
|
*/ |
104 |
|
private static final char[] upperSpecial = CharData.UPPER_SPECIAL.value; |
105 |
|
|
106 |
/** |
/** |
107 |
* Characters which make up the String. |
* Characters which make up the String. |
200 |
// Since Strings are immutable, don't copy value. |
// Since Strings are immutable, don't copy value. |
201 |
value = str.value; |
value = str.value; |
202 |
count = str.count; |
count = str.count; |
203 |
|
cachedHashCode = str.cachedHashCode; |
204 |
} |
} |
205 |
|
|
206 |
/** |
/** |
268 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
269 |
this.count = count; |
this.count = count; |
270 |
value = new char[count]; |
value = new char[count]; |
271 |
for (int i = 0; i < count; i++) |
hibyte <<= 8; |
272 |
value[i] = (char) (((hibyte & 0xff) << 8) | (ascii[i + offset] & 0xff)); |
offset += count; |
273 |
|
while (count > 0) |
274 |
|
value[--count] = (char) (hibyte | (ascii[--offset] & 0xff)); |
275 |
} |
} |
276 |
|
|
277 |
/** |
/** |
322 |
public String(byte[] data, int offset, int count, String encoding) |
public String(byte[] data, int offset, int count, String encoding) |
323 |
throws UnsupportedEncodingException |
throws UnsupportedEncodingException |
324 |
{ |
{ |
|
// XXX Sun checks encoding for null, then checks negative bounds, then |
|
|
// checks data for null, and finally searches for encoding. |
|
325 |
if (offset < 0 || count < 0 || offset + count > data.length) |
if (offset < 0 || count < 0 || offset + count > data.length) |
326 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
327 |
try |
try |
328 |
{ |
{ |
329 |
|
// XXX Consider using java.nio here. |
330 |
value = EncodingManager.getDecoder(encoding) |
value = EncodingManager.getDecoder(encoding) |
331 |
.convertToChars(data, offset, count); |
.convertToChars(data, offset, count); |
332 |
} |
} |
384 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
385 |
try |
try |
386 |
{ |
{ |
387 |
|
// XXX Consider using java.nio here. |
388 |
value = EncodingManager.getDecoder() |
value = EncodingManager.getDecoder() |
389 |
.convertToChars(data, offset, count); |
.convertToChars(data, offset, count); |
390 |
} |
} |
424 |
*/ |
*/ |
425 |
public String(StringBuffer buf) |
public String(StringBuffer buf) |
426 |
{ |
{ |
427 |
// XXX Synchronize on buf. |
synchronized (buf) |
428 |
count = buf.length(); |
{ |
429 |
value = new char[count]; |
count = buf.count; |
430 |
buf.getChars(0, buf.length(), value, 0); |
// Share unless buf is 3/4 empty. |
431 |
|
if ((count << 2) < buf.value.length) |
432 |
|
{ |
433 |
|
value = new char[count]; |
434 |
|
System.arraycopy(buf.value, 0, value, 0, count); |
435 |
|
} |
436 |
|
else |
437 |
|
{ |
438 |
|
buf.shared = true; |
439 |
|
value = buf.value; |
440 |
|
} |
441 |
|
} |
442 |
} |
} |
443 |
|
|
444 |
/** |
/** |
445 |
* Special constructor used when data can safely be shared, rather than |
* Special constructor used when data can safely be shared, rather than |
446 |
* cloning it now (such as from StringBuffer). |
* cloning it. |
447 |
* |
* |
448 |
* @param data the characters, not modifiable by users, non-null |
* @param data the characters, not modifiable by users, non-null |
449 |
* @param length number of characters in data |
* @param length number of characters in data |
490 |
* @param dstBegin index to start writing characters into dst |
* @param dstBegin index to start writing characters into dst |
491 |
* @throws NullPointerException if dst is null |
* @throws NullPointerException if dst is null |
492 |
* @throws IndexOutOfBoundsException if any indices are out of bounds |
* @throws IndexOutOfBoundsException if any indices are out of bounds |
493 |
* (while unspecified, this is a StringIndexOutOfBoundsException) |
* (while unspecified, source problems cause a |
494 |
|
* StringIndexOutOfBoundsException, and dst problems cause an |
495 |
|
* ArrayIndexOutOfBoundsException) |
496 |
*/ |
*/ |
497 |
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) |
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) |
498 |
{ |
{ |
499 |
if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count |
if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count) |
|
|| dstBegin < 0 || dstBegin + srcEnd - srcBegin > dst.length) |
|
500 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
501 |
// XXX System.arraycopy this. |
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); |
|
for (int i = srcBegin; i < srcEnd; i++) |
|
|
dst[dstBegin + i - srcBegin] = value[i]; |
|
502 |
} |
} |
503 |
|
|
504 |
/** |
/** |
510 |
* @param srcEnd index after the last character to be copied from this String |
* @param srcEnd index after the last character to be copied from this String |
511 |
* @param dst byte array which each low byte of this String is copied into |
* @param dst byte array which each low byte of this String is copied into |
512 |
* @param dstBegin index to start writing characters into dst |
* @param dstBegin index to start writing characters into dst |
513 |
* @throws NullPointerException if dst is null |
* @throws NullPointerException if dst is null and copy length is non-zero |
514 |
* @throws IndexOutOfBoundsException if any indices are out of bounds |
* @throws IndexOutOfBoundsException if any indices are out of bounds |
515 |
* (while unspecified, this is a StringIndexOutOfBoundsException) |
* (while unspecified, source problems cause a |
516 |
|
* StringIndexOutOfBoundsException, and dst problems cause an |
517 |
|
* ArrayIndexOutOfBoundsException) |
518 |
* @see #getBytes() |
* @see #getBytes() |
519 |
* @see #getBytes(String) |
* @see #getBytes(String) |
520 |
* @deprecated use {@link #getBytes()}, which uses a char to byte encoder |
* @deprecated use {@link #getBytes()}, which uses a char to byte encoder |
521 |
*/ |
*/ |
522 |
public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) |
public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) |
523 |
{ |
{ |
524 |
if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count || |
if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > count) |
|
dstBegin < 0 || dstBegin + srcEnd - srcBegin > dst.length) |
|
525 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
526 |
for (int i = srcBegin; i < srcEnd; i++) |
dstBegin -= srcBegin; |
527 |
dst[dstBegin + i - srcBegin] = (byte) value[i]; |
while (srcBegin < srcEnd) |
528 |
|
dst[dstBegin++] = (byte) value[srcBegin++]; |
529 |
} |
} |
530 |
|
|
531 |
/** |
/** |
546 |
{ |
{ |
547 |
try |
try |
548 |
{ |
{ |
549 |
|
// XXX Consider using java.nio here. |
550 |
return EncodingManager.getEncoder(enc) |
return EncodingManager.getEncoder(enc) |
551 |
.convertToBytes(value, offset, count); |
.convertToBytes(value, offset, count); |
552 |
} |
} |
563 |
* {@link java.nio.charset.CharsetEncoder}. The behavior is not specified if |
* {@link java.nio.charset.CharsetEncoder}. The behavior is not specified if |
564 |
* the encoder encounters a problem; this implementation returns null. |
* the encoder encounters a problem; this implementation returns null. |
565 |
* |
* |
|
* @param enc encoding name |
|
566 |
* @return the resulting byte array, or null on a problem |
* @return the resulting byte array, or null on a problem |
|
* @throws NullPointerException if enc is null |
|
|
* @throws UnsupportedEncodingException if encoding is not supported |
|
567 |
* @since 1.1 |
* @since 1.1 |
568 |
*/ |
*/ |
569 |
public byte[] getBytes() |
public byte[] getBytes() |
570 |
{ |
{ |
571 |
try |
try |
572 |
{ |
{ |
573 |
|
// XXX Consider using java.nio here. |
574 |
return EncodingManager.getEncoder() |
return EncodingManager.getEncoder() |
575 |
.convertToBytes(value, offset, count); |
.convertToBytes(value, offset, count); |
576 |
} |
} |
596 |
String str2 = (String) anObject; |
String str2 = (String) anObject; |
597 |
if (count != str2.count) |
if (count != str2.count) |
598 |
return false; |
return false; |
599 |
for (int i = 0; i < count; i++) |
if (value == str2.value) |
600 |
|
return true; |
601 |
|
int i = count; |
602 |
|
while (--i >= 0) |
603 |
if (value[i] != str2.value[i]) |
if (value[i] != str2.value[i]) |
604 |
return false; |
return false; |
605 |
return true; |
return true; |
616 |
*/ |
*/ |
617 |
public boolean contentEquals(StringBuffer buffer) |
public boolean contentEquals(StringBuffer buffer) |
618 |
{ |
{ |
619 |
// XXX Synchronize on buffer. |
synchronized (buffer) |
620 |
if (count != buffer.count) |
{ |
621 |
return false; |
if (count != buffer.count) |
622 |
for (int i = 0; i < count; i++) |
return false; |
623 |
if (value[i] != buffer.value[i]) |
if (value == buffer.value) |
624 |
return false; |
return true; // Possible if shared. |
625 |
return true; |
int i = count; |
626 |
|
while (--i >= 0) |
627 |
|
if (value[i] != buffer.value[i]) |
628 |
|
return false; |
629 |
|
return true; |
630 |
|
} |
631 |
} |
} |
632 |
|
|
633 |
/** |
/** |
651 |
{ |
{ |
652 |
if (anotherString == null || count != anotherString.count) |
if (anotherString == null || count != anotherString.count) |
653 |
return false; |
return false; |
654 |
for (int i = 0; i < count; i++) |
int i = count; |
655 |
// Note that checking c1 != c2 is redundant, but avoids method calls. |
while (--i >= 0) |
656 |
if (value[i] != anotherString.value[i] |
{ |
657 |
&& (Character.toUpperCase(value[i]) |
char c1 = value[i]; |
658 |
!= Character.toUpperCase(anotherString.value[i])) |
char c2 = anotherString.value[i]; |
659 |
&& (Character.toLowerCase(value[i]) |
// Note that checking c1 != c2 is redundant, but avoids method calls. |
660 |
!= Character.toLowerCase(anotherString.value[i]))) |
if (c1 != c2 |
661 |
return false; |
&& Character.toUpperCase(c1) != Character.toUpperCase(c2) |
662 |
|
&& Character.toLowerCase(c1) != Character.toLowerCase(c2)) |
663 |
|
return false; |
664 |
|
} |
665 |
return true; |
return true; |
666 |
} |
} |
667 |
|
|
681 |
*/ |
*/ |
682 |
public int compareTo(String anotherString) |
public int compareTo(String anotherString) |
683 |
{ |
{ |
684 |
int min = Math.min(count, anotherString.count); |
int i = Math.min(count, anotherString.count); |
685 |
for (int i = 0; i < min; i++) |
while (--i >= 0) |
686 |
{ |
{ |
687 |
int result = value[i] - anotherString.value[i]; |
int result = value[i] - anotherString.value[i]; |
688 |
if (result != 0) |
if (result != 0) |
768 |
if (toffset < 0 || ooffset < 0 || toffset + len > count || |
if (toffset < 0 || ooffset < 0 || toffset + len > count || |
769 |
ooffset + len > other.count) |
ooffset + len > other.count) |
770 |
return false; |
return false; |
771 |
for (int i = 0; i < len; i++) |
while (--len >= 0) |
772 |
// Note that checking c1 != c2 is redundant when ignoreCase is true, |
{ |
773 |
// but it avoids method calls. |
char c1 = value[toffset++]; |
774 |
if (value[toffset + i] != other.value[ooffset + i] |
char c2 = value[ooffset++]; |
775 |
&& (! ignoreCase |
// Note that checking c1 != c2 is redundant when ignoreCase is true, |
776 |
|| ((Character.toLowerCase(value[toffset + i]) |
// but it avoids method calls. |
777 |
!= Character.toLowerCase(other.value[ooffset + i])) |
if (c1 != c2 |
778 |
&& (Character.toUpperCase(value[toffset + i]) |
&& (! ignoreCase |
779 |
!= Character.toUpperCase(other.value[ooffset + i]))))) |
|| (Character.toLowerCase(c1) != Character.toLowerCase(c2) |
780 |
return false; |
&& (Character.toUpperCase(c1) |
781 |
|
!= Character.toUpperCase(c2))))) |
782 |
|
return false; |
783 |
|
} |
784 |
return true; |
return true; |
785 |
} |
} |
786 |
|
|
798 |
*/ |
*/ |
799 |
public boolean startsWith(String prefix, int toffset) |
public boolean startsWith(String prefix, int toffset) |
800 |
{ |
{ |
801 |
if (toffset < 0 || toffset > count) |
return regionMatches(false, toffset, prefix, 0, prefix.count); |
|
return false; |
|
|
return prefix.count == 0 |
|
|
|| regionMatches(toffset, prefix, 0, prefix.count); |
|
802 |
} |
} |
803 |
|
|
804 |
/** |
/** |
812 |
*/ |
*/ |
813 |
public boolean startsWith(String prefix) |
public boolean startsWith(String prefix) |
814 |
{ |
{ |
815 |
return prefix.count == 0 |
return regionMatches(false, 0, prefix, 0, prefix.count); |
|
|| regionMatches(0, prefix, 0, prefix.count); |
|
816 |
} |
} |
817 |
|
|
818 |
/** |
/** |
826 |
*/ |
*/ |
827 |
public boolean endsWith(String suffix) |
public boolean endsWith(String suffix) |
828 |
{ |
{ |
829 |
return suffix.count == 0 |
return regionMatches(false, count - suffix.count, suffix, 0, suffix.count); |
|
|| regionMatches(count - suffix.count, suffix, 0, suffix.count); |
|
830 |
} |
} |
831 |
|
|
832 |
/** |
/** |
873 |
{ |
{ |
874 |
if (fromIndex < 0) |
if (fromIndex < 0) |
875 |
fromIndex = 0; |
fromIndex = 0; |
876 |
for (int i = fromIndex; i < count; i++) |
for ( ; fromIndex < count; fromIndex++) |
877 |
if (value[i] == ch) |
if (value[fromIndex] == ch) |
878 |
return i; |
return fromIndex; |
879 |
return -1; |
return -1; |
880 |
} |
} |
881 |
|
|
904 |
{ |
{ |
905 |
if (fromIndex >= count) |
if (fromIndex >= count) |
906 |
fromIndex = count - 1; |
fromIndex = count - 1; |
907 |
for (int i = fromIndex; i >= 0; i--) |
for ( ; fromIndex >= 0; fromIndex--) |
908 |
if (value[i] == ch) |
if (value[fromIndex] == ch) |
909 |
return i; |
return fromIndex; |
910 |
return -1; |
return -1; |
911 |
} |
} |
912 |
|
|
937 |
{ |
{ |
938 |
if (fromIndex < 0) |
if (fromIndex < 0) |
939 |
fromIndex = 0; |
fromIndex = 0; |
940 |
for (int i = fromIndex; i <= count; i++) |
int limit = count - str.count; |
941 |
if (regionMatches(i, str, 0, str.count)) |
for ( ; fromIndex <= limit; fromIndex++) |
942 |
return i; |
if (regionMatches(fromIndex, str, 0, str.count)) |
943 |
|
return fromIndex; |
944 |
return -1; |
return -1; |
945 |
} |
} |
946 |
|
|
969 |
*/ |
*/ |
970 |
public int lastIndexOf(String str, int fromIndex) |
public int lastIndexOf(String str, int fromIndex) |
971 |
{ |
{ |
972 |
if (fromIndex >= count) |
fromIndex = Math.min(fromIndex, count - str.count); |
973 |
fromIndex = count - str.count; |
for ( ; fromIndex >= 0; fromIndex--) |
974 |
for (int i = fromIndex; i >= 0; i--) |
if (regionMatches(fromIndex, str, 0, str.count)) |
975 |
if (regionMatches(i, str, 0, str.count)) |
return fromIndex; |
|
return i; |
|
976 |
return -1; |
return -1; |
977 |
} |
} |
978 |
|
|
1005 |
{ |
{ |
1006 |
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) |
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) |
1007 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
1008 |
char[] newStr = new char[endIndex - beginIndex]; |
if (beginIndex == 0) |
1009 |
System.arraycopy(value, beginIndex, newStr, 0, endIndex - beginIndex); |
// Package constructor avoids an array copy. |
1010 |
return new String(newStr); |
return endIndex == count ? this : new String(value, endIndex); |
1011 |
|
int len = endIndex - beginIndex; |
1012 |
|
char[] newStr = new char[len]; |
1013 |
|
System.arraycopy(value, beginIndex, newStr, 0, len); |
1014 |
|
// Package constructor avoids an array copy. |
1015 |
|
return new String(newStr, len); |
1016 |
} |
} |
1017 |
|
|
1018 |
/** |
/** |
1044 |
{ |
{ |
1045 |
if (str.count == 0) |
if (str.count == 0) |
1046 |
return this; |
return this; |
1047 |
|
if (count == 0) |
1048 |
|
return str; |
1049 |
char[] newStr = new char[count + str.count]; |
char[] newStr = new char[count + str.count]; |
1050 |
System.arraycopy(this.value, 0, newStr, 0, count); |
System.arraycopy(value, 0, newStr, 0, count); |
1051 |
System.arraycopy(str.value, 0, newStr, count, str.count); |
System.arraycopy(str.value, 0, newStr, count, str.count); |
1052 |
return new String(newStr); |
// Package constructor avoids an array copy. |
1053 |
|
return new String(newStr, newStr.length); |
1054 |
} |
} |
1055 |
|
|
1056 |
/** |
/** |
1063 |
*/ |
*/ |
1064 |
public String replace(char oldChar, char newChar) |
public String replace(char oldChar, char newChar) |
1065 |
{ |
{ |
1066 |
int index = 0; |
if (oldChar == newChar) |
1067 |
for (; index < count; index++) |
return this; |
1068 |
|
int index = count; |
1069 |
|
while (--index >= 0) |
1070 |
if (value[index] == oldChar) |
if (value[index] == oldChar) |
1071 |
break; |
break; |
1072 |
if (index == count) return this; |
if (index < 0) |
1073 |
char[] newStr = new char[count]; |
return this; |
1074 |
System.arraycopy(value, 0, newStr, 0, count); |
char[] newStr = (char[]) value.clone(); |
1075 |
for (int i = index; i < count; i++) |
do |
1076 |
if (value[i] == oldChar) |
if (value[index] == oldChar) |
1077 |
newStr[i] = newChar; |
value[index] = newChar; |
1078 |
return new String(newStr); |
while (--index >= 0); |
1079 |
|
// Package constructor avoids an array copy. |
1080 |
|
return new String(newStr, count); |
1081 |
} |
} |
1082 |
|
|
1083 |
/** |
/** |
1088 |
* @return true if the pattern matches |
* @return true if the pattern matches |
1089 |
* @throws NullPointerException if regex is null |
* @throws NullPointerException if regex is null |
1090 |
* @throws PatternSyntaxExceptions if regex is invalid |
* @throws PatternSyntaxExceptions if regex is invalid |
1091 |
|
* @see Pattern#matches(String, CharSequence) |
1092 |
* @since 1.4 |
* @since 1.4 |
1093 |
* @XXX Add this method. |
*/ |
1094 |
public boolean matches(String regex) |
public boolean matches(String regex) |
1095 |
{ |
{ |
1096 |
return Pattern.matches(regex, this); |
return Pattern.matches(regex, this); |
1097 |
} |
} |
|
*/ |
|
1098 |
|
|
1099 |
/** |
/** |
1100 |
* Replaces the first substring match of the regular expression with a |
* Replaces the first substring match of the regular expression with a |
1106 |
* @return the modified string |
* @return the modified string |
1107 |
* @throws NullPointerException if regex or replacement is null |
* @throws NullPointerException if regex or replacement is null |
1108 |
* @throws PatternSyntaxExceptions if regex is invalid |
* @throws PatternSyntaxExceptions if regex is invalid |
1109 |
|
* @see #replaceAll(String, String) |
1110 |
|
* @see Pattern#compile(String) |
1111 |
|
* @see Pattern#matcher(CharSequence) |
1112 |
|
* @see Matcher#replaceFirst(String) |
1113 |
* @since 1.4 |
* @since 1.4 |
1114 |
* @XXX Add this method. |
*/ |
1115 |
public String replaceFirst(String regex, String replacement) |
public String replaceFirst(String regex, String replacement) |
1116 |
{ |
{ |
1117 |
return Pattern.compile(regex).matcher(this).replaceFirst(replacement); |
return Pattern.compile(regex).matcher(this).replaceFirst(replacement); |
1118 |
} |
} |
|
*/ |
|
1119 |
|
|
1120 |
/** |
/** |
1121 |
* Replaces all matching substrings of the regular expression with a |
* Replaces all matching substrings of the regular expression with a |
1127 |
* @return the modified string |
* @return the modified string |
1128 |
* @throws NullPointerException if regex or replacement is null |
* @throws NullPointerException if regex or replacement is null |
1129 |
* @throws PatternSyntaxExceptions if regex is invalid |
* @throws PatternSyntaxExceptions if regex is invalid |
1130 |
|
* @see #replaceFirst(String, String) |
1131 |
|
* @see Pattern#compile(String) |
1132 |
|
* @see Pattern#matcher(CharSequence) |
1133 |
|
* @see Matcher#replaceAll(String) |
1134 |
* @since 1.4 |
* @since 1.4 |
1135 |
* @XXX Add this method. |
*/ |
1136 |
public String replaceAll(String regex, String replacement) |
public String replaceAll(String regex, String replacement) |
1137 |
{ |
{ |
1138 |
return Pattern.compile(regex).matcher(this).replaceFirst(replacement); |
return Pattern.compile(regex).matcher(this).replaceAll(replacement); |
1139 |
} |
} |
|
*/ |
|
1140 |
|
|
1141 |
/** |
/** |
1142 |
* Split this string around the matches of a regular expression. Each |
* Split this string around the matches of a regular expression. Each |
1168 |
* @return the array of split strings |
* @return the array of split strings |
1169 |
* @throws NullPointerException if regex or replacement is null |
* @throws NullPointerException if regex or replacement is null |
1170 |
* @throws PatternSyntaxExceptions if regex is invalid |
* @throws PatternSyntaxExceptions if regex is invalid |
1171 |
|
* @see Pattern#compile(String) |
1172 |
|
* @see Pattern#split(CharSequence, int) |
1173 |
* @since 1.4 |
* @since 1.4 |
1174 |
* @XXX Add this method. |
*/ |
1175 |
public String[] split(String regex, int limit) |
public String[] split(String regex, int limit) |
1176 |
{ |
{ |
1177 |
return Pattern.compile(regex).split(this, limit); |
return Pattern.compile(regex).split(this, limit); |
1178 |
} |
} |
|
*/ |
|
1179 |
|
|
1180 |
/** |
/** |
1181 |
* Split this string around the matches of a regular expression. Each |
* Split this string around the matches of a regular expression. Each |
1189 |
* @throws NullPointerException if regex or replacement is null |
* @throws NullPointerException if regex or replacement is null |
1190 |
* @throws PatternSyntaxExceptions if regex is invalid |
* @throws PatternSyntaxExceptions if regex is invalid |
1191 |
* @see #split(String, int) |
* @see #split(String, int) |
1192 |
|
* @see Pattern#compile(String) |
1193 |
|
* @see Pattern#split(CharSequence, int) |
1194 |
* @since 1.4 |
* @since 1.4 |
1195 |
* @XXX Add this method. |
*/ |
1196 |
public String[] split(String regex) |
public String[] split(String regex) |
1197 |
{ |
{ |
1198 |
return Pattern.compile(regex).split(this, 0); |
return Pattern.compile(regex).split(this, 0); |
1199 |
} |
} |
|
*/ |
|
1200 |
|
|
1201 |
/** |
/** |
1202 |
* Lowercases this String according to a particular locale. This uses |
* Lowercases this String according to a particular locale. This uses |
1205 |
* |
* |
1206 |
* @param loc locale to use |
* @param loc locale to use |
1207 |
* @return new lowercased String, or this if no characters were lowercased |
* @return new lowercased String, or this if no characters were lowercased |
1208 |
|
* @throws NullPointerException if loc is null |
1209 |
* @see #toUpperCase(Locale) |
* @see #toUpperCase(Locale) |
1210 |
* @since 1.1 |
* @since 1.1 |
1211 |
*/ |
*/ |
1212 |
public String toLowerCase(Locale loc) |
public String toLowerCase(Locale loc) |
1213 |
{ |
{ |
1214 |
// XXX Fix this to follow Unicode rules. |
// First, see if the current string is already lower case. |
1215 |
char[] newStr = new char[count]; |
boolean turkish = "tr".equals(loc.getLanguage()); |
1216 |
for (int i = 0; i < count; i++) |
int i = count; |
1217 |
newStr[i] = Character.toLowerCase(value[i]); |
while (--i >= 0) |
1218 |
for (int i = 0; i < count; i++) |
{ |
1219 |
if (value[i] != newStr[i]) |
char ch = value[i]; |
1220 |
return new String(newStr); |
if ((turkish && ch == '\u0049') |
1221 |
return this; |
|| ch != Character.toLowerCase(ch)) |
1222 |
|
break; |
1223 |
|
} |
1224 |
|
if (i < 0) |
1225 |
|
return this; |
1226 |
|
|
1227 |
|
// Now we perform the conversion. Fortunately, there are no multi-character |
1228 |
|
// lowercase expansions in Unicode 3.0.0. |
1229 |
|
char[] newStr = (char[]) value.clone(); |
1230 |
|
do |
1231 |
|
{ |
1232 |
|
char ch = value[i]; |
1233 |
|
// Hardcoded special case. |
1234 |
|
newStr[i] = (turkish && ch == '\u0049') ? '\u0131' |
1235 |
|
: Character.toLowerCase(ch); |
1236 |
|
} |
1237 |
|
while (--i >= 0); |
1238 |
|
// Package constructor avoids an array copy. |
1239 |
|
return new String(newStr, count); |
1240 |
} |
} |
1241 |
|
|
1242 |
/** |
/** |
1244 |
* applied to the platform's default Locale, so the resulting string may |
* applied to the platform's default Locale, so the resulting string may |
1245 |
* be a different length. |
* be a different length. |
1246 |
* |
* |
1247 |
* @return new lowercased String, or this if no characters where lowercased |
* @return new lowercased String, or this if no characters were lowercased |
1248 |
* @see #toLowerCase(Locale) |
* @see #toLowerCase(Locale) |
1249 |
|
* @see #toUpperCase() |
1250 |
*/ |
*/ |
1251 |
public String toLowerCase() |
public String toLowerCase() |
1252 |
{ |
{ |
1260 |
* |
* |
1261 |
* @param loc locale to use |
* @param loc locale to use |
1262 |
* @return new uppercased String, or this if no characters were uppercased |
* @return new uppercased String, or this if no characters were uppercased |
1263 |
|
* @throws NullPointerException if loc is null |
1264 |
* @see #toLowerCase(Locale) |
* @see #toLowerCase(Locale) |
1265 |
* @since 1.1 |
* @since 1.1 |
1266 |
*/ |
*/ |
1267 |
public String toUpperCase(Locale loc) |
public String toUpperCase(Locale loc) |
1268 |
{ |
{ |
1269 |
// XXX Fix this to follow Unicode rules. |
// First, see how many characters we have to grow by, as well as if the |
1270 |
char[] newStr = new char[count]; |
// current string is already upper case. |
1271 |
for (int i = 0; i < count; i++) |
boolean turkish = "tr".equals(loc.getLanguage()); |
1272 |
newStr[i] = Character.toUpperCase(value[i]); |
int expand = 0; |
1273 |
for (int i = 0; i < count; i++) |
boolean unchanged = true; |
1274 |
if (value[i] != newStr[i]) |
for (int i = count; --i >= 0; ) |
1275 |
return new String(newStr); |
{ |
1276 |
return this; |
char ch = value[i]; |
1277 |
|
expand += upperCaseExpansion(ch); |
1278 |
|
unchanged = (unchanged && expand == 0 |
1279 |
|
&& ! (turkish && ch == '\u0069') |
1280 |
|
&& ch == Character.toUpperCase(ch)); |
1281 |
|
} |
1282 |
|
if (unchanged) |
1283 |
|
return this; |
1284 |
|
|
1285 |
|
// Now we perform the conversion. |
1286 |
|
if (expand == 0) |
1287 |
|
{ |
1288 |
|
char[] newStr = (char[]) value.clone(); |
1289 |
|
for (int i = count; --i >= 0; ) |
1290 |
|
{ |
1291 |
|
char ch = value[i]; |
1292 |
|
// Hardcoded special case. |
1293 |
|
newStr[i] = (turkish && ch == '\u0069') ? '\u0130' |
1294 |
|
: Character.toUpperCase(ch); |
1295 |
|
} |
1296 |
|
// Package constructor avoids an array copy. |
1297 |
|
return new String(newStr, count); |
1298 |
|
} |
1299 |
|
|
1300 |
|
// Expansion is necessary. |
1301 |
|
char[] newStr = new char[count + expand]; |
1302 |
|
for (int i = 0, j = 0; i < count; ) |
1303 |
|
{ |
1304 |
|
char ch = value[i++]; |
1305 |
|
// Hardcoded special case. |
1306 |
|
if (turkish && ch == '\u0069') |
1307 |
|
{ |
1308 |
|
newStr[j++] = '\u0130'; |
1309 |
|
continue; |
1310 |
|
} |
1311 |
|
expand = upperCaseExpansion(ch); |
1312 |
|
if (expand > 0) |
1313 |
|
{ |
1314 |
|
int offset = upperCaseIndex(ch); |
1315 |
|
while (expand-- >= 0) |
1316 |
|
newStr[j++] = upperExpand[offset++]; |
1317 |
|
} |
1318 |
|
else |
1319 |
|
newStr[j++] = Character.toUpperCase(ch); |
1320 |
|
} |
1321 |
|
// Package constructor avoids an array copy. |
1322 |
|
return new String(newStr, newStr.length); |
1323 |
} |
} |
1324 |
|
|
1325 |
/** |
/** |
1327 |
* applied to the platform's default Locale, so the resulting string may |
* applied to the platform's default Locale, so the resulting string may |
1328 |
* be a different length. |
* be a different length. |
1329 |
* |
* |
1330 |
* @return new uppercased String, or this if no characters where uppercased |
* @return new uppercased String, or this if no characters were uppercased |
1331 |
* @see #toUpperCase(Locale) |
* @see #toUpperCase(Locale) |
1332 |
|
* @see #toLowerCase() |
1333 |
*/ |
*/ |
1334 |
public String toUpperCase() |
public String toUpperCase() |
1335 |
{ |
{ |
1337 |
} |
} |
1338 |
|
|
1339 |
/** |
/** |
1340 |
* Trims all characters less than or equal to ' ' (many ASCII control |
* Trims all characters less than or equal to <code>'\u0020'</code> |
1341 |
* characters, and all {@link Character#whitespace(char)}) from the |
* (<code>' '</code>) from the beginning and end of this String. This |
1342 |
* beginning and end of this String. |
* includes many, but not all, ASCII control characters, and all |
1343 |
|
* {@link Character#whitespace(char)}. |
1344 |
* |
* |
1345 |
* @return new trimmed String, or this if nothing trimmed |
* @return new trimmed String, or this if nothing trimmed |
1346 |
*/ |
*/ |
1350 |
return this; |
return this; |
1351 |
int begin = 0; |
int begin = 0; |
1352 |
do |
do |
1353 |
{ |
if (begin == count) |
1354 |
if (begin == count) |
return ""; |
|
return ""; |
|
|
} |
|
1355 |
while (value[begin++] <= '\u0020'); |
while (value[begin++] <= '\u0020'); |
1356 |
int end = count; |
int end = count; |
1357 |
while (value[--end] <= '\u0020'); |
while (value[--end] <= '\u0020'); |
1377 |
*/ |
*/ |
1378 |
public char[] toCharArray() |
public char[] toCharArray() |
1379 |
{ |
{ |
1380 |
|
if (count == value.length) |
1381 |
|
return (char[]) value.clone(); |
1382 |
char[] copy = new char[count]; |
char[] copy = new char[count]; |
1383 |
System.arraycopy(value, 0, copy, 0, count); |
System.arraycopy(value, 0, copy, 0, count); |
1384 |
return copy; |
return copy; |
1409 |
*/ |
*/ |
1410 |
public static String valueOf(char[] data) |
public static String valueOf(char[] data) |
1411 |
{ |
{ |
1412 |
return new String(data, 0, data.length); |
return new String(data); |
1413 |
} |
} |
1414 |
|
|
1415 |
/** |
/** |
1464 |
*/ |
*/ |
1465 |
public static String copyValueOf(char[] data) |
public static String copyValueOf(char[] data) |
1466 |
{ |
{ |
1467 |
return new String(data, 0, data.length); |
return new String(data); |
1468 |
} |
} |
1469 |
|
|
1470 |
/** |
/** |
1486 |
*/ |
*/ |
1487 |
public static String valueOf(char c) |
public static String valueOf(char c) |
1488 |
{ |
{ |
1489 |
// XXX Share this array. |
// Package constructor avoids an array copy. |
1490 |
return new String(new char[] { c }); |
return new String(new char[] { c }, 1); |
1491 |
} |
} |
1492 |
|
|
1493 |
/** |
/** |
1546 |
* (s1.intern() == s2.intern()). All string literals and string-valued |
* (s1.intern() == s2.intern()). All string literals and string-valued |
1547 |
* constant expressions are already interned. |
* constant expressions are already interned. |
1548 |
* |
* |
1549 |
* @return intern'd String |
* @return the interned String |
1550 |
*/ |
*/ |
1551 |
public String intern() |
public String intern() |
1552 |
{ |
{ |
1553 |
// XXX Synchronize access to the table, to avoid races. |
synchronized (internTable) |
1554 |
Object o = internTable.get(this); |
{ |
1555 |
if (o != null) |
WeakReference ref = (WeakReference) internTable.get(this); |
1556 |
return (String) o; |
if (ref != null) |
1557 |
internTable.put(this, this); |
{ |
1558 |
|
String s = (String) ref.get(); |
1559 |
|
// If s is null, then no strong references exist to the String; |
1560 |
|
// the weak hash map will soon delete the key. |
1561 |
|
if (s != null) |
1562 |
|
return s; |
1563 |
|
} |
1564 |
|
internTable.put(this, new WeakReference(this)); |
1565 |
|
} |
1566 |
return this; |
return this; |
1567 |
} |
} |
1568 |
} |
|
1569 |
|
/** |
1570 |
|
* Helper function used to detect which characters have a multi-character |
1571 |
|
* uppercase expansion. Note that this is only used in locations which |
1572 |
|
* track one-to-many capitalization (java.lang.Character does not do this). |
1573 |
|
* As of Unicode 3.0.0, the result is limited in the range 0 to 2, as the |
1574 |
|
* longest uppercase expansion is three characters (a growth of 2 from the |
1575 |
|
* lowercase character). |
1576 |
|
* |
1577 |
|
* @param ch the char to check |
1578 |
|
* @return the number of characters to add when converting to uppercase |
1579 |
|
* @see CharData#DIRECTION |
1580 |
|
* @see CharData#UPPER_SPECIAL |
1581 |
|
* @see #toUpperCase(Locale) |
1582 |
|
*/ |
1583 |
|
private static int upperCaseExpansion(char ch) |
1584 |
|
{ |
1585 |
|
return Character.direction[Character.readChar(ch) >> 7] & 3; |
1586 |
|
} |
1587 |
|
|
1588 |
|
/** |
1589 |
|
* Helper function used to locate the offset in upperExpand given a |
1590 |
|
* character with a multi-character expansion. The binary search is |
1591 |
|
* optimized under the assumption that this method will only be called on |
1592 |
|
* characters which exist in upperSpecial. |
1593 |
|
* |
1594 |
|
* @param ch the char to check |
1595 |
|
* @return the index where its expansion begins |
1596 |
|
* @see CharData#UPPER_SPECIAL |
1597 |
|
* @see CharData#UPPER_EXPAND |
1598 |
|
* @see #toUpperCase(Locale) |
1599 |
|
*/ |
1600 |
|
private static int upperCaseIndex(char ch) |
1601 |
|
{ |
1602 |
|
// Simple binary search for the correct character. |
1603 |
|
int low = 0; |
1604 |
|
int hi = upperSpecial.length - 2; |
1605 |
|
int mid = ((low + hi) >> 2) << 1; |
1606 |
|
char c = upperSpecial[mid]; |
1607 |
|
while (ch != c) |
1608 |
|
{ |
1609 |
|
if (ch < c) |
1610 |
|
hi = mid - 2; |
1611 |
|
else |
1612 |
|
low = mid + 2; |
1613 |
|
mid = ((low + hi) >> 2) << 1; |
1614 |
|
c = upperSpecial[mid]; |
1615 |
|
} |
1616 |
|
return upperSpecial[mid + 1]; |
1617 |
|
} |
1618 |
|
} // class String |