/*
* 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 java.lang.Comparable;
import javax.realtime.MemoryArea;
import javolution37.javolution.JavolutionError;
import javolution37.javolution.realtime.HeapContext;
import javolution37.javolution.realtime.Realtime;
import javolution37.javolution.realtime.RealtimeObject;
import javolution37.javolution.util.FastComparator;
import javolution37.javolution.util.FastMap;
/**
* This class represents an immutable character sequence with extremely
* fast {@link #concat concatenation}, {@link #insert insertion} and
* {@link #delete deletion} capabilities (O[Log(n)]) instead of
* O[n] for StringBuffer/StringBuilder).
* Instances of this class have the following advantages over
* {@link String}
:java.lang.String or CharSequence. As for any CharSequence, parsing of primitive types can
* be achieved using the {@link javolution37.javolution.lang.TypeFormat} utility class.
{@link Text} literals should be explicitely {@link #intern interned}. * Unlike strings literals and strings-value constant expressions, * interning is not implicit. For example:[code] * final static Text TRUE = Text.intern("true"); * final static Text FALSE = Text.intern("false"); * [/code] * Interned texts are always allocated in ImmortalMemory (RTSJ VMs).
* Implementation Note: To avoid expensive copy operations ,
* {@link Text} instances are broken down into smaller immutable
* sequences (they form a minimal-depth binary tree).
* Internal copies are then performed in O[Log(n)]
* instead of O[n]).
"null" character sequence.
*/
public static final Text NULL = Text.intern("null");
/**
* Holds the total number of characters.
*/
int _count;
/**
* Holds the hash code or 0 if unknown.
*/
int _hashCode;
/**
* Default constructor.
*/
private Text() {
}
/**
* Returns the text representing the specified object.
* If the specified object is null this method
* returns {@link #NULL}.
*
* @param obj the object to represent as text.
* @return the textual representation of the specified object.
* @see Realtime#toText
*/
public static Text valueOf(Object obj) {
if (obj instanceof String) {
return StringWrapper.newInstance((String) obj);
} else if (obj instanceof Realtime) {
return ((Realtime) obj).toText();
} else if (obj instanceof CharSequence) {
final CharSequence csq = (CharSequence) obj;
return Text.valueOf(csq, 0, csq.length());
} else if (obj != null) {
return StringWrapper.newInstance(obj.toString());
} else {
return NULL;
}
}
/**
* Returns the text that contains the specified subsequence of characters.
*
* @param csq the character sequence source.
* @param start the index of the first character.
* @param end the index after the last character.
* @return the corresponding text instance.
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0)
* || (start > end) || (end > csq.length())
*/
public static Text valueOf(CharSequence csq, int start, int end) {
if ((start < 0) || (end < 0) || (start > end) || (end > csq.length()))
throw new IndexOutOfBoundsException();
final int length = end - start;
if (length <= Primitive.BLOCK_SIZE) {
Primitive text = Primitive.newInstance(length);
for (int i = 0; i < length;) {
text._data[i] = csq.charAt(start + i++);
}
return text;
} else {
final int middle = start + (length >> 1);
Composite text = Composite.newInstance(Text.valueOf(csq, start,
middle), Text.valueOf(csq, middle, end));
return text;
}
}
/**
* Returns the text that contains the characters from the specified
* array.
*
* @param chars the array source of the characters.
* @return the corresponding instance.
*/
public static Text valueOf(char[] chars) {
return valueOf(chars, 0, chars.length);
}
/**
* Returns the text that contains the characters from the specified
* subarray of characters.
*
* @param chars the source of the characters.
* @param offset the index of the first character in the data soure.
* @param length the length of the text returned.
* @return the corresponding instance.
* @throws IndexOutOfBoundsException if (offset < 0) ||
* (length < 0) || ((offset + length) > chars.length)
*/
public static Text valueOf(char[] chars, int offset, int length) {
if ((offset < 0) || (length < 0) || ((offset + length) > chars.length))
throw new IndexOutOfBoundsException();
if (length <= Primitive.BLOCK_SIZE) {
Primitive text = Primitive.newInstance(length);
for (int i = 0; i < length;) {
text._data[i] = chars[offset + i++];
}
return text;
} else {
final int middle = offset + (length >> 1);
Composite text = Composite.newInstance(Text.valueOf(chars, offset,
middle - offset), Text.valueOf(chars, middle, offset
+ length - middle));
return text;
}
}
/**
* Returns the text representation of the boolean argument.
*
* @param b a boolean.
* @return if the argument is true, the text
* "true" is returned; otherwise, the text
* "false" is returned.
*/
public static Text valueOf(boolean b) {
return b ? TRUE : FALSE;
}
private static final Text TRUE = Text.intern("true");
private static final Text FALSE = Text.intern("false");
/**
* Returns the {@link #intern unique} text instance corresponding to the
* specified character.
*
* @param c a character.
* @return a text of length 1 containing 'c'.
*/
public static Text valueOf(char c) {
if ((c < 128) && (ASCII[c] != null))
return ASCII[c];
Primitive text = Primitive.newInstance(1);
text._data[0] = c;
Text textIntern = Text.intern(text);
if (c < 128) {
ASCII[c] = textIntern;
}
return textIntern;
}
private static final Text[] ASCII = new Text[128];
/**
* Returns the decimal representation of the specified int
* argument.
*
* @param i the int to format.
* @return the corresponding text instance.
*/
public static Text valueOf(int i) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(i, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Returns the radix representation of the specified int
* argument.
*
* @param i the int to format.
* @param radix the radix (e.g. 16 for hexadecimal).
* @return the corresponding text instance.
*/
public static Text valueOf(int i, int radix) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(i, radix, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Returns the decimal representation of the specified long
* argument.
*
* @param l the long to format.
* @return the corresponding text instance.
*/
public static Text valueOf(long l) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(l, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Returns the radix representation of the specified long
* argument.
*
* @param l the long to format.
* @param radix the radix (e.g. 16 for hexadecimal).
* @return the corresponding text instance.
*/
public static Text valueOf(long l, int radix) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(l, radix, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**
* Returns the textual representation of the specified float
* instance.
*
* @param f the float to format.
* @return the corresponding text instance.
/**/
public static Text valueOf(float f) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(f, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**/
/**
* Returns the textual representation of the specified double
* argument.
*
* @param d the double to format.
* @return the corresponding text instance.
/**/
public static Text valueOf(double d) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(d, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**/
/**
* Returns the textual representation of 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 the corresponding text instance.
* @throws IllegalArgumentException if ((digits > 19) ||
* (digits <= 0)))
/**/
public static Text valueOf(double value, int digits,
boolean scientific, boolean showZero) {
try {
Primitive text = Primitive.newInstance(0);
TypeFormat.format(value, digits, scientific, showZero, text);
return text;
} catch (IOException e) {
throw new JavolutionError(e);
}
}
/**/
/**
* Returns the length of this text.
*
* @return the number of characters (16-bits Unicode) composing this text.
*/
public final int length() {
return _count;
}
/**
* Returns the concatenation of this text and the textual
* representation of the specified object.
*
* @param obj the object whose textual representation is appended.
* @return this.concat(Text.valueOf(obj))
*/
public final Text plus(Object obj) {
return this.concat(Text.valueOf(obj));
}
/**
* Concatenates the specified text to the end of this text.
* This method is extremely fast (faster even than
* StringBuffer.append(String)) and still returns
* a text instance with an internal binary tree of minimal depth!
*
* @param that the text that is concatenated.
* @return this + that
*/
public final Text concat(Text that) {
if (this._count == 0) {
return that;
} else if (that._count == 0) {
return this;
} else if (((that._count << 1) < this._count)
&& (this instanceof Composite)) { // this too large, break up?
Composite thisComposite = (Composite) this;
if (thisComposite._head._count > thisComposite._tail._count) {
return Composite.newInstance(thisComposite._head,
thisComposite._tail.concat(that));
} else {
return Composite.newInstance(this, that);
}
} else if (((this._count << 1) < that._count)
&& (that instanceof Composite)) { // that too large, break up?
Composite thatComposite = (Composite) that;
if (thatComposite._head._count < thatComposite._tail._count) {
return Composite.newInstance(this.concat(thatComposite._head),
thatComposite._tail);
} else {
return Composite.newInstance(this, that);
}
} else { //
return Composite.newInstance(this, that);
}
}
/**
* Returns a portion of this text.
*
* @param start the index of the first character inclusive.
* @return the sub-text starting at the specified position.
* @throws IndexOutOfBoundsException if (start < 0) ||
* (start > this.length())
*/
public final Text subtext(int start) {
return subtext(start, length());
}
/**
* Returns the text having the specified text inserted at
* the specified location.
*
* @param index the insertion position.
* @param txt the text being inserted.
* @return subtext(0, index).concat(txt).concat(subtext(index))
* @throws IndexOutOfBoundsException if (index < 0) ||
* (index > this.length())
*/
public final Text insert(int index, Text txt) {
// Concatenates the smallest part first.
return ((index << 1) < _count) ? this.subtext(0, index).concat(txt)
.concat(this.subtext(index)) : this.subtext(0, index).concat(
txt.concat(this.subtext(index)));
}
/**
* Returns the text without the characters between the specified indexes.
*
* @param start the beginning index, inclusive.
* @param end the ending index, exclusive.
* @return subtext(0, start).concat(subtext(end))
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0) ||
* (start > end) || (end > this.length()
*/
public final Text delete(int start, int end) {
return this.subtext(0, start).concat(this.subtext(end));
}
/**
* Replaces each character sequence of this text that matches the specified
* target sequence with the specified replacement sequence.
*
* @param target the text to be replaced.
* @param replacement the replacement text.
* @return the resulting text.
*/
public final Text replace(Text target, Text replacement) {
int i = indexOf(target);
return (i < 0) ? this : // No target sequence found.
this.subtext(0, i).concat(replacement).concat(
this.subtext(i + target.length()).replace(target,
replacement));
}
/**
* Returns {@link #subtext(int, int) subtext(start, end)}.
*
* @param start the index of the first character inclusive.
* @param end the index of the last character exclusive.
* @return this.subtext(start, end)
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0) ||
* (start > end) || (end > this.length())
*/
public final CharSequence subSequence(int start, int end) {
return this.subtext(start, end);
}
/**
* Returns the index within this text of the first occurrence
* of the specified character sequence searching forward.
*
* @param csq a character sequence.
* @return the index of the first character of the character sequence found;
* or -1 if the character sequence is not found.
*/
public final int indexOf(CharSequence csq) {
return indexOf(csq, 0);
}
/**
* Returns the index within this text of the first occurrence
* of the specified characters sequence searching forward from
* the specified index.
*
* @param csq a character sequence.
* @param fromIndex the index to start the search from.
* @return the index in the range
* [fromIndex, length() - csq.length()]
* or -1 if the character sequence is not found.
*/
public final int indexOf(CharSequence csq, int fromIndex) {
// Limit cases.
final int csqLength = csq.length();
final int min = Math.max(0, fromIndex);
final int max = _count - csqLength;
if (csqLength == 0) {
return (min > max) ? -1 : min;
}
// Searches for csq.
final char c = csq.charAt(0);
for (int i = indexOf(c, min); (i >= 0) && (i <= max); i = indexOf(c,
++i)) {
boolean match = true;
for (int j = 1; j < csqLength; j++) {
if (this.charAt(i + j) != csq.charAt(j)) {
match = false;
break;
}
}
if (match) {
return i;
}
}
return -1;
}
/**
* Returns the index within this text of the last occurrence of
* the specified characters sequence searching backward.
*
* @param csq a character sequence.
* @return the index of the first character of the character sequence found;
* or -1 if the character sequence is not found.
*/
public final int lastIndexOf(CharSequence csq) {
return lastIndexOf(csq, _count);
}
/**
* Returns the index within this text of the last occurrence of
* the specified character sequence searching backward from the specified
* index.
*
* @param csq a character sequence.
* @param fromIndex the index to start the backward search from.
* @return the index in the range [0, fromIndex] or
* -1 if the character sequence is not found.
*/
public final int lastIndexOf(CharSequence csq, int fromIndex) {
// Limit cases.
final int csqLength = csq.length();
final int min = 0;
final int max = Math.min(fromIndex, _count - csqLength);
if (csqLength == 0) {
return (min > max) ? -1 : max;
}
// Searches for csq.
final char c = csq.charAt(0);
for (int i = lastIndexOf(c, max); (i >= 0); i = lastIndexOf(c, --i)) {
boolean match = true;
for (int j = 1; j < csqLength; j++) {
if (this.charAt(i + j) != csq.charAt(j)) {
match = false;
break;
}
}
if (match) {
return i;
}
}
return -1;
}
/**
* Indicates if this text starts with the specified prefix.
*
* @param prefix the prefix.
* @return true if the character sequence represented by the
* argument is a prefix of the character sequence represented by
* this text; false otherwise.
*/
public final boolean startsWith(CharSequence prefix) {
return startsWith(prefix, 0);
}
/**
* Indicates if this text ends with the specified suffix.
*
* @param suffix the suffix.
* @return true if the character sequence represented by the
* argument is a suffix of the character sequence represented by
* this text; false otherwise.
*/
public final boolean endsWith(CharSequence suffix) {
return startsWith(suffix, length() - suffix.length());
}
/**
* Indicates if this text starts with the specified prefix
* at the specified index.
*
* @param prefix the prefix.
* @param index the index of the prefix location in this string.
* @return this.substring(index).startsWith(prefix)
*/
public final boolean startsWith(CharSequence prefix, int index) {
final int prefixLength = prefix.length();
if ((index >= 0) && (index <= (this.length() - prefixLength))) {
for (int i = 0, j = index; i < prefixLength;) {
if (prefix.charAt(i++) != this.charAt(j++)) {
return false;
}
}
return true;
} else {
return false;
}
}
/**
* Returns a copy of this text, with leading and trailing
* whitespace omitted.
*
* @return a copy of this text with leading and trailing white
* space removed, or this text if it has no leading or
* trailing white space.
*/
public final Text trim() {
int first = 0; // First character index.
int last = length() - 1; // Last character index.
while ((first <= last) && (charAt(first) <= ' ')) {
first++;
}
while ((last >= first) && (charAt(last) <= ' ')) {
last--;
}
return subtext(first, last + 1);
}
/**
* Returns a text equals to the specified character sequence from a pool of
* unique text instances in ImmortalMemory.
*
* @return an unique text instance allocated in ImmortalMemory.
*/
public static Text intern(final CharSequence csq) {
Text text = (Text) INTERN_TEXT.get(csq); // Thread-Safe - No entry removed.
if (text != null)
return text;
synchronized (INTERN_TEXT) {
// Synchronized check to ensure unicity.
text = (Text) INTERN_TEXT.get(csq);
if (text != null)
return text;
MemoryArea.getMemoryArea(INTERN_TEXT).executeInArea(new Runnable() {
public void run() {
HeapContext.enter(); // Prevents stack allocations.
try {
Text tmp = Text.valueOf(csq, 0, csq.length()); // Copies.
INTERN_TEXT.put(tmp, tmp);
} finally {
HeapContext.exit();
}
}
});
}
return (Text) INTERN_TEXT.get(csq);
}
/**
* Returns a text equals to the specified string from a pool of
* unique text instances in ImmortalMemory.
*
* @return an unique text instance allocated in ImmortalMemory.
*/
public static Text intern(final String str) {
Text text = (Text) INTERN_TEXT.get(str); // Thread-Safe - No entry removed.
if (text != null)
return text;
synchronized (INTERN_TEXT) {
// Synchronized check to ensure unicity.
text = (Text) INTERN_TEXT.get(str);
if (text != null)
return text;
MemoryArea.getMemoryArea(INTERN_TEXT).executeInArea(new Runnable() {
public void run() {
HeapContext.enter(); // Prevents stack allocations.
try {
Text tmp = Text.valueOf(new String(str)); // Copies.
INTERN_TEXT.put(tmp, tmp);
} finally {
HeapContext.exit();
}
}
});
}
return (Text) INTERN_TEXT.get(str);
}
/**
* Indicates if this text has the same character content as the specified
* character sequence.
*
* @param csq the character sequence to compare with.
* @return true if the specified character sequence has the
* same character content as this text; false otherwise.
*/
public final boolean contentEquals(CharSequence csq) {
if (csq.length() != _count)
return false;
for (int i = 0; i < _count;) {
if (this.charAt(i) != csq.charAt(i++))
return false;
}
return true;
}
/**
* Indicates if this text has the same character contend as the specified
* character sequence ignoring case considerations.
*
* @param csq the CharSequence to compare this text against.
* @return true if the argument and this text are equal,
* ignoring case; false otherwise.
*/
public final boolean contentEqualsIgnoreCase(CharSequence csq) {
if (this._count != csq.length())
return false;
for (int i = 0; i < _count;) {
char u1 = this.charAt(i);
char u2 = csq.charAt(i++);
if (u1 != u2) {
u1 = Character.toUpperCase(u1);
u2 = Character.toUpperCase(u2);
if ((u1 != u2)
&& (Character.toLowerCase(u1) != Character
.toLowerCase(u2)))
return false;
}
}
return true;
}
/**
* Compares this text against the specified object for equality.
* Returns true if the specified object is a text having
* the same character sequence as this text.
* For generic comparaison with any character sequence the
* {@link #contentEquals(CharSequence)} should be used.
*
* @param obj the object to compare with or null.
* @return true if that is a text with the same character
* sequence as this text; false otherwise.
*/
public final boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Text))
return false;
final Text that = (Text) obj;
if (this._count != that._count)
return false;
for (int i = 0; i < _count;) {
if (this.charAt(i) != that.charAt(i++))
return false;
}
return true;
}
/**
* Returns the hash code for this text.
*
* @return the hash code value.
*/
public final int hashCode() {
if (_hashCode != 0)
return _hashCode;
int h = _hashCode;
final int length = this.length();
for (int i = 0; i < length;) {
h = 31 * h + charAt(i++);
}
return _hashCode = h;
}
/**
* Compares this text to another character sequence or string
* lexicographically.
*
* @param csq the character sequence to be compared.
* @return TypeFormat.LEXICAL_COMPARATOR.compare(this, csq)
* @throws ClassCastException if the specifed object is not a
* CharSequence or a String.
*/
public final int compareTo(Object csq) {
return FastComparator.LEXICAL.compare(this, csq);
}
/**
* Returns this (implements
* {@link javolution37.javolution.realtime.Realtime Realtime} interface).
*
* @return this
*/
public final Text toText() {
return this;
}
/**
* Returns a compact copy of this text allocated on the stack when
* executing in a {@link javolution37.javolution.realtime.PoolContext PoolContext}.
*
* @return a local compact copy of this text.
*/
public final Text copy() {
return Text.valueOf(this, 0, _count);
}
/**
* Returns the depth of the internal tree used to represent this text.
*
* @return the maximum depth of the text internal binary tree.
*/
public abstract int depth();
/**
* 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 abstract char charAt(int index);
/**
* Returns the index within this text of the first occurrence of the
* specified character, starting the search at the specified index.
*
* @param c the character to search for.
* @param fromIndex the index to start the search from.
* @return the index of the first occurrence of the character in this text
* that is greater than or equal to fromIndex,
* or -1 if the character does not occur.
*/
public abstract int indexOf(char c, int fromIndex);
/**
* Returns the index within this text of the first occurrence of the
* specified character, searching backward and starting at the specified
* index.
*
* @param c the character to search for.
* @param fromIndex the index to start the search backward from.
* @return the index of the first occurrence of the character in this text
* that is less than or equal to fromIndex,
* or -1 if the character does not occur.
*/
public abstract int lastIndexOf(char c, int fromIndex);
/**
* Returns a portion of this text.
*
* @param start the index of the first character inclusive.
* @param end the index of the last character exclusive.
* @return the sub-text starting at the specified start position and
* ending just before the specified end position.
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0) ||
* (start > end) || (end > this.length())
*/
public abstract Text subtext(int start, int end);
/**
* Copies the characters from this text into the destination
* character array.
*
* @param start the index of the first character to copy.
* @param end the index after the last character to copy.
* @param dest the destination array.
* @param destPos the start offset in the destination array.
* @throws IndexOutOfBoundsException if (start < 0) || (end < 0) ||
* (start > end) || (end > this.length())
*/
public abstract void getChars(int start, int end, char dest[], int destPos);
/**
* Converts the characters of this text to lower case.
*
* @return the text in lower case.
* @see Character#toLowerCase(char)
*/
public abstract Text toLowerCase();
/**
* Converts the characters of this text to upper case.
*
* @return the text in lower case.
* @see Character#toUpperCase(char)
*/
public abstract Text toUpperCase();
/**
* Returns the String value corresponding to this text.
*
* @return the java.lang.String for this text.
*/
public abstract String stringValue();
//////////////////////////////////////////////////////////////////
// Wilfried add-ons (methods provided by Microsoft .Net in C#)
//
/**
* Returns the text that contains a specific length sequence of the
* character specified.
*
* @param c the character to fill this text with.
* @param length the length of the text returned.
* @return the corresponding instance.
* @throws IndexOutOfBoundsException if (length < 0)
*/
public static Text valueOf(char c, int length) {
if (length < 0)
throw new IndexOutOfBoundsException();
if (length <= Primitive.BLOCK_SIZE) {
Primitive text = Primitive.newInstance(length);
for (int i = 0; i < length;) {
text._data[i++] = c;
}
return text;
} else {
final int middle = (length >> 1);
return Composite.newInstance(Text.valueOf(c, middle), Text.valueOf(
c, length - middle));
}
}
/**
* Indicates if all characters of this text are whitespaces
* (no characters greater than the space character).
*
*@return true if this text contains only whitespace.
*/
public final boolean isBlank() {
return isBlank(0, length());
}
/**
* Indicates if the specified sub-range of characters of this text
* are whitespaces (no characters greater than the space character).
*
*@param start the start index.
*@param length the number of characters to inspect.
*/
public final boolean isBlank(int start, int length) {
for (; start < length; start++) {
if (charAt(start) > ' ')
return false;
}
return true;
}
/**
* Returns a copy of this text, with leading whitespace omitted.
*
* @return a copy of this text with leading white space removed,
* or this text if it has no leading white space.
*/
public final Text trimStart() {
int first = 0; // First character index.
int last = length() - 1; // Last character index.
while ((first <= last) && (charAt(first) <= ' ')) {
first++;
}
return subtext(first, last + 1);
}
/**
* Returns a copy of this text, with trailing
* whitespace omitted.
*
* @return a copy of this text with trailing white space removed,
* or this text if it has no trailing white space.
*/
public final Text trimEnd() {
int first = 0; // First character index.
int last = length() - 1; // Last character index.
while ((last >= first) && (charAt(last) <= ' ')) {
last--;
}
return subtext(first, last + 1);
}
/**
* Pads this text on the left with spaces to make the minimum total length
* as specified.
* The new length of the new text is equal to the original length plus
* (length()-len) spaces.
*
* @param len the total number of characters to make this text equal to.
* @return a new text or the same text if no padding required.
* @throws an IllegalArgumentException if the (len<0).
*/
public final Text padLeft(int len) {
return padLeft(len, ' ');
}
/**
* Pads this text on the left to make the minimum total length as specified.
* Spaces or the given Unicode character are used to pad with.
* (length()-len) pad characters.
*
* @param len the total number of characters to make this text equal to.
* @param c the character to pad using.
* @return a new text or the same text if no padding required.
* @throws an IllegalArgumentException if the (len<0).
*/
public final Text padLeft(int len, char c) {
final int padSize = (len <= length()) ? 0 : len - length();
return this.insert(0, Text.valueOf(c, padSize));
}
/**
* Pads this text on the right with spaces to make the minimum total length
* as specified.
* The new length of the new text is equal to the original length plus
* (length()-len) spaces.
*
* @param len the total number of characters to make this text equal to.
* @return a new text or the same text if no padding required.
* @throws an IllegalArgumentException if the (len<0).
*/
public final Text padRight(int len) {
return padRight(len, ' ');
}
/**
* Pads this text on the right to make the minimum total length as specified.
* Spaces or the given Unicode character are used to pad with.
* (length()-len) pad characters.
*
* @param len the total number of characters to make this text equal to.
* @param c the character to pad using.
* @return a new text or the same text if no padding required.
* @throws an IllegalArgumentException if the (len<0).
*/
public final Text padRight(int len, char c) {
final int padSize = (len <= length()) ? 0 : len - length();
return this.concat(Text.valueOf(c, padSize));
}
/**
* Returns the index within this text of the first occurrence
* of any character in the specified character set.
*
* @param charSet the character set.
* @return the index of the first character that matches one of the
* characters in the supplied set; or -1 if none.
*/
public final int indexOfAny(CharSet charSet) {
return indexOfAny(charSet, 0, length());
}
/**
* Returns the index within a region of this text of the first occurrence
* of any character in the specified character set.
*
* @param charSet the character set.
* @param start the index of the start of the search region in this text.
* @return the index of the first character that matches one of the
* characters in the supplied set; or -1 if none.
*/
public final int indexOfAny(CharSet charSet, int start) {
return indexOfAny(charSet, start, length());
}
/**
* Returns the index within a region of this text of the first occurrence
* of any character in the specified character set.
*
* @param charSet the character set.
* @param start the index of the start of the search region in this text.
* @param length the length of the region to search.
* @return the index of the first character that matches one of the
* characters in the supplied array; or -1 if none.
*/
public final int indexOfAny(CharSet charSet, int start, int length) {
final int stop = start + length;
for (int i = start; i < stop; i++) {
if (charSet.contains(charAt(i)))
return i;
}
return -1;
}
/**
* Returns the index within this text of the last occurrence
* of any character in the specified character set.
*
* @param charSet the character set.
* @return the index of the last character that matches one of the
* characters in the supplied array; or -1 if none.
*/
public final int lastIndexOfAny(CharSet charSet) {
return lastIndexOfAny(charSet, 0, length());
}
/**
* Returns the index within a region of this text of the last occurrence
* of any character in the specified character set.
*
* @param charSet the character set.
* @param start the index of the start of the search region in this text.
* @return the index of the last character that matches one of the
* characters in the supplied array; or -1 if none.
*/
public final int lastIndexOfAny(CharSet charSet, int start) {
return lastIndexOfAny(charSet, start, length() - start);
}
/**
* Returns the index within a region of this text of the last occurrence
* of any character in the specified character set.
*
* @param charSet the character set.
* @param start the index of the start of the search region in this text.
* @param length the length of the region to search.
* @return the index of the last character that matches one of the
* characters in the supplied array; or -1 if none.
*/
public final int lastIndexOfAny(CharSet charSet, int start, int length) {
for (int i = start + length; --i >= start;) {
if (charSet.contains(charAt(i)))
return i;
}
return -1;
}
//
////////////////////////////////////////////////////////////////////////////
/**
* This class represents a text block (up to 32 characters).
*/
private static final class Primitive extends Text implements Appendable {
/**
* Holds the default size for primitive blocks of characters.
*/
private static final int BLOCK_SIZE = 32;
/**
* Holds the associated factory.
*/
private static final Factory FACTORY = new Factory() {
public Object create() {
return new Primitive();
}
};
/**
* Holds the raw data (primitive).
*/
private final char[] _data = new char[BLOCK_SIZE];
/**
* Default constructor.
*/
private Primitive() {
}
/**
* Returns a new/recycled primitive text of specified length.
*
* @param the length of this primitive text.
*/
private static Primitive newInstance(int length) {
Primitive text = (Primitive) FACTORY.object();
text._count = length;
text._hashCode = 0;
return text;
}
// Implements abstract method.
public int depth() {
return 0;
}
// Implements abstract method.
public char charAt(int index) {
if (index >= _count)
throw new IndexOutOfBoundsException();
return _data[index];
}
// Implements abstract method.
public int indexOf(char c, int fromIndex) {
for (int i = Math.max(fromIndex, 0); i < _count; i++) {
if (_data[i] == c)
return i;
}
return -1;
}
// Implements abstract method.
public int lastIndexOf(char c, int fromIndex) {
for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) {
if (_data[i] == c)
return i;
}
return -1;
}
// Implements abstract method.
public Text subtext(int start, int end) {
if ((start == 0) && (end == _count))
return this;
if ((start < 0) || (start > end) || (end > _count))
throw new IndexOutOfBoundsException();
if (start == end)
return Text.EMPTY;
Primitive text = Primitive.newInstance(end - start);
for (int i = start, j = 0; i < end;) {
text._data[j++] = _data[i++];
}
return text;
}
// Implements abstract method.
public void getChars(int start, int end, char dest[], int destPos) {
if ((end > _count) || (end < start))
throw new IndexOutOfBoundsException();
for (int i = start, j = destPos; i < end;) {
dest[j++] = _data[i++];
}
}
// Implements abstract method.
public Text toLowerCase() {
Primitive text = newInstance(_count);
for (int i = 0; i < _count;) {
text._data[i] = Character.toLowerCase(_data[i++]);
}
return text;
}
// Implements abstract method.
public Text toUpperCase() {
Primitive text = newInstance(_count);
for (int i = 0; i < _count;) {
text._data[i] = Character.toUpperCase(_data[i++]);
}
return text;
}
// Implements abstract method.
public String stringValue() {
return new String(_data, 0, _count);
}
// Implements appendable (for primitive types formatting).
public Appendable append(char c) throws IOException {
_data[_count++] = c;
return this;
}
// Implements appendable (for primitive types formatting).
public Appendable append(CharSequence csq) throws IOException {
return append(csq, 0, csq.length());
}
// Implements appendable (for primitive types formatting).
public Appendable append(CharSequence csq, int start, int end)
throws IOException {
for (int i = start; i < end;) {
_data[_count++] = csq.charAt(i++);
}
return this;
}
}
/**
* This class represents a text composite.
*/
private static final class Composite extends Text {
/**
* Holds the associtate factory.
*/
private static final Factory FACTORY = new Factory() {
public Object create() {
return new Composite();
}
};
/**
* Holds the head block of character (composite).
*/
private Text _head;
/**
* Holds the tail block of character (composite).
*/
private Text _tail;
/**
* Default constructor.
*/
private Composite() {
}
/**
* Returns a new/recycled composite text.
*
* @param head the head of this composite.
* @param tail the tail of this composite.
* @return the corresponding composite text.
*/
private static Composite newInstance(Text head, Text tail) {
Composite text = (Composite) FACTORY.object();
text._hashCode = 0;
text._count = head._count + tail._count;
text._head = head;
text._tail = tail;
return text;
}
// Implements abstract method.
public int depth() {
return Math.max(_head.depth(), _tail.depth()) + 1;
}
// Implements abstract method.
public char charAt(int index) {
return (index < _head._count) ? _head.charAt(index) : _tail
.charAt(index - _head._count);
}
// Implements abstract method.
public int indexOf(char c, int fromIndex) {
final int cesure = _head._count;
if (fromIndex < cesure) {
final int headIndex = _head.indexOf(c, fromIndex);
if (headIndex >= 0)
return headIndex; // Found in head.
}
final int tailIndex = _tail.indexOf(c, fromIndex - cesure);
return (tailIndex >= 0) ? tailIndex + cesure : -1;
}
// Implements abstract method.
public int lastIndexOf(char c, int fromIndex) {
final int cesure = _head._count;
if (fromIndex >= cesure) {
final int tailIndex = _tail.lastIndexOf(c, fromIndex - cesure);
if (tailIndex >= 0)
return tailIndex + cesure; // Found in tail.
}
return _head.lastIndexOf(c, fromIndex);
}
// Implements abstract method.
public Text subtext(int start, int end) {
final int cesure = _head._count;
if (end <= cesure)
return _head.subtext(start, end);
if (start >= cesure)
return _tail.subtext(start - cesure, end - cesure);
if ((start == 0) && (end == _count))
return this;
// Overlaps head and tail.
return _head.subtext(start, cesure).concat(
_tail.subtext(0, end - cesure));
}
// Implements abstract method.
public void getChars(int start, int end, char dest[], int destPos) {
final int cesure = _head._count;
if (end <= cesure) {
_head.getChars(start, end, dest, destPos);
} else if (start >= cesure) {
_tail.getChars(start - cesure, end - cesure, dest, destPos);
} else { // Overlaps head and tail.
_head.getChars(start, cesure, dest, destPos);
_tail.getChars(0, end - cesure, dest, destPos + cesure - start);
}
}
// Implements abstract method.
public Text toLowerCase() {
return newInstance(_head.toLowerCase(), _tail.toLowerCase());
}
// Implements abstract method.
public Text toUpperCase() {
return newInstance(_head.toUpperCase(), _tail.toUpperCase());
}
// Implements abstract method.
public String stringValue() {
char[] data = new char[_count];
this.getChars(0, _count, data, 0);
return new String(data, 0, _count);
}
// Overrides.
public boolean move(ObjectSpace os) {
if (super.move(os)) {
_head.move(os);
_tail.move(os);
return true;
}
return false;
}
}
/**
* This class represents a String Wrapper.
*/
private static final class StringWrapper extends Text {
/**
* Holds the associated factory.
*/
private static final Factory FACTORY = new Factory() {
public Object create() {
return new StringWrapper();
}
};
/**
* Holds the string.
*/
private String _string;
/**
* Holds the offset.
*/
private int _offset;
/**
* Default constructor.
*/
private StringWrapper() {
}
/**
* Returns a new/recycled text wrapping the specified string.
*
* @param str the string to wrap.
* @return the corresponding text instance.
*/
private static StringWrapper newInstance(String str) {
StringWrapper text = (StringWrapper) FACTORY.object();
text._count = str.length();
text._hashCode = 0;
text._string = str;
text._offset = 0;
return text;
}
// Implements abstract method.
public int depth() {
return 0;
}
// Implements abstract method.
public char charAt(int index) {
if ((index >= _count) || (index < 0))
throw new IndexOutOfBoundsException();
return _string.charAt(_offset + index);
}
// Implements abstract method.
public int indexOf(char c, int fromIndex) {
for (int i = Math.max(fromIndex, 0); i < _count; i++) {
if (_string.charAt(_offset + i) == c)
return i;
}
return -1;
}
// Implements abstract method.
public int lastIndexOf(char c, int fromIndex) {
for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) {
if (_string.charAt(_offset + i) == c)
return i;
}
return -1;
}
// Implements abstract method.
public Text subtext(int start, int end) {
if ((start == 0) && (end == _count))
return this;
if ((start < 0) || (start > end) || (end > _count))
throw new IndexOutOfBoundsException();
if (start == end)
return Text.EMPTY;
StringWrapper text = (StringWrapper) FACTORY.object();
text._count = end - start;
text._hashCode = 0;
text._string = _string;
text._offset = _offset + start;
return text;
}
// Implements abstract method.
public void getChars(int start, int end, char dest[], int destPos) {
if ((end > _count) || (end < start) || (start < 0))
throw new IndexOutOfBoundsException();
_string.getChars(start + _offset, end + _offset, dest, destPos);
}
// Implements abstract method.
public Text toLowerCase() {
return copy().toLowerCase(); // To avoid dynamic heap allocation.
}
// Implements abstract method.
public Text toUpperCase() {
return copy().toUpperCase(); // To avoid dynamic heap allocation.
}
// Implements abstract method.
public String stringValue() {
if ((_offset == 0) && (_count == _string.length()))
return _string;
return _string.substring(_offset, _offset + _count);
}
}
}
Ads help maintain this website.