59 |
* <code>new StringBuffer().append(a).append(b).toString()</code>. |
* <code>new StringBuffer().append(a).append(b).toString()</code>. |
60 |
* |
* |
61 |
* <p>Classpath's StringBuffer is capable of sharing memory with Strings for |
* <p>Classpath's StringBuffer is capable of sharing memory with Strings for |
62 |
* efficiency. This will help in two instances: first, when a StringBuffer |
* efficiency. This will help when a StringBuffer is converted to a String |
63 |
* is created from a String but is never changed, and second, when a |
* and the StringBuffer is not changed after that (quite common when performing |
64 |
* StringBuffer is converted to a String and the StringBuffer is not changed |
* string concatenation). |
|
* after that. |
|
65 |
* |
* |
66 |
* @author Paul Fisher |
* @author Paul Fisher |
67 |
* @author John Keiser |
* @author John Keiser |
137 |
* |
* |
138 |
* @param str the <code>String</code> to convert |
* @param str the <code>String</code> to convert |
139 |
* @throws NullPointerException if str is null |
* @throws NullPointerException if str is null |
|
* @XXX Optimize for sharing. |
|
140 |
*/ |
*/ |
141 |
public StringBuffer(String str) |
public StringBuffer(String str) |
142 |
{ |
{ |
143 |
count = str.length(); |
// Unfortunately, because the size is 16 larger, we cannot share. |
144 |
|
count = str.count; |
145 |
value = new char[count + DEFAULT_CAPACITY]; |
value = new char[count + DEFAULT_CAPACITY]; |
146 |
str.getChars(0, count, value, 0); |
str.getChars(0, count, value, 0); |
147 |
} |
} |
206 |
throw new StringIndexOutOfBoundsException(newLength); |
throw new StringIndexOutOfBoundsException(newLength); |
207 |
|
|
208 |
ensureCapacity_unsynchronized(newLength); |
ensureCapacity_unsynchronized(newLength); |
209 |
for (int i = count; i < newLength; ++i) |
while (count < newLength) |
210 |
value[i] = '\0'; |
value[count++] = '\0'; |
211 |
count = newLength; |
count = newLength; |
212 |
} |
} |
213 |
|
|
221 |
*/ |
*/ |
222 |
public synchronized char charAt(int index) |
public synchronized char charAt(int index) |
223 |
{ |
{ |
224 |
if (index >= count) |
if (index < 0 || index >= count) |
225 |
throw new StringIndexOutOfBoundsException(index); |
throw new StringIndexOutOfBoundsException(index); |
226 |
return value[index]; |
return value[index]; |
227 |
} |
} |
236 |
* @param dstOffset the index to start copying into |
* @param dstOffset the index to start copying into |
237 |
* @throws NullPointerException if dst is null |
* @throws NullPointerException if dst is null |
238 |
* @throws IndexOutOfBoundsException if any source or target indices are |
* @throws IndexOutOfBoundsException if any source or target indices are |
239 |
* out of range (while unspecified, this is a |
* out of range (while unspecified, source problems cause a |
240 |
* StringIndexOutOfBoundsException) |
* StringIndexOutOfBoundsException, and dest problems cause an |
241 |
|
* ArrayIndexOutOfBoundsException) |
242 |
* @see System#arraycopy(Object, int, Object, int, int) |
* @see System#arraycopy(Object, int, Object, int, int) |
243 |
*/ |
*/ |
244 |
public synchronized void getChars(int srcOffset, int srcEnd, |
public synchronized void getChars(int srcOffset, int srcEnd, |
245 |
char[] dst, int dstOffset) |
char[] dst, int dstOffset) |
246 |
{ |
{ |
|
if (srcOffset < 0 || srcOffset > srcEnd) |
|
|
throw new StringIndexOutOfBoundsException(srcOffset); |
|
247 |
int todo = srcEnd - srcOffset; |
int todo = srcEnd - srcOffset; |
248 |
if (srcEnd > count || dstOffset + todo > count) |
if (srcOffset < 0 || srcEnd > count || todo < 0) |
249 |
throw new StringIndexOutOfBoundsException(srcEnd); |
throw new StringIndexOutOfBoundsException(); |
250 |
System.arraycopy(value, srcOffset, dst, dstOffset, todo); |
System.arraycopy(value, srcOffset, dst, dstOffset, todo); |
251 |
} |
} |
252 |
|
|
279 |
*/ |
*/ |
280 |
public StringBuffer append(Object obj) |
public StringBuffer append(Object obj) |
281 |
{ |
{ |
282 |
return append(String.valueOf(obj)); |
return append(obj == null ? "null" : obj.toString()); |
283 |
} |
} |
284 |
|
|
285 |
/** |
/** |
293 |
{ |
{ |
294 |
if (str == null) |
if (str == null) |
295 |
str = "null"; |
str = "null"; |
296 |
int len = str.length(); |
int len = str.count; |
297 |
ensureCapacity_unsynchronized(count + len); |
ensureCapacity_unsynchronized(count + len); |
298 |
str.getChars(0, len, value, count); |
str.getChars(0, len, value, count); |
299 |
count += len; |
count += len; |
310 |
* @see #append(Object) |
* @see #append(Object) |
311 |
* @since 1.4 |
* @since 1.4 |
312 |
*/ |
*/ |
313 |
public StringBuffer append(StringBuffer stringBuffer) |
public synchronized StringBuffer append(StringBuffer stringBuffer) |
314 |
{ |
{ |
315 |
// XXX The point of this method was to AVOID creating the String... |
if (stringBuffer == null) |
316 |
return append(stringBuffer.toString()); |
return append("null"); |
317 |
|
synchronized (stringBuffer) |
318 |
|
{ |
319 |
|
int len = stringBuffer.count; |
320 |
|
ensureCapacity_unsynchronized(count + len); |
321 |
|
System.arraycopy(stringBuffer.value, 0, value, count, len); |
322 |
|
count += len; |
323 |
|
} |
324 |
|
return this; |
325 |
} |
} |
326 |
|
|
327 |
/** |
/** |
372 |
*/ |
*/ |
373 |
public StringBuffer append(boolean bool) |
public StringBuffer append(boolean bool) |
374 |
{ |
{ |
375 |
return append(String.valueOf(bool)); |
return append(bool ? "true" : "false"); |
376 |
} |
} |
377 |
|
|
378 |
/** |
/** |
397 |
* @return this <code>StringBuffer</code> |
* @return this <code>StringBuffer</code> |
398 |
* @see String#valueOf(int) |
* @see String#valueOf(int) |
399 |
*/ |
*/ |
400 |
|
// This is native in libgcj, for efficiency. |
401 |
public StringBuffer append(int inum) |
public StringBuffer append(int inum) |
402 |
{ |
{ |
403 |
return append(String.valueOf(inum)); |
return append(String.valueOf(inum)); |
414 |
*/ |
*/ |
415 |
public StringBuffer append(long lnum) |
public StringBuffer append(long lnum) |
416 |
{ |
{ |
417 |
return append(String.valueOf(lnum)); |
return append(Long.toString(lnum, 10)); |
418 |
} |
} |
419 |
|
|
420 |
/** |
/** |
428 |
*/ |
*/ |
429 |
public StringBuffer append(float fnum) |
public StringBuffer append(float fnum) |
430 |
{ |
{ |
431 |
return append(String.valueOf(fnum)); |
return append(Float.toString(fnum)); |
432 |
} |
} |
433 |
|
|
434 |
/** |
/** |
442 |
*/ |
*/ |
443 |
public StringBuffer append(double dnum) |
public StringBuffer append(double dnum) |
444 |
{ |
{ |
445 |
return append(String.valueOf(dnum)); |
return append(Double.toString(dnum)); |
446 |
} |
} |
447 |
|
|
448 |
/** |
/** |
502 |
if (start < 0 || start > count || start > end) |
if (start < 0 || start > count || start > end) |
503 |
throw new StringIndexOutOfBoundsException(start); |
throw new StringIndexOutOfBoundsException(start); |
504 |
|
|
505 |
int len = str.length(); |
int len = str.count; |
506 |
// Calculate the difference in 'count' after the replace. |
// Calculate the difference in 'count' after the replace. |
507 |
int delta = len - ((end > count ? count : end) - start); |
int delta = len - (end > count ? count : end) + start; |
508 |
ensureCapacity_unsynchronized(count + delta); |
ensureCapacity_unsynchronized(count + delta); |
509 |
|
|
510 |
if (delta != 0 && end < count) |
if (delta != 0 && end < count) |
562 |
*/ |
*/ |
563 |
public synchronized String substring(int beginIndex, int endIndex) |
public synchronized String substring(int beginIndex, int endIndex) |
564 |
{ |
{ |
565 |
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) |
int len = endIndex - beginIndex; |
566 |
|
if (beginIndex < 0 || endIndex > count || len < 0) |
567 |
throw new StringIndexOutOfBoundsException(); |
throw new StringIndexOutOfBoundsException(); |
568 |
// XXX FIXME: for libgcj it would be possible, and more efficient, to |
if (len == 0) |
569 |
// enable sharing here. |
return ""; |
570 |
return new String(value, beginIndex, endIndex - beginIndex); |
// Share the char[] unless 3/4 empty. |
571 |
|
if (beginIndex != 0 || (endIndex << 2) < value.length) |
572 |
|
return new String(value, beginIndex, len); |
573 |
|
// XXX Todo: share even when beginIndex != 0. |
574 |
|
shared = true; |
575 |
|
// Package constructor avoids an array copy. |
576 |
|
return new String(value, len); |
577 |
} |
} |
578 |
|
|
579 |
/** |
/** |
592 |
public synchronized StringBuffer insert(int offset, |
public synchronized StringBuffer insert(int offset, |
593 |
char[] str, int str_offset, int len) |
char[] str, int str_offset, int len) |
594 |
{ |
{ |
595 |
if (offset < 0 || offset > count) |
if (offset < 0 || offset > count || len < 0 |
596 |
throw new StringIndexOutOfBoundsException(offset); |
|| str_offset < 0 || str_offset + len > str.length) |
597 |
if (len < 0) |
throw new StringIndexOutOfBoundsException(); |
|
throw new StringIndexOutOfBoundsException(len); |
|
|
if (str_offset < 0 || str_offset + len > str.length) |
|
|
throw new StringIndexOutOfBoundsException(str_offset); |
|
598 |
ensureCapacity_unsynchronized(count + len); |
ensureCapacity_unsynchronized(count + len); |
599 |
System.arraycopy(value, offset, value, offset + len, count - offset); |
System.arraycopy(value, offset, value, offset + len, count - offset); |
600 |
System.arraycopy(str, str_offset, value, offset, len); |
System.arraycopy(str, str_offset, value, offset, len); |
615 |
*/ |
*/ |
616 |
public StringBuffer insert(int offset, Object obj) |
public StringBuffer insert(int offset, Object obj) |
617 |
{ |
{ |
618 |
return insert(offset, String.valueOf(obj)); |
return insert(offset, obj == null ? "null" : obj.toString()); |
619 |
} |
} |
620 |
|
|
621 |
/** |
/** |
634 |
throw new StringIndexOutOfBoundsException(offset); |
throw new StringIndexOutOfBoundsException(offset); |
635 |
if (str == null) |
if (str == null) |
636 |
str = "null"; |
str = "null"; |
637 |
int len = str.length(); |
int len = str.count; |
638 |
ensureCapacity_unsynchronized(count + len); |
ensureCapacity_unsynchronized(count + len); |
639 |
System.arraycopy(value, offset, value, offset + len, count - offset); |
System.arraycopy(value, offset, value, offset + len, count - offset); |
640 |
str.getChars(0, len, value, offset); |
str.getChars(0, len, value, offset); |
655 |
*/ |
*/ |
656 |
public StringBuffer insert(int offset, char[] data) |
public StringBuffer insert(int offset, char[] data) |
657 |
{ |
{ |
658 |
// Sun checks offset before checking if data is null, hence this delay |
return insert(offset, data, 0, data.length); |
|
// of dereferencing data. |
|
|
return insert(offset, data, 0, data == null ? 0 : data.length); |
|
659 |
} |
} |
660 |
|
|
661 |
/** |
/** |
722 |
*/ |
*/ |
723 |
public StringBuffer insert(int offset, long lnum) |
public StringBuffer insert(int offset, long lnum) |
724 |
{ |
{ |
725 |
return insert(offset, String.valueOf(lnum)); |
return insert(offset, Long.toString(lnum, 10)); |
726 |
} |
} |
727 |
|
|
728 |
/** |
/** |
738 |
*/ |
*/ |
739 |
public StringBuffer insert(int offset, float fnum) |
public StringBuffer insert(int offset, float fnum) |
740 |
{ |
{ |
741 |
return insert(offset, String.valueOf(fnum)); |
return insert(offset, Float.toString(fnum)); |
742 |
} |
} |
743 |
|
|
744 |
/** |
/** |
754 |
*/ |
*/ |
755 |
public StringBuffer insert(int offset, double dnum) |
public StringBuffer insert(int offset, double dnum) |
756 |
{ |
{ |
757 |
return insert(offset, String.valueOf(dnum)); |
return insert(offset, Double.toString(dnum)); |
758 |
} |
} |
759 |
|
|
760 |
/** |
/** |
785 |
*/ |
*/ |
786 |
public synchronized int indexOf(String str, int fromIndex) |
public synchronized int indexOf(String str, int fromIndex) |
787 |
{ |
{ |
788 |
// XXX Avoid the object creation here. |
if (fromIndex < 0) |
789 |
return this.toString().indexOf(str, fromIndex); |
fromIndex = 0; |
790 |
|
int limit = count - str.count; |
791 |
|
for ( ; fromIndex <= limit; fromIndex++) |
792 |
|
if (regionMatches(fromIndex, str)) |
793 |
|
return fromIndex; |
794 |
|
return -1; |
795 |
} |
} |
796 |
|
|
797 |
/** |
/** |
822 |
*/ |
*/ |
823 |
public synchronized int lastIndexOf(String str, int fromIndex) |
public synchronized int lastIndexOf(String str, int fromIndex) |
824 |
{ |
{ |
825 |
// XXX Avoid the object creation here. |
fromIndex = Math.min(fromIndex, count - str.count); |
826 |
return this.toString().lastIndexOf(str, fromIndex); |
for ( ; fromIndex >= 0; fromIndex--) |
827 |
|
if (regionMatches(fromIndex, str)) |
828 |
|
return fromIndex; |
829 |
|
return -1; |
830 |
} |
} |
831 |
|
|
832 |
/** |
/** |
839 |
{ |
{ |
840 |
// Call ensureCapacity to enforce copy-on-write. |
// Call ensureCapacity to enforce copy-on-write. |
841 |
ensureCapacity_unsynchronized(count); |
ensureCapacity_unsynchronized(count); |
842 |
for (int i = 0; i < count / 2; ++i) |
for (int i = count >> 1, j = count - i; --i >= 0; ++j) |
843 |
{ |
{ |
844 |
char c = value[i]; |
char c = value[i]; |
845 |
value[i] = value[count - i - 1]; |
value[i] = value[j]; |
846 |
value[count - i - 1] = c; |
value[j] = c; |
847 |
} |
} |
848 |
return this; |
return this; |
849 |
} |
} |
858 |
*/ |
*/ |
859 |
public String toString() |
public String toString() |
860 |
{ |
{ |
861 |
// XXX Implement sharing with this call, to match libgcj. |
// The string will set this.shared = true. |
862 |
return new String(this); |
return new String(this); |
863 |
} |
} |
864 |
|
|
888 |
shared = false; |
shared = false; |
889 |
} |
} |
890 |
} |
} |
891 |
|
|
892 |
|
/** |
893 |
|
* Predicate which determines if a substring of this matches another String |
894 |
|
* starting at a specified offset for each String and continuing for a |
895 |
|
* specified length. This is more efficient than creating a String to call |
896 |
|
* indexOf on. |
897 |
|
* |
898 |
|
* @param toffset index to start comparison at for this String |
899 |
|
* @param other String to compare region to this String |
900 |
|
* @return true if regions match, false otherwise |
901 |
|
* @see #indexOf(String, int) |
902 |
|
* @see #lastIndexOf(String, int) |
903 |
|
* @see String#regionMatches(boolean, int, String, int, int) |
904 |
|
*/ |
905 |
|
private boolean regionMatches(int toffset, String other) |
906 |
|
{ |
907 |
|
for (int index = 0, len = other.count; --len >= 0; toffset++, index++) |
908 |
|
// Note that libgcj requires other.charAt() instead of other.value[]. |
909 |
|
if (value[toffset] != other.value[index]) |
910 |
|
return false; |
911 |
|
return true; |
912 |
|
} |
913 |
} |
} |