package gnu.java.lang;
+import gnu.classpath.SystemProperties;
+
import java.io.Serializable;
/**
*
* @serial the number of characters in the buffer
*/
- int count;
+ private int count;
/**
* The buffer. Note that this has permissions set this way so that String
*
* @serial the buffer
*/
- char[] value;
+ private char[] value;
+
+ /**
+ * A flag to denote whether the string being created has been
+ * allocated to a {@link String} object. On construction,
+ * the character array, {@link #value} is referenced only
+ * by this class. Once {@link #toString()},
+ * {@link #substring(int)} or {@link #substring(int,int)}
+ * are called, the array is also referenced by a {@link String}
+ * object and this flag is set. Subsequent modifications to
+ * this buffer cause a new array to be allocated and the flag
+ * to be reset.
+ */
+ private boolean allocated = false;
/**
* The default capacity of a buffer.
+ * This can be configured using gnu.classpath.cpstringbuilder.capacity
*/
- private static final int DEFAULT_CAPACITY = 16;
+ private static final int DEFAULT_CAPACITY;
+
+ static
+ {
+ String cap =
+ SystemProperties.getProperty("gnu.classpath.cpstringbuilder.capacity");
+ if (cap == null)
+ DEFAULT_CAPACITY = 32;
+ else
+ DEFAULT_CAPACITY = Integer.parseInt(cap);
+ }
/**
- * Create a new CPStringBuilder with default capacity 16.
+ * Create a new CPStringBuilder with the default capacity.
*/
public CPStringBuilder()
{
}
/**
- * Create an empty <code>StringBuffer</code> with the specified initial
+ * Create an empty <code>CPStringBuilder</code> with the specified initial
* capacity.
*
* @param capacity the initial capacity
}
/**
- * Create a new <code>StringBuffer</code> with the characters in the
+ * Create a new <code>CPStringBuilder</code> with the characters in the
* specified <code>String</code>. Initial capacity will be the size of the
- * String plus 16.
+ * String plus the default capacity.
*
* @param str the <code>String</code> to convert
* @throws NullPointerException if str is null
}
/**
- * Create a new <code>StringBuffer</code> with the characters in the
+ * Create a new <code>CPStringBuilder</code> with the characters in the
+ * specified <code>StringBuffer</code>. Initial capacity will be the size of the
+ * String plus the default capacity.
+ *
+ * @param str the <code>String</code> to convert
+ * @throws NullPointerException if str is null
+ */
+ public CPStringBuilder(StringBuffer str)
+ {
+ count = str.length();
+ value = new char[count + DEFAULT_CAPACITY];
+ str.getChars(0, count, value, 0);
+ }
+
+ /**
+ * Create a new <code>CPStringBuilder</code> with the characters in the
+ * specified <code>StringBuilder</code>. Initial capacity will be the size of the
+ * String plus the default capacity.
+ *
+ * @param str the <code>String</code> to convert
+ * @throws NullPointerException if str is null
+ */
+ public CPStringBuilder(StringBuilder str)
+ {
+ count = str.length();
+ value = new char[count + DEFAULT_CAPACITY];
+ str.getChars(0, count, value, 0);
+ }
+
+ /**
+ * Create a new <code>CPStringBuilder</code> with the characters in the
* specified <code>CharSequence</code>. Initial capacity will be the
- * length of the sequence plus 16; if the sequence reports a length
- * less than or equal to 0, then the initial capacity will be 16.
+ * length of the sequence plus the default capacity; if the sequence
+ * reports a length less than or equal to 0, then the initial capacity
+ * will be the default.
*
* @param seq the initializing <code>CharSequence</code>
* @throws NullPointerException if str is null
value[i] = seq.charAt(i);
}
- /**
- * Increase the capacity of this <code>StringBuffer</code>. This will
- * ensure that an expensive growing operation will not occur until
- * <code>minimumCapacity</code> is reached. The buffer is grown to the
- * larger of <code>minimumCapacity</code> and
- * <code>capacity() * 2 + 2</code>, if it is not already large enough.
- *
- * @param minimumCapacity the new capacity
- * @see #capacity()
- */
- public void ensureCapacity(int minimumCapacity)
- {
- ensureCapacity_unsynchronized(minimumCapacity);
- }
-
/**
* Set the length of this StringBuffer. If the new length is greater than
* the current length, all the new characters are set to '\0'. If the new
int valueLength = value.length;
- /* Always call ensureCapacity_unsynchronized in order to preserve
+ /* Always call ensureCapacity in order to preserve
copy-on-write semantics. */
- ensureCapacity_unsynchronized(newLength);
+ ensureCapacity(newLength);
if (newLength < valueLength)
{
if (index < 0 || index >= count)
throw new StringIndexOutOfBoundsException(index);
// Call ensureCapacity to enforce copy-on-write.
- ensureCapacity_unsynchronized(count);
+ ensureCapacity(count);
value[index] = ch;
}
if (str == null)
str = "null";
int len = str.length();
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
{
if (offset < 0 || count < 0 || offset > data.length - count)
throw new StringIndexOutOfBoundsException();
- ensureCapacity_unsynchronized(this.count + count);
+ ensureCapacity(this.count + count);
System.arraycopy(data, offset, value, this.count, count);
this.count += count;
return this;
*/
public CPStringBuilder append(char ch)
{
- ensureCapacity_unsynchronized(count + 1);
+ ensureCapacity(count + 1);
value[count++] = ch;
return this;
}
return append("null");
if (end - start > 0)
{
- ensureCapacity_unsynchronized(count + end - start);
+ ensureCapacity(count + end - start);
for (; start < end; ++start)
value[count++] = seq.charAt(start);
}
public CPStringBuilder appendCodePoint(int code)
{
int len = Character.charCount(code);
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
Character.toChars(code, value, count);
count += len;
return this;
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
- ensureCapacity_unsynchronized(count);
+ ensureCapacity(count);
if (count - end != 0)
System.arraycopy(value, end, value, start, count - end);
count -= end - start;
int len = str.length();
// Calculate the difference in 'count' after the replace.
int delta = len - (end > count ? count : end) + start;
- ensureCapacity_unsynchronized(count + delta);
+ ensureCapacity(count + delta);
if (delta != 0 && end < count)
System.arraycopy(value, end, value, end + delta, count - end);
if (offset < 0 || offset > count || len < 0
|| str_offset < 0 || str_offset > str.length - len)
throw new StringIndexOutOfBoundsException();
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
System.arraycopy(str, str_offset, value, offset, len);
count += len;
if (str == null)
str = "null";
int len = str.length();
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
str.getChars(0, len, value, offset);
count += len;
if (start < 0 || end < 0 || start > end || end > sequence.length())
throw new IndexOutOfBoundsException();
int len = end - start;
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
for (int i = start; i < end; ++i)
value[offset++] = sequence.charAt(i);
{
if (offset < 0 || offset > count)
throw new StringIndexOutOfBoundsException(offset);
- ensureCapacity_unsynchronized(count + 1);
+ ensureCapacity(count + 1);
System.arraycopy(value, offset, value, offset + 1, count - offset);
value[offset] = ch;
count++;
public CPStringBuilder reverse()
{
// Call ensureCapacity to enforce copy-on-write.
- ensureCapacity_unsynchronized(count);
+ ensureCapacity(count);
for (int i = count >> 1, j = count - i; --i >= 0; ++j)
{
char c = value[i];
// If we save more than 200 characters, shrink.
// If we save more than 1/4 of the buffer, shrink.
if (wouldSave > 200 || wouldSave * 4 > value.length)
- {
- char[] newValue = new char[count];
- System.arraycopy(value, 0, newValue, 0, count);
- value = newValue;
- }
+ allocateArray(count);
}
/**
/**
* Increase the capacity of this <code>StringBuilder</code>. This will
- * ensure that an expensive growing operation will not occur until
- * <code>minimumCapacity</code> is reached. The buffer is grown to the
- * larger of <code>minimumCapacity</code> and
+ * ensure that an expensive growing operation will not occur until either
+ * <code>minimumCapacity</code> is reached or the array has been allocated.
+ * The buffer is grown to the larger of <code>minimumCapacity</code> and
* <code>capacity() * 2 + 2</code>, if it is not already large enough.
*
* @param minimumCapacity the new capacity
* @see #capacity()
*/
- protected void ensureCapacity_unsynchronized(int minimumCapacity)
+ public void ensureCapacity(int minimumCapacity)
{
- if (minimumCapacity > value.length)
+ if (allocated || minimumCapacity > value.length)
{
int max = value.length * 2 + 2;
minimumCapacity = (minimumCapacity < max ? max : minimumCapacity);
- char[] nb = new char[minimumCapacity];
- System.arraycopy(value, 0, nb, 0, count);
- value = nb;
+ allocateArray(minimumCapacity);
}
}
+ /**
+ * Allocates a new character array. This method is triggered when
+ * a write is attempted after the array has been passed to a
+ * {@link String} object, so that the builder does not modify
+ * the immutable {@link String}.
+ *
+ * @param capacity the size of the new array.
+ */
+ private void allocateArray(int capacity)
+ {
+ char[] nb = new char[capacity];
+ System.arraycopy(value, 0, nb, 0, count);
+ value = nb;
+ allocated = false;
+ }
+
/**
* Get the length of the <code>String</code> this <code>StringBuilder</code>
* would create. Not to be confused with the <em>capacity</em> of the
}
/**
- * Creates a substring of this StringBuilder, starting at a specified index
+ * Creates a substring of this CPStringBuilder, starting at a specified index
+ * and ending at the end of this StringBuilder.
+ *
+ * @param beginIndex index to start substring (base 0)
+ * @return new String which is a substring of this StringBuilder
+ * @throws StringIndexOutOfBoundsException if beginIndex is out of bounds
+ * @see #substring(int, int)
+ */
+ public String substring(int beginIndex)
+ {
+ return substring(beginIndex, count);
+ }
+
+ /**
+ * Creates a substring of this CPStringBuilder, starting at a specified index
* and ending at one character before a specified index.
*
* @param beginIndex index to start at (inclusive, base 0)
int len = endIndex - beginIndex;
if (len == 0)
return "";
+ allocated = true;
return VMCPStringBuilder.toString(value, beginIndex, len);
}
/**
- * Convert this <code>StringBuilder</code> to a <code>String</code>. The
+ * Convert this <code>CPStringBuilder</code> to a <code>String</code>. The
* String is composed of the characters currently in this StringBuilder. Note
- * that the result is not a copy, so future modifications to this buffer
- * do affect the String.
+ * that the result is not a copy, so we flag this here and make sure to
+ * allocate a new array on the next write attempt (see {@link #ensureCapacity(int)}).
*
* @return the characters in this StringBuilder
*/
public String toString()
{
+ allocated = true;
return VMCPStringBuilder.toString(value, 0, count);
}