/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2005 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution37.javolution.lang;
import java.io.IOException;
import java.io.Serializable;
import java.lang.CharSequence;
import javax.realtime.MemoryArea;
import javolution37.javolution.JavolutionError;
import javolution37.javolution.realtime.Realtime;
import javolution37.javolution.realtime.RealtimeObject;
/**
* This class represents an {@link Appendable} text whose capacity expands
* gently without incurring expensive resize/copy operations ever.
*
* This class is not intended for large documents manipulations which
* should be performed with the {@link Text} class directly
* (O(Log(n)) {@link Text#insert insertion} and
* {@link Text#delete deletion} capabilities).
*
* This implementation is not synchronized.
*
* @author Jean-Marie Dautelle
* @version 3.7, January 1, 2006
*/
public class TextBuilder extends RealtimeObject implements Appendable,
java.lang.CharSequence, Reusable, Serializable {
/**
* Holds the factory for this text builder.
*/
private static final Factory FACTORY = new Factory() {
public Object create() {
return new TextBuilder();
}
public void cleanup(Object obj) {
((TextBuilder) obj).reset();
}
};
//
// Holds the character buffers. The array sizes are adjusted to ensures that
// no more than 4 time the required space is ever allocated.
//
// char[1<>R3)&M3][(i>>R2)&M2][(i>>R1)&M1][(i>>R0)&M0]
//
private static final int D0 = 5;
private static final int M0 = (1 << D0) - 1;
private static final int C0 = 1 << D0; // capacity chars0
private static final int D1 = D0 + 2;
private static final int R1 = D0;
private static final int M1 = (1 << D1) - 1;
private static final int C1 = 1 << (D0 + D1); // capacity chars1
private static final int D2 = D1 + 2;
private static final int R2 = D0 + D1;
private static final int M2 = (1 << D2) - 1;
private static final int C2 = 1 << (D0 + D1 + D2); // capacity chars2
private static final int D3 = D2 + 2;
private static final int R3 = D0 + D1 + D2;
private final char[] _chars0 = new char[1 << D0]; // 5 bits (32).
private char[][] _chars1; // new char[1<<7][1<<5]; // 12 bits (4096)
private char[][][] _chars2; // new char[1<<9][1<<7][1<<5]; // 21 bits (2097152)
private char[][][][] _chars3; // new char[1<<11][1<<9][1<<7][1<<5];
/**
* Holds the current capacity.
*/
private int _capacity = 1 << D0;
/**
* Holds the current length.
*/
private int _length;
/**
* Creates a text builder of small initial capacity.
*/
public TextBuilder() {
}
/**
* Creates a text builder holding the specified character sequence.
*
* @param csq the initial character sequence of this text builder.
*/
public TextBuilder(CharSequence csq) {
append(csq);
}
/**
* Creates a text builder of specified initial capacity.
* Unless the text length exceeds the specified capacity, operations
* on this text builder will not allocate memory.
*
* @param capacity the initial capacity.
*/
public TextBuilder(int capacity) {
while (capacity > _capacity) {
increaseCapacity();
}
}
/**
* Returns a text builder allocated from the "stack" when executing in
* a {@link javolution37.javolution.realtime.PoolContext PoolContext}).
*
* @return a new, preallocated or recycled text builder instance.
*/
public static TextBuilder newInstance() {
return (TextBuilder) FACTORY.object();
}
/**
* Returns the length (character count) of this text builder.
*
* @return the number of characters (16-bits Unicode).
*/
public final int length() {
return _length;
}
/**
* Returns the character at the specified index.
*
* @param index the index of the character.
* @return the character at the specified index.
* @throws IndexOutOfBoundsException if (index < 0) ||
* (index >= this.length()).
*/
public final char charAt(int index) {
if ((index < 0) || (index >= _length))
throw new IndexOutOfBoundsException("index: " + index);
if (index < C0) {
return _chars0[index];
} else if (index < C1) {
return _chars1[(index >> R1)][index & M0];
} else if (index < C2) {
return _chars2[(index >> R2)][(index >> R1) & M1][index & M0];
} else {
return _chars3[(index >> R3)][(index >> R2) & M2][(index >> R1)
& M1][index & M0];
}
}
/**
* Copies the character from this text builder into the destination
* character array.
*
* @param srcBegin this text start index.
* @param srcEnd this text end index (not included).
* @param dst the destination array to copy the data into.
* @param dstBegin the offset into the destination array.
* @throws IndexOutOfBoundsException if (srcBegin < 0) ||
* (dstBegin < 0) || (srcBegin > srcEnd) || (srcEnd > this.length())
* || ((dstBegin + srcEnd - srcBegin) > dst.length)
*/
public final void getChars(int srcBegin, int srcEnd, char[] dst,
int dstBegin) {
if ((srcBegin < 0) || (dstBegin < 0) || (srcBegin > srcEnd)
|| (srcEnd > this.length())
|| ((dstBegin + srcEnd - srcBegin) > dst.length))
throw new IndexOutOfBoundsException();
for (int i = srcBegin, j = dstBegin; i < srcEnd;) {
dst[j++] = charAt(i++);
}
}
/**
* Sets the character at the specified position.
*
* @param index the index of the character to modify.
* @param c the new character.
* @throws IndexOutOfBoundsException if (index < 0) ||
* (index >= this.length())
*/
public final void setCharAt(int index, char c) {
if ((index < 0) || (index >= _length))
throw new IndexOutOfBoundsException("index: " + index);
if (index < C0) {
_chars0[index] = c;
} else if (index < C1) {
_chars1[(index >> R1)][index & M0] = c;
} else if (index < C2) {
_chars2[(index >> R2)][(index >> R1) & M1][index & M0] = c;
} else {
_chars3[(index >> R3)][(index >> R2) & M2][(index >> R1) & M1][index
& M0] = c;
}
}
/**
* Sets the length of this character builder.
* If the length is greater than the current length; the
* null character '\u0000' is inserted.
*
* @param newLength the new length of this builder.
* @throws IndexOutOfBoundsException if (newLength < 0)
*/
public final void setLength(int newLength) {
if (newLength < 0)
throw new IndexOutOfBoundsException();
if (newLength <= _length) {
_length = newLength;
} else {
for (int i = _length; i++ < newLength;) {
append('\u0000');
}
}
}
/**
* Returns an instance of {@link Text} (immutable) corresponding
* to the character sequence between the specified indexes.
*
* @param start the index of the first character inclusive.
* @param end the index of the last character exclusive.
* @return an immutable character sequence.
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0) ||
* (start > end) || (end > this.length())
*/
public final CharSequence subSequence(int start, int end) {
if ((start < 0) || (end < 0) || (start > end) || (end > _length))
throw new IndexOutOfBoundsException();
return Text.valueOf(this, start, end);
}
/**
* Appends the specified character.
*
* @param c the character to append.
* @return this
*/
public final TextBuilder append(char c) {
if (_length >= _capacity)
increaseCapacity();
final int i = _length++;
if (i < C0) {
_chars0[i] = c;
} else if (i < C1) {
_chars1[(i >> R1)][i & M0] = c;
} else if (i < C2) {
_chars2[(i >> R2)][(i >> R1) & M1][i & M0] = c;
} else {
_chars3[(i >> R3)][(i >> R2) & M2][(i >> R1) & M1][i & M0] = c;
}
return this;
}
/**
* Appends the specified character sequence. If the specified character
* sequence is null this method is equivalent to
* append("null").
*
* @param csq the character sequence to append or null.
* @return this
*/
public final TextBuilder append(CharSequence csq) {
return (csq == null) ? append("null") : append(csq, 0, csq.length());
}
/**
* Appends a subsequence of the specified character sequence.
* If the specified character sequence is null this method
* is equivalent to append("null").
*
* @param csq the character sequence to append or null.
* @param start the index of the first character to append.
* @param end the index after the last character to append.
* @return this
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0)
* || (start > end) || (end > csq.length())
*/
public final TextBuilder append(CharSequence csq, int start, int end) {
if (csq == null)
return append("null");
if ((start < 0) || (end < 0) || (start > end) || (end > csq.length()))
throw new IndexOutOfBoundsException();
for (int i = start; i < end;) {
append(csq.charAt(i++));
}
return this;
}
/**
* Appends the textual representation of the specified object.
* If the specified object is null this method
* is equivalent to append("null").
*
* @param obj the object to represent or null.
* @return this
*/
public final TextBuilder append(Object obj) {
if (obj instanceof String) {
return append((String) obj);
} else if (obj instanceof CharSequence) {
return (TextBuilder) append((CharSequence) obj);
} else if (obj instanceof Realtime) {
return append(((Realtime) obj).toText());
} else if (obj != null) {
return append(obj.toString());
} else {
return append("null");
}
}
/**
* Appends the specified string to this text builder.
* If the specified string is null this method
* is equivalent to append("null").
*
* @param str the string to append or null.
* @return this
*/
public final TextBuilder append(String str) {
if (str == null)
return append("null");
final int length = str.length();
for (int i = 0; i < length;) {
append(str.charAt(i++));
}
return this;
}
/**
* Appends a subsequence of the specified string.
* If the specified character sequence is null this method
* is equivalent to append("null").
*
* @param str the string to append or null.
* @param start the index of the first character to append.
* @param end the index after the last character to append.
* @return this
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0)
* || (start > end) || (end > csq.length())
*/
public final TextBuilder append(String str, int start, int end) {
if (str == null)
return append("null");
if ((start < 0) || (end < 0) || (start > end) || (end > str.length()))
throw new IndexOutOfBoundsException();
for (int i = start; i < end;) {
append(str.charAt(i++));
}
return this;
}
/**
* Appends the specified text to this text builder.
* If the specified text is null this method
* is equivalent to append("null").
*
* @param text the text to append or null.
* @return this
*/
public TextBuilder append(Text text) {
if (text == null)
return append("null");
final int length = text.length();
for (int i = 0; i < length;) {
append(text.charAt(i++));
}
return this;
}
/**
* Appends the characters from the char array argument.
*
* @param chars the character array source.
* @return this
*/
public final TextBuilder append(char chars[]) {
return append(chars, 0, chars.length);
}
/**
* Appends the characters from a subarray of the char array argument.
*
* @param chars the character array source.
* @param offset the index of the first character to append.
* @param length the number of character to append.
* @return this
* @throws IndexOutOfBoundsException if (offset < 0) ||
* (length < 0) || ((offset + length) > chars.length)
*/
public final TextBuilder append(char chars[], int offset, int length) {
if ((offset < 0) || (length < 0) || ((offset + length) > chars.length))
throw new IndexOutOfBoundsException();
final int end = offset + length;
for (int i = offset; i < end;) {
append(chars[i++]);
}
return this;
}
/**
* Appends the textual representation of the specified boolean
* (equivalent to TypeFormat.format(b, this)).
*
* @param b the boolean to format.
* @return this
* @see TypeFormat
*/
public final TextBuilder append(boolean b) {
try {
TypeFormat.format(b, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Appends the decimal representation of the specified int
* (equivalent to TypeFormat.format(i, this)).
*
* @param i the int to format.
* @return this
* @see TypeFormat
*/
public final TextBuilder append(int i) {
try {
TypeFormat.format(i, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Appends the radix representation of the specified int
* argument.
*
* @param i the int to format.
* @param radix the radix (e.g. 16 for hexadecimal).
* @return this
* @see TypeFormat
*/
public final TextBuilder append(int i, int radix) {
try {
TypeFormat.format(i, radix, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Appends the decimal representation of the specified long
* (equivalent to TypeFormat.format(l, this)).
*
* @param l the long to format.
* @return this
* @see TypeFormat
*/
public final TextBuilder append(long l) {
try {
TypeFormat.format(l, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Appends the radix representation of the specified long
* argument.
*
* @param l the long to format.
* @param radix the radix (e.g. 16 for hexadecimal).
* @return this
* @see TypeFormat
*/
public final TextBuilder append(long l, int radix) {
try {
TypeFormat.format(l, radix, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Appends the textual representation of the specified float
* (equivalent to TypeFormat.format(f, this)).
*
* @param f the float to format.
* @return this
* @see TypeFormat
/**/
public final TextBuilder append(float f) {
try {
TypeFormat.format(f, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**/
/**
* Appends the textual representation of the specified double
* (equivalent to TypeFormat.format(d, this)).
*
* @param d the double to format.
* @return this
* @see TypeFormat
/**/
public final TextBuilder append(double d) {
try {
TypeFormat.format(d, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**/
/**
* Appends the specified double value according to the
* specified formatting arguments.
*
* @param value the double value.
* @param digits the number of significative digits (excludes exponent).
* @param scientific true to forces the use of the scientific
* notation (e.g. 1.23E3); false
* otherwise.
* @param showZero true if trailing fractional zeros are
* represented; false otherwise.
* @return this
* @throws IllegalArgumentException if ((digits > 19) ||
* (digits <= 0)))
/**/
public final TextBuilder append(double value, int digits,
boolean scientific, boolean showZero) {
try {
TypeFormat.format(value, digits, scientific, showZero, this);
return this;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**/
/**
* Inserts the specified character sequence at the specified location.
*
* @param index the insertion position.
* @param csq the character sequence being inserted.
* @return this
* @throws IndexOutOfBoundsException if (index < 0) ||
* (index > this.length())
*/
public final TextBuilder insert(int index, CharSequence csq) {
if ((index < 0) || (index > _length))
throw new IndexOutOfBoundsException("index: " + index);
final int shift = csq.length();
_length += shift;
while (_length >= _capacity) {
increaseCapacity();
}
for (int i = _length - shift; --i >= index;) {
this.setCharAt(i + shift, this.charAt(i));
}
for (int i = csq.length(); --i >= 0;) {
this.setCharAt(index + i, csq.charAt(i));
}
return this;
}
/**
* Removes the characters between the specified indices.
*
* @param start the beginning index, inclusive.
* @param end the ending index, exclusive.
* @return this
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0)
* || (start > end) || (end > this.length())
*/
public final TextBuilder delete(int start, int end) {
if ((start < 0) || (end < 0) || (start > end) || (end > this.length()))
throw new IndexOutOfBoundsException();
for (int i = end, j = start; i < _length;) {
this.setCharAt(j++, this.charAt(i++));
}
_length -= end - start;
return this;
}
/**
* Reverses this character sequence.
*
* @return this
*/
public final TextBuilder reverse() {
final int n = _length - 1;
for (int j = (n - 1) >> 1; j >= 0;) {
char c = charAt(j);
setCharAt(j, charAt(n - j));
setCharAt(n - j--, c);
}
return this;
}
/**
* Returns the {@link Text} corresponding to this {@link TextBuilder}
* (allocated on the "stack" when executing in a
* {@link javolution37.javolution.realtime.PoolContext PoolContext}).
*
* @return the corresponding {@link Text} instance.
*/
public final Text toText() {
return Text.valueOf(this, 0, this.length());
}
/**
* Resets this text builder for reuse (sets its length to 0).
*/
public final void reset() {
setLength(0);
}
/**
* Returns the hash code for this text builder.
*
* @return the hash code value.
*/
public final int hashCode() {
int h = 0;
for (int i = 0; i < _length;) {
h = 31 * h + charAt(i++);
}
return h;
}
/**
* Compares this text builder against the specified object for equality.
* Returns true if the specified object is a text builder
* having the same character content.
*
* @param obj the object to compare with or null.
* @return true if that is a text builder with the same
* character content as this text; false otherwise.
*/
public final boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof TextBuilder))
return false;
TextBuilder that = (TextBuilder) obj;
if (this._length != that._length)
return false;
for (int i = 0; i < _length;) {
if (this.charAt(i) != that.charAt(i++))
return false;
}
return true;
}
/**
* Increases this text builder capacity.
*/
private void increaseCapacity() {
MemoryArea.getMemoryArea(this).executeInArea(new Runnable() {
public void run() {
final int c = _capacity;
_capacity += 1 << D0;
if (c < C1) {
if (_chars1 == null) {
_chars1 = new char[1 << D1][];
}
_chars1[(c >> R1)] = new char[1 << D0];
} else if (c < C2) {
if (_chars2 == null) {
_chars2 = new char[1 << D2][][];
}
if (_chars2[(c >> R2)] == null) {
_chars2[(c >> R2)] = new char[1 << D1][];
}
_chars2[(c >> R2)][(c >> R1) & M1] = new char[1 << D0];
} else {
if (_chars3 == null) {
_chars3 = new char[1 << D3][][][];
}
if (_chars3[(c >> R3)] == null) {
_chars3[(c >> R3)] = new char[1 << D2][][];
}
if (_chars3[(c >> R3)][(c >> R2) & M2] == null) {
_chars3[(c >> R3)][(c >> R2) & M2] = new char[1 << D1][];
}
_chars3[(c >> R3)][(c >> R2) & M2][(c >> R1) & M1] = new char[1 << D0];
}
}
});
}
}
Ads help maintain this website.