0001: /*
0002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
0003: * Copyright (C) 2005 - Javolution (http://javolution.org/)
0004: * All rights reserved.
0005: *
0006: * Permission to use, copy, modify, and distribute this software is
0007: * freely granted, provided that this notice is preserved.
0008: */
0009: package javolution.text;
0010:
0011: import java.io.IOException;
0012: import java.io.Writer;
0013:
0014: import j2me.lang.CharSequence;
0015: import j2me.lang.Comparable;
0016: import j2me.lang.Number;
0017: import j2me.lang.ThreadLocal;
0018: import j2mex.realtime.MemoryArea;
0019: import javolution.JavolutionError;
0020: import javolution.context.ObjectFactory;
0021: import javolution.io.UTF8StreamWriter;
0022: import javolution.lang.MathLib;
0023: import javolution.lang.Realtime;
0024: import javolution.lang.ValueType;
0025: import javolution.util.FastComparator;
0026: import javolution.util.FastMap;
0027: import javolution.xml.XMLSerializable;
0028:
0029: /**
0030: * <p> This class represents an immutable character sequence with
0031: * fast {@link #concat concatenation}, {@link #insert insertion} and
0032: * {@link #delete deletion} capabilities (O[Log(n)]) instead of
0033: * O[n] for StringBuffer/StringBuilder).</p>
0034: * <p> This class has the same methods as
0035: * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html">
0036: * Java String</a> and
0037: * <a href="http://msdn2.microsoft.com/en-us/library/system.string.aspx">
0038: * .NET String</a> with the following benefits:<ul>
0039: * <li> No need for an intermediate
0040: * {@link StringBuffer}/{@link StringBuilder} in order to manipulate
0041: * textual documents (insertion, deletion or concatenation).</li>
0042: * <li> Bug free. They are not plagued by the {@link String#substring} <a
0043: * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4513622">
0044: * memory leak bug</a> (when small substrings prevent memory from
0045: * larger string from being garbage collected).</li>
0046: * <li> More flexible as they allows for search and comparison with any
0047: * <code>java.lang.String</code> or <code>CharSequence</code>.</li>
0048: * <li> Support custom allocation policies (instances allocated on the
0049: * "stack" when executing in a
0050: * {@link javolution.context.StackContext StackContext}).</li>
0051: * </ul></p>
0052: * <p> {@link Text} literals should be explicitely {@link #intern interned}.
0053: * Unlike strings literals and strings-value constant expressions,
0054: * interning is not implicit. For example:[code]
0055: * final static Text TRUE = Text.intern("true");
0056: * final static Text FALSE = Text.intern("false");
0057: * [/code]
0058: * Interned texts are always allocated in ImmortalMemory (RTSJ VMs).</p>
0059: * <p> {@link Text} instances can be {@link #println printed out} directly
0060: * (no intermediate <code>String</code> allocated). For example:[code]
0061: * FastTable myTable ...;
0062: * myTable.toText().println(); // Prints to System.out
0063: * [/code]</p>
0064: *
0065: * <p><i> Implementation Note: To avoid expensive copy operations ,
0066: * {@link Text} instances are broken down into smaller immutable
0067: * sequences, they form a minimal-depth binary tree.
0068: * The tree is maintained balanced automatically through <a
0069: * href="http://en.wikipedia.org/wiki/Tree_rotation">tree rotations</a>.
0070: * Insertion/deletions are performed in <code>O[Log(n)]</code>
0071: * instead of <code>O[n]</code> for
0072: * <code>StringBuffer/StringBuilder</code>.</i></p>
0073: *
0074: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
0075: * @author Wilfried Middleton
0076: * @version 5.1, July 1, 2007
0077: */
0078: public final class Text implements CharSequence, Comparable,
0079: XMLSerializable, ValueType, Realtime {
0080:
0081: /**
0082: * Holds the default size for primitive blocks of characters.
0083: */
0084: private static final int BLOCK_SIZE = 1 << 5;
0085:
0086: /**
0087: * Holds the mask used to ensure a block boundary cesures.
0088: */
0089: private static final int BLOCK_MASK = ~(BLOCK_SIZE - 1);
0090:
0091: /**
0092: * Holds the primitive text factory.
0093: */
0094:
0095: /**
0096: * Holds the texts interned in ImmortalMemory
0097: */
0098: private static final FastMap INTERN_INSTANCES = new FastMap()
0099: .setKeyComparator(FastComparator.LEXICAL);
0100:
0101: /**
0102: * Holds the text builder used to create small text instances.
0103: */
0104: private static final ThreadLocal TEXT_BUILDER = new ThreadLocal() {
0105: protected Object initialValue() {
0106: return new TextBuilder();
0107: }
0108: };
0109:
0110: /**
0111: * Holds an empty character sequence.
0112: */
0113: public static final Text EMPTY = Text.intern("");
0114:
0115: /**
0116: * Holds the raw data (primitive) or <code>null</code> (composite).
0117: */
0118: private final char[] _data;
0119:
0120: /**
0121: * Holds the total number of characters.
0122: */
0123: private int _count;
0124:
0125: /**
0126: * Holds the head block of character (composite).
0127: */
0128: private Text _head;
0129:
0130: /**
0131: * Holds the tail block of character (composite).
0132: */
0133: private Text _tail;
0134:
0135: /**
0136: * Creates a new text instance.
0137: *
0138: * @param isPrimitive indicates if primitive or composite.
0139: */
0140: private Text(boolean isPrimitive) {
0141: _data = isPrimitive ? new char[BLOCK_SIZE] : null;
0142: }
0143:
0144: /**
0145: * Creates a text holding the characters from the specified <code>String
0146: * </code>.
0147: *
0148: * @param str the string holding the character content.
0149: */
0150: public Text(String str) {
0151: this (str.length() <= BLOCK_SIZE);
0152: _count = str.length();
0153: if (_data != null) { // Primitive.
0154: str.getChars(0, _count, _data, 0);
0155: } else { // Composite, splits on a block boundary.
0156: int half = ((_count + BLOCK_SIZE) >> 1) & BLOCK_MASK;
0157: _head = new Text(str.substring(0, half));
0158: _tail = new Text(str.substring(half, _count));
0159: }
0160: }
0161:
0162: /**
0163: * Returns the text representing the specified object.
0164: * If the object is an instance of {@link Realtime}
0165: * then {@link Realtime#toText()} is returned.
0166: *
0167: * @param obj the object to represent as text.
0168: * @return the textual representation of the specified object.
0169: */
0170: public static Text valueOf(Object obj) {
0171: if (obj instanceof Realtime)
0172: return ((Realtime) obj).toText();
0173: if (obj instanceof Number) // Use faster primitive formatting.
0174: return Text.valueOfNumber(obj);
0175: return Text.valueOf(String.valueOf(obj));
0176: }
0177:
0178: private static Text valueOf(String str) {
0179: return Text.valueOf(str, 0, str.length());
0180: }
0181:
0182: private static Text valueOf(String str, int start, int end) {
0183: int length = end - start;
0184: if (length <= BLOCK_SIZE) {
0185: Text text = newPrimitive(length);
0186: str.getChars(start, end, text._data, 0);
0187: return text;
0188: } else { // Splits on a block boundary.
0189: int half = ((length + BLOCK_SIZE) >> 1) & BLOCK_MASK;
0190: return newComposite(Text.valueOf(str, start, start + half),
0191: Text.valueOf(str, start + half, end));
0192: }
0193: }
0194:
0195: // For Integer, Long, Float and Double use direct formatting.
0196: private static Text valueOfNumber(Object num) {
0197: if (num instanceof Integer)
0198: return Text.valueOf(((Integer) num).intValue());
0199: if (num instanceof Long)
0200: return Text.valueOf(((Long) num).longValue());
0201: /* @JVM-1.1+@
0202: if (num instanceof Float)
0203: return Text.valueOf(((Float)num).floatValue());
0204: if (num instanceof Double)
0205: return Text.valueOf(((Double)num).doubleValue());
0206: /**/
0207: return Text.valueOf(String.valueOf(num));
0208: }
0209:
0210: /**
0211: * Returns the text that contains the characters from the specified
0212: * array.
0213: *
0214: * @param chars the array source of the characters.
0215: * @return the corresponding instance.
0216: */
0217: public static Text valueOf(char[] chars) {
0218: return Text.valueOf(chars, 0, chars.length);
0219: }
0220:
0221: /**
0222: * Returns the text that contains the characters from the specified
0223: * subarray of characters.
0224: *
0225: * @param chars the source of the characters.
0226: * @param offset the index of the first character in the data soure.
0227: * @param length the length of the text returned.
0228: * @return the corresponding instance.
0229: * @throws IndexOutOfBoundsException if <code>(offset < 0) ||
0230: * (length < 0) || ((offset + length) > chars.length)</code>
0231: */
0232: public static Text valueOf(char[] chars, int offset, int length) {
0233: if ((offset < 0) || (length < 0)
0234: || ((offset + length) > chars.length))
0235: throw new IndexOutOfBoundsException();
0236: if (length <= BLOCK_SIZE) {
0237: Text text = Text.newPrimitive(length);
0238: System.arraycopy(chars, offset, text._data, 0, length);
0239: return text;
0240: } else { // Splits on a block boundary.
0241: int half = ((length + BLOCK_SIZE) >> 1) & BLOCK_MASK;
0242: return Text.newComposite(Text.valueOf(chars, offset, half),
0243: Text.valueOf(chars, offset + half, length - half));
0244: }
0245: }
0246:
0247: /**
0248: * Converts a text builder to a text instance (optimization for
0249: * TextBuilder.toText()).
0250: *
0251: * @param start the index of the first character inclusive.
0252: * @param end the index of the last character exclusive.
0253: * @return the corresponding text instance.
0254: */
0255: static Text valueOf(TextBuilder tb, int start, int end) {
0256: int length = end - start;
0257: if (length <= BLOCK_SIZE) {
0258: Text text = Text.newPrimitive(length);
0259: tb.getChars(start, end, text._data, 0);
0260: return text;
0261: } else { // Splits on a block boundary.
0262: int half = ((length + BLOCK_SIZE) >> 1) & BLOCK_MASK;
0263: return Text.newComposite(Text.valueOf(tb, start, start
0264: + half), Text.valueOf(tb, start + half, end));
0265: }
0266: }
0267:
0268: /**
0269: * Returns the text representation of the <code>boolean</code> argument.
0270: *
0271: * @param b a <code>boolean</code>.
0272: * @return if the argument is <code>true</code>, the text
0273: * <code>"true"</code> is returned; otherwise, the text
0274: * <code>"false"</code> is returned.
0275: */
0276: public static Text valueOf(boolean b) {
0277: return b ? TRUE : FALSE;
0278: }
0279:
0280: private static final Text TRUE = Text.intern("true");
0281:
0282: private static final Text FALSE = Text.intern("false");
0283:
0284: /**
0285: * Returns the text instance corresponding to the specified character.
0286: *
0287: * @param c a character.
0288: * @return a text of length <code>1</code> containing <code>'c'</code>.
0289: */
0290: public static Text valueOf(char c) {
0291: Text text = Text.newPrimitive(1);
0292: text._data[0] = c;
0293: return text;
0294: }
0295:
0296: /**
0297: * Returns the decimal representation of the specified <code>int</code>
0298: * argument.
0299: *
0300: * @param i the <code>int</code> to format.
0301: * @return the corresponding text instance.
0302: */
0303: public static Text valueOf(int i) {
0304: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0305: tmp.clear().append(i);
0306: return tmp.toText();
0307: }
0308:
0309: /**
0310: * Returns the radix representation of the specified <code>int</code>
0311: * argument.
0312: *
0313: * @param i the <code>int</code> to format.
0314: * @param radix the radix (e.g. <code>16</code> for hexadecimal).
0315: * @return the corresponding text instance.
0316: */
0317: public static Text valueOf(int i, int radix) {
0318: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0319: tmp.clear().append(i, radix);
0320: return tmp.toText();
0321: }
0322:
0323: /**
0324: * Returns the decimal representation of the specified <code>long</code>
0325: * argument.
0326: *
0327: * @param l the <code>long</code> to format.
0328: * @return the corresponding text instance.
0329: */
0330: public static Text valueOf(long l) {
0331: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0332: tmp.clear().append(l);
0333: return tmp.toText();
0334: }
0335:
0336: /**
0337: * Returns the radix representation of the specified <code>long</code>
0338: * argument.
0339: *
0340: * @param l the <code>long</code> to format.
0341: * @param radix the radix (e.g. <code>16</code> for hexadecimal).
0342: * @return the corresponding text instance.
0343: */
0344: public static Text valueOf(long l, int radix) {
0345: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0346: tmp.clear().append(l, radix);
0347: return tmp.toText();
0348: }
0349:
0350: /**
0351: * Returns the textual representation of the specified <code>float</code>
0352: * instance.
0353: *
0354: * @param f the <code>float</code> to format.
0355: * @return the corresponding text instance.
0356: /*@JVM-1.1+@
0357: public static Text valueOf(float f) {
0358: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0359: tmp.clear().append(f);
0360: return tmp.toText();
0361: }
0362: /**/
0363:
0364: /**
0365: * Returns the textual representation of the specified <code>double</code>
0366: * argument.
0367: *
0368: * @param d the <code>double</code> to format.
0369: * @return the corresponding text instance.
0370: /*@JVM-1.1+@
0371: public static Text valueOf(double d) {
0372: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0373: tmp.clear().append(d);
0374: return tmp.toText();
0375: }
0376: /**/
0377:
0378: /**
0379: * Returns the textual representation of the specified <code>double</code>
0380: * argument formatted as specified.
0381: *
0382: * @param d the <code>double</code> to format.
0383: * @param digits the number of significative digits (excludes exponent) or
0384: * <code>-1</code> to mimic the standard library (16 or 17 digits).
0385: * @param scientific <code>true</code> to forces the use of the scientific
0386: * notation (e.g. <code>1.23E3</code>); <code>false</code>
0387: * otherwise.
0388: * @param showZero <code>true</code> if trailing fractional zeros are
0389: * represented; <code>false</code> otherwise.
0390: * @return the corresponding text instance.
0391: * @throws IllegalArgumentException if <code>(digits > 19)</code>)
0392: /*@JVM-1.1+@
0393: public static Text valueOf(double d, int digits,
0394: boolean scientific, boolean showZero) {
0395: TextBuilder tmp = (TextBuilder) TEXT_BUILDER.get();
0396: tmp.clear().append(d, digits, scientific, showZero);
0397: return tmp.toText();
0398: }
0399: /**/
0400:
0401: /**
0402: * Returns the length of this text.
0403: *
0404: * @return the number of characters (16-bits Unicode) composing this text.
0405: */
0406: public int length() {
0407: return _count;
0408: }
0409:
0410: /**
0411: * Returns the concatenation of this text and the textual
0412: * representation of the specified object.
0413: *
0414: * @param obj the object whose textual representation is appended.
0415: * @return <code>this.concat(Text.valueOf(obj))</code>
0416: */
0417: public Text plus(Object obj) {
0418: return this .concat(Text.valueOf(obj));
0419: }
0420:
0421: /**
0422: * Concatenates the specified text to the end of this text.
0423: * This method is very fast (faster even than
0424: * <code>StringBuffer.append(String)</code>) and still returns
0425: * a text instance with an internal binary tree of minimal depth!
0426: *
0427: * @param that the text that is concatenated.
0428: * @return <code>this + that</code>
0429: */
0430: public Text concat(Text that) {
0431: // All Text instances are maintained balanced:
0432: // (head < tail * 2) & (tail < head * 2)
0433:
0434: final int length = this ._count + that._count;
0435: if (length <= BLOCK_SIZE) { // Merges to primitive.
0436: Text text = Text.newPrimitive(length);
0437: this .getChars(0, this ._count, text._data, 0);
0438: that.getChars(0, that._count, text._data, this ._count);
0439: return text;
0440:
0441: } else { // Returns a composite.
0442: Text head = this ;
0443: Text tail = that;
0444: if (((head._count << 1) < tail._count)
0445: && (tail._data == null)) { // tail is composite
0446: // head too small, returns (head + tail/2) + (tail/2)
0447: if (tail._head._count > tail._tail._count) {
0448: // Rotates to concatenate with smaller part.
0449: tail = tail.rightRotation();
0450: }
0451: head = head.concat(tail._head);
0452: tail = tail._tail;
0453:
0454: } else if (((tail._count << 1) < head._count)
0455: && (head._data == null)) { // head is composite.
0456: // tail too small, returns (head/2) + (head/2 concat tail)
0457: if (head._tail._count > head._head._count) {
0458: // Rotates to concatenate with smaller part.
0459: head = head.leftRotation();
0460: }
0461: tail = head._tail.concat(tail);
0462: head = head._head;
0463: }
0464: return Text.newComposite(head, tail);
0465: }
0466: }
0467:
0468: private Text rightRotation() {
0469: // See: http://en.wikipedia.org/wiki/Tree_rotation
0470: Text P = this ._head;
0471: if (P._data != null)
0472: return this ; // Head not a composite, cannot rotate.
0473: Text A = P._head;
0474: Text B = P._tail;
0475: Text C = this ._tail;
0476: return Text.newComposite(A, Text.newComposite(B, C));
0477: }
0478:
0479: private Text leftRotation() {
0480: // See: http://en.wikipedia.org/wiki/Tree_rotation
0481: Text Q = this ._tail;
0482: if (Q._data != null)
0483: return this ; // Tail not a composite, cannot rotate.
0484: Text B = Q._head;
0485: Text C = Q._tail;
0486: Text A = this ._head;
0487: return Text.newComposite(Text.newComposite(A, B), C);
0488: }
0489:
0490: /**
0491: * Returns a portion of this text.
0492: *
0493: * @param start the index of the first character inclusive.
0494: * @return the sub-text starting at the specified position.
0495: * @throws IndexOutOfBoundsException if <code>(start < 0) ||
0496: * (start > this.length())</code>
0497: */
0498: public Text subtext(int start) {
0499: return subtext(start, length());
0500: }
0501:
0502: /**
0503: * Returns the text having the specified text inserted at
0504: * the specified location.
0505: *
0506: * @param index the insertion position.
0507: * @param txt the text being inserted.
0508: * @return <code>subtext(0, index).concat(txt).concat(subtext(index))</code>
0509: * @throws IndexOutOfBoundsException if <code>(index < 0) ||
0510: * (index > this.length())</code>
0511: */
0512: public Text insert(int index, Text txt) {
0513: return subtext(0, index).concat(txt).concat(subtext(index));
0514: }
0515:
0516: /**
0517: * Returns the text without the characters between the specified indexes.
0518: *
0519: * @param start the beginning index, inclusive.
0520: * @param end the ending index, exclusive.
0521: * @return <code>subtext(0, start).concat(subtext(end))</code>
0522: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
0523: * (start > end) || (end > this.length()</code>
0524: */
0525: public Text delete(int start, int end) {
0526: if (start > end)
0527: throw new IndexOutOfBoundsException();
0528: return subtext(0, start).concat(subtext(end));
0529: }
0530:
0531: /**
0532: * Replaces each character sequence of this text that matches the specified
0533: * target sequence with the specified replacement sequence.
0534: *
0535: * @param target the character sequence to be replaced.
0536: * @param replacement the replacement sequence.
0537: * @return the resulting text.
0538: */
0539: public Text replace(CharSequence target, CharSequence replacement) {
0540: int i = indexOf(target);
0541: return (i < 0) ? this : // No target sequence found.
0542: subtext(0, i).concat(Text.valueOf(replacement)).concat(
0543: subtext(i + target.length()).replace(target,
0544: replacement));
0545: }
0546:
0547: /**
0548: * Replaces the specified characters in this text with the specified
0549: * replacement sequence.
0550: *
0551: * @param charSet the set of characters to be replaced.
0552: * @param replacement the replacement sequence.
0553: * @return the resulting text.
0554: */
0555: public Text replace(CharSet charSet, CharSequence replacement) {
0556: int i = indexOfAny(charSet);
0557: return (i < 0) ? this : // No character to replace.
0558: subtext(0, i).concat(Text.valueOf(replacement)).concat(
0559: subtext(i + 1).replace(charSet, replacement));
0560: }
0561:
0562: /**
0563: * Returns {@link #subtext(int, int) subtext(start, end)}.
0564: *
0565: * @param start the index of the first character inclusive.
0566: * @param end the index of the last character exclusive.
0567: * @return <code>this.subtext(start, end)</code>
0568: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
0569: * (start > end) || (end > this.length())</code>
0570: */
0571: public CharSequence subSequence(int start, int end) {
0572: return subtext(start, end);
0573: }
0574:
0575: /**
0576: * Returns the index within this text of the first occurrence
0577: * of the specified character sequence searching forward.
0578: *
0579: * @param csq a character sequence.
0580: * @return the index of the first character of the character sequence found;
0581: * or <code>-1</code> if the character sequence is not found.
0582: */
0583: public int indexOf(CharSequence csq) {
0584: return indexOf(csq, 0);
0585: }
0586:
0587: /**
0588: * Returns the index within this text of the first occurrence
0589: * of the specified characters sequence searching forward from
0590: * the specified index.
0591: *
0592: * @param csq a character sequence.
0593: * @param fromIndex the index to start the search from.
0594: * @return the index in the range
0595: * <code>[fromIndex, length() - csq.length()]</code>
0596: * or <code>-1</code> if the character sequence is not found.
0597: */
0598: public int indexOf(CharSequence csq, int fromIndex) {
0599:
0600: // Limit cases.
0601: final int csqLength = csq.length();
0602: final int min = Math.max(0, fromIndex);
0603: final int max = _count - csqLength;
0604: if (csqLength == 0) {
0605: return (min > max) ? -1 : min;
0606: }
0607:
0608: // Searches for csq.
0609: final char c = csq.charAt(0);
0610: for (int i = indexOf(c, min); (i >= 0) && (i <= max); i = indexOf(
0611: c, ++i)) {
0612: boolean match = true;
0613: for (int j = 1; j < csqLength; j++) {
0614: if (this .charAt(i + j) != csq.charAt(j)) {
0615: match = false;
0616: break;
0617: }
0618: }
0619: if (match) {
0620: return i;
0621: }
0622: }
0623: return -1;
0624: }
0625:
0626: /**
0627: * Returns the index within this text of the last occurrence of
0628: * the specified characters sequence searching backward.
0629: *
0630: * @param csq a character sequence.
0631: * @return the index of the first character of the character sequence found;
0632: * or <code>-1</code> if the character sequence is not found.
0633: */
0634: public int lastIndexOf(CharSequence csq) {
0635: return lastIndexOf(csq, _count);
0636: }
0637:
0638: /**
0639: * Returns the index within this text of the last occurrence of
0640: * the specified character sequence searching backward from the specified
0641: * index.
0642: *
0643: * @param csq a character sequence.
0644: * @param fromIndex the index to start the backward search from.
0645: * @return the index in the range <code>[0, fromIndex]</code> or
0646: * <code>-1</code> if the character sequence is not found.
0647: */
0648: public int lastIndexOf(CharSequence csq, int fromIndex) {
0649:
0650: // Limit cases.
0651: final int csqLength = csq.length();
0652: final int min = 0;
0653: final int max = Math.min(fromIndex, _count - csqLength);
0654: if (csqLength == 0) {
0655: return (min > max) ? -1 : max;
0656: }
0657:
0658: // Searches for csq.
0659: final char c = csq.charAt(0);
0660: for (int i = lastIndexOf(c, max); (i >= 0); i = lastIndexOf(c,
0661: --i)) {
0662: boolean match = true;
0663: for (int j = 1; j < csqLength; j++) {
0664: if (this .charAt(i + j) != csq.charAt(j)) {
0665: match = false;
0666: break;
0667: }
0668: }
0669: if (match) {
0670: return i;
0671: }
0672: }
0673: return -1;
0674:
0675: }
0676:
0677: /**
0678: * Indicates if this text starts with the specified prefix.
0679: *
0680: * @param prefix the prefix.
0681: * @return <code>true</code> if the character sequence represented by the
0682: * argument is a prefix of the character sequence represented by
0683: * this text; <code>false</code> otherwise.
0684: */
0685: public boolean startsWith(CharSequence prefix) {
0686: return startsWith(prefix, 0);
0687: }
0688:
0689: /**
0690: * Indicates if this text ends with the specified suffix.
0691: *
0692: * @param suffix the suffix.
0693: * @return <code>true</code> if the character sequence represented by the
0694: * argument is a suffix of the character sequence represented by
0695: * this text; <code>false</code> otherwise.
0696: */
0697: public boolean endsWith(CharSequence suffix) {
0698: return startsWith(suffix, length() - suffix.length());
0699: }
0700:
0701: /**
0702: * Indicates if this text starts with the specified prefix
0703: * at the specified index.
0704: *
0705: * @param prefix the prefix.
0706: * @param index the index of the prefix location in this string.
0707: * @return <code>this.substring(index).startsWith(prefix)</code>
0708: */
0709: public boolean startsWith(CharSequence prefix, int index) {
0710: final int prefixLength = prefix.length();
0711: if ((index >= 0) && (index <= (this .length() - prefixLength))) {
0712: for (int i = 0, j = index; i < prefixLength;) {
0713: if (prefix.charAt(i++) != this .charAt(j++)) {
0714: return false;
0715: }
0716: }
0717: return true;
0718: } else {
0719: return false;
0720: }
0721: }
0722:
0723: /**
0724: * Returns a copy of this text, with leading and trailing
0725: * whitespace omitted.
0726: *
0727: * @return a copy of this text with leading and trailing white
0728: * space removed, or this text if it has no leading or
0729: * trailing white space.
0730: */
0731: public Text trim() {
0732: int first = 0; // First character index.
0733: int last = length() - 1; // Last character index.
0734: while ((first <= last) && (charAt(first) <= ' ')) {
0735: first++;
0736: }
0737: while ((last >= first) && (charAt(last) <= ' ')) {
0738: last--;
0739: }
0740: return subtext(first, last + 1);
0741: }
0742:
0743: /**
0744: * Returns a text equals to the specified character sequence from a pool of
0745: * unique text instances in <code>ImmortalMemory</code>.
0746: *
0747: * @return an unique text instance allocated in <code>ImmortalMemory</code>.
0748: */
0749: public static Text intern(final CharSequence csq) {
0750: Text text = (Text) INTERN_INSTANCES.get(csq); // Thread-Safe - No entry removed.
0751: return (text != null) ? text : Text.internImpl(csq.toString());
0752: }
0753:
0754: /**
0755: * Returns a text equals to the specified string from a pool of
0756: * unique text instances in <code>ImmortalMemory</code>.
0757: *
0758: * @return an unique text instance allocated in <code>ImmortalMemory</code>.
0759: */
0760: public static Text intern(final String str) {
0761: Text text = (Text) INTERN_INSTANCES.get(str); // Thread-Safe - No entry removed.
0762: return (text != null) ? text : Text.internImpl(str);
0763: }
0764:
0765: private static synchronized Text internImpl(final String str) {
0766: if (!INTERN_INSTANCES.containsKey(str)) { // Synchronized check.
0767: MemoryArea.getMemoryArea(INTERN_INSTANCES).executeInArea(
0768: new Runnable() {
0769: public void run() {
0770: Text txt = new Text(str);
0771: INTERN_INSTANCES.put(txt, txt);
0772: }
0773: });
0774: }
0775: return (Text) INTERN_INSTANCES.get(str);
0776: }
0777:
0778: /**
0779: * Indicates if this text has the same character content as the specified
0780: * character sequence.
0781: *
0782: * @param csq the character sequence to compare with.
0783: * @return <code>true</code> if the specified character sequence has the
0784: * same character content as this text; <code>false</code> otherwise.
0785: */
0786: public boolean contentEquals(CharSequence csq) {
0787: if (csq.length() != _count)
0788: return false;
0789: for (int i = 0; i < _count;) {
0790: if (this .charAt(i) != csq.charAt(i++))
0791: return false;
0792: }
0793: return true;
0794: }
0795:
0796: /**
0797: * Indicates if this text has the same character contend as the specified
0798: * character sequence ignoring case considerations.
0799: *
0800: * @param csq the <code>CharSequence</code> to compare this text against.
0801: * @return <code>true</code> if the argument and this text are equal,
0802: * ignoring case; <code>false</code> otherwise.
0803: */
0804: public boolean contentEqualsIgnoreCase(CharSequence csq) {
0805: if (this ._count != csq.length())
0806: return false;
0807: for (int i = 0; i < _count;) {
0808: char u1 = this .charAt(i);
0809: char u2 = csq.charAt(i++);
0810: if (u1 != u2) {
0811: u1 = Character.toUpperCase(u1);
0812: u2 = Character.toUpperCase(u2);
0813: if ((u1 != u2)
0814: && (Character.toLowerCase(u1) != Character
0815: .toLowerCase(u2)))
0816: return false;
0817:
0818: }
0819: }
0820: return true;
0821: }
0822:
0823: /**
0824: * Compares this text against the specified object for equality.
0825: * Returns <code>true</code> if the specified object is a text having
0826: * the same character sequence as this text.
0827: * For generic comparaison with any character sequence the
0828: * {@link #contentEquals(CharSequence)} should be used.
0829: *
0830: * @param obj the object to compare with or <code>null</code>.
0831: * @return <code>true</code> if that is a text with the same character
0832: * sequence as this text; <code>false</code> otherwise.
0833: */
0834: public boolean equals(Object obj) {
0835: if (this == obj)
0836: return true;
0837: if (!(obj instanceof Text))
0838: return false;
0839: final Text that = (Text) obj;
0840: if (this ._count != that._count)
0841: return false;
0842: for (int i = 0; i < _count;) {
0843: if (this .charAt(i) != that.charAt(i++))
0844: return false;
0845: }
0846: return true;
0847: }
0848:
0849: /**
0850: * Returns the hash code for this text.
0851: *
0852: * @return the hash code value.
0853: */
0854: public int hashCode() {
0855: int h = 0;
0856: final int length = this .length();
0857: for (int i = 0; i < length;) {
0858: h = 31 * h + charAt(i++);
0859: }
0860: return h;
0861: }
0862:
0863: /**
0864: * Compares this text to another character sequence or string
0865: * lexicographically.
0866: *
0867: * @param csq the character sequence to be compared.
0868: * @return <code>TypeFormat.LEXICAL_COMPARATOR.compare(this, csq)</code>
0869: * @throws ClassCastException if the specifed object is not a
0870: * <code>CharSequence</code> or a <code>String</code>.
0871: */
0872: public int compareTo(Object csq) {
0873: return ((FastComparator) FastComparator.LEXICAL).compare(this ,
0874: csq);
0875: }
0876:
0877: /**
0878: * Returns <code>this</code> (implements
0879: * {@link javolution.lang.ValueType Realtime} interface).
0880: *
0881: * @return <code>this</code>
0882: */
0883: public Text toText() {
0884: return this ;
0885: }
0886:
0887: /**
0888: * Prints out this text to <code>System.out</code> (UTF-8 encoding).
0889: * This method is equivalent to:[code]
0890: * synchronized(OUT) {
0891: * print(OUT);
0892: * OUT.flush();
0893: * }
0894: * ...
0895: * static final OUT = new UTF8StreamWriter().setOutput(System.out);
0896: * [/code]
0897: */
0898: public void print() {
0899: try {
0900: synchronized (SYSTEM_OUT_WRITER) {
0901: print(SYSTEM_OUT_WRITER);
0902: SYSTEM_OUT_WRITER.flush();
0903: }
0904: } catch (IOException e) { // Should never happen.
0905: throw new JavolutionError(e);
0906: }
0907: }
0908:
0909: private static final UTF8StreamWriter SYSTEM_OUT_WRITER = new UTF8StreamWriter()
0910: .setOutput(System.out);
0911:
0912: /**
0913: * Prints out this text to <code>System.out</code> and then terminates the
0914: * line. This method is equivalent to:[code]
0915: * synchronized(OUT) {
0916: * println(OUT);
0917: * OUT.flush();
0918: * }
0919: * ...
0920: * static final OUT = new UTF8StreamWriter().setOutput(System.out);
0921: * [/code]
0922: */
0923: public void println() {
0924: try {
0925: synchronized (SYSTEM_OUT_WRITER) {
0926: println(SYSTEM_OUT_WRITER);
0927: SYSTEM_OUT_WRITER.flush();
0928: }
0929: } catch (IOException e) { // Should never happen.
0930: throw new JavolutionError(e);
0931: }
0932: }
0933:
0934: /**
0935: * Prints out this text to the specified writer.
0936: *
0937: * @param writer the destination writer.
0938: */
0939: public void print(Writer writer) throws IOException {
0940: if (_data != null) { // Primitive
0941: writer.write(_data, 0, _count);
0942: } else { // Composite.
0943: _head.print(writer);
0944: _tail.print(writer);
0945: }
0946: }
0947:
0948: /**
0949: * Prints out this text to the specified writer and then terminates
0950: * the line.
0951: *
0952: * @param writer the destination writer.
0953: */
0954: public void println(Writer writer) throws IOException {
0955: print(writer);
0956: writer.write('\n');
0957: }
0958:
0959: /**
0960: * Converts the characters of this text to lower case.
0961: *
0962: * @return the text in lower case.
0963: * @see Character#toLowerCase(char)
0964: */
0965: public Text toLowerCase() {
0966: if (_data == null) // Composite.
0967: return Text.newComposite(_head.toLowerCase(), _tail
0968: .toLowerCase());
0969: Text text = Text.newPrimitive(_count);
0970: for (int i = 0; i < _count;) {
0971: text._data[i] = Character.toLowerCase(_data[i++]);
0972: }
0973: return text;
0974: }
0975:
0976: /**
0977: * Converts the characters of this text to upper case.
0978: *
0979: * @return the text in lower case.
0980: * @see Character#toUpperCase(char)
0981: */
0982: public Text toUpperCase() {
0983: if (_data == null) // Composite.
0984: return newComposite(_head.toUpperCase(), _tail
0985: .toUpperCase());
0986: Text text = Text.newPrimitive(_count);
0987: for (int i = 0; i < _count;) {
0988: text._data[i] = Character.toUpperCase(_data[i++]);
0989: }
0990: return text;
0991: }
0992:
0993: /**
0994: * Returns the depth of the internal tree used to represent this text.
0995: *
0996: * @return the maximum depth of the text internal binary tree.
0997: */
0998: public int depth() {
0999: if (_data != null) // Primitive.
1000: return 0;
1001: return MathLib.max(_head.depth(), _tail.depth()) + 1;
1002: }
1003:
1004: /**
1005: * Returns the character at the specified index.
1006: *
1007: * @param index the index of the character.
1008: * @return the character at the specified index.
1009: * @throws IndexOutOfBoundsException if <code>(index < 0) ||
1010: * (index >= this.length())</code>
1011: */
1012: public char charAt(int index) {
1013: if (index >= _count)
1014: throw new IndexOutOfBoundsException();
1015: return (_data != null) ? _data[index]
1016: : (index < _head._count) ? _head.charAt(index) : _tail
1017: .charAt(index - _head._count);
1018: }
1019:
1020: /**
1021: * Returns the index within this text of the first occurrence of the
1022: * specified character, starting the search at the specified index.
1023: *
1024: * @param c the character to search for.
1025: * @param fromIndex the index to start the search from.
1026: * @return the index of the first occurrence of the character in this text
1027: * that is greater than or equal to <code>fromIndex</code>,
1028: * or <code>-1</code> if the character does not occur.
1029: */
1030: public int indexOf(char c, int fromIndex) {
1031: if (_data != null) { // Primitive.
1032: for (int i = MathLib.max(fromIndex, 0); i < _count; i++) {
1033: if (_data[i] == c)
1034: return i;
1035: }
1036: return -1;
1037: } else { // Composite.
1038: final int cesure = _head._count;
1039: if (fromIndex < cesure) {
1040: final int headIndex = _head.indexOf(c, fromIndex);
1041: if (headIndex >= 0)
1042: return headIndex; // Found in head.
1043: }
1044: final int tailIndex = _tail.indexOf(c, fromIndex - cesure);
1045: return (tailIndex >= 0) ? tailIndex + cesure : -1;
1046: }
1047: }
1048:
1049: /**
1050: * Returns the index within this text of the first occurrence of the
1051: * specified character, searching backward and starting at the specified
1052: * index.
1053: *
1054: * @param c the character to search for.
1055: * @param fromIndex the index to start the search backward from.
1056: * @return the index of the first occurrence of the character in this text
1057: * that is less than or equal to <code>fromIndex</code>,
1058: * or <code>-1</code> if the character does not occur.
1059: */
1060: public int lastIndexOf(char c, int fromIndex) {
1061: if (_data != null) { // Primitive.
1062: for (int i = MathLib.min(fromIndex, _count - 1); i >= 0; i--) {
1063: if (_data[i] == c)
1064: return i;
1065: }
1066: return -1;
1067: } else { // Composite.
1068: final int cesure = _head._count;
1069: if (fromIndex >= cesure) {
1070: final int tailIndex = _tail.lastIndexOf(c, fromIndex
1071: - cesure);
1072: if (tailIndex >= 0)
1073: return tailIndex + cesure; // Found in tail.
1074: }
1075: return _head.lastIndexOf(c, fromIndex);
1076: }
1077: }
1078:
1079: /**
1080: * Returns a portion of this text.
1081: *
1082: * @param start the index of the first character inclusive.
1083: * @param end the index of the last character exclusive.
1084: * @return the sub-text starting at the specified start position and
1085: * ending just before the specified end position.
1086: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
1087: * (start > end) || (end > this.length())</code>
1088: */
1089: public Text subtext(int start, int end) {
1090: if (_data != null) { // Primitive.
1091: if ((start < 0) || (start > end) || (end > _count))
1092: throw new IndexOutOfBoundsException();
1093: if ((start == 0) && (end == _count))
1094: return this ;
1095: if (start == end)
1096: return Text.EMPTY;
1097: int length = end - start;
1098: Text text = Text.newPrimitive(length);
1099: System.arraycopy(_data, start, text._data, 0, length);
1100: return text;
1101: } else { // Composite.
1102: final int cesure = _head._count;
1103: if (end <= cesure)
1104: return _head.subtext(start, end);
1105: if (start >= cesure)
1106: return _tail.subtext(start - cesure, end - cesure);
1107: if ((start == 0) && (end == _count))
1108: return this ;
1109: // Overlaps head and tail.
1110: return _head.subtext(start, cesure).concat(
1111: _tail.subtext(0, end - cesure));
1112: }
1113: }
1114:
1115: /**
1116: * Copies the characters from this text into the destination
1117: * character array.
1118: *
1119: * @param start the index of the first character to copy.
1120: * @param end the index after the last character to copy.
1121: * @param dest the destination array.
1122: * @param destPos the start offset in the destination array.
1123: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
1124: * (start > end) || (end > this.length())</code>
1125: */
1126: public void getChars(int start, int end, char dest[], int destPos) {
1127: if (_data != null) { // Primitive.
1128: if ((start < 0) || (end > _count) || (start > end))
1129: throw new IndexOutOfBoundsException();
1130: System.arraycopy(_data, start, dest, destPos, end - start);
1131: } else { // Composite.
1132: final int cesure = _head._count;
1133: if (end <= cesure) {
1134: _head.getChars(start, end, dest, destPos);
1135: } else if (start >= cesure) {
1136: _tail.getChars(start - cesure, end - cesure, dest,
1137: destPos);
1138: } else { // Overlaps head and tail.
1139: _head.getChars(start, cesure, dest, destPos);
1140: _tail.getChars(0, end - cesure, dest, destPos + cesure
1141: - start);
1142: }
1143: }
1144: }
1145:
1146: /**
1147: * Returns the <code>String</code> representation of this text.
1148: *
1149: * @return the <code>java.lang.String</code> for this text.
1150: */
1151: public String toString() {
1152: if (_data != null) { // Primitive.
1153: return new String(_data, 0, _count);
1154: } else { // Composite.
1155: char[] data = new char[_count];
1156: this .getChars(0, _count, data, 0);
1157: return new String(data, 0, _count);
1158: }
1159: }
1160:
1161: // Implements ValueType interface.
1162: public Object/*{Text}*/copy() {
1163: if (_data != null) { // Primitive.
1164: Text text = Text.newPrimitive(_count);
1165: System.arraycopy(_data, 0, text._data, 0, _count);
1166: return text;
1167: } else { // Composite.
1168: return Text.newComposite((Text) _head.copy(), (Text) _tail
1169: .copy());
1170: }
1171: }
1172:
1173: //////////////////////////////////////////////////////////////////
1174: // Wilfried add-ons (methods provided by Microsoft .Net in C#)
1175: //
1176:
1177: /**
1178: * Returns the text that contains a specific length sequence of the
1179: * character specified.
1180: *
1181: * @param c the character to fill this text with.
1182: * @param length the length of the text returned.
1183: * @return the corresponding instance.
1184: * @throws IndexOutOfBoundsException if <code>(length < 0)</code>
1185: */
1186: public static Text valueOf(char c, int length) {
1187: if (length < 0)
1188: throw new IndexOutOfBoundsException();
1189: if (length <= BLOCK_SIZE) {
1190: Text text = Text.newPrimitive(length);
1191: for (int i = 0; i < length;) {
1192: text._data[i++] = c;
1193: }
1194: return text;
1195: } else {
1196: final int middle = (length >> 1);
1197: return Text.newComposite(Text.valueOf(c, middle), Text
1198: .valueOf(c, length - middle));
1199: }
1200: }
1201:
1202: /**
1203: * Indicates if all characters of this text are whitespaces
1204: * (no characters greater than the space character).
1205: *
1206: *@return <code>true</code> if this text contains only whitespace.
1207: */
1208: public boolean isBlank() {
1209: return isBlank(0, length());
1210: }
1211:
1212: /**
1213: * Indicates if the specified sub-range of characters of this text
1214: * are whitespaces (no characters greater than the space character).
1215: *
1216: *@param start the start index.
1217: *@param length the number of characters to inspect.
1218: */
1219: public boolean isBlank(int start, int length) {
1220: for (; start < length; start++) {
1221: if (charAt(start) > ' ')
1222: return false;
1223: }
1224: return true;
1225: }
1226:
1227: /**
1228: * Returns a copy of this text, with leading whitespace omitted.
1229: *
1230: * @return a copy of this text with leading white space removed,
1231: * or this text if it has no leading white space.
1232: */
1233: public Text trimStart() {
1234: int first = 0; // First character index.
1235: int last = length() - 1; // Last character index.
1236: while ((first <= last) && (charAt(first) <= ' ')) {
1237: first++;
1238: }
1239: return subtext(first, last + 1);
1240: }
1241:
1242: /**
1243: * Returns a copy of this text, with trailing
1244: * whitespace omitted.
1245: *
1246: * @return a copy of this text with trailing white space removed,
1247: * or this text if it has no trailing white space.
1248: */
1249: public Text trimEnd() {
1250: int first = 0; // First character index.
1251: int last = length() - 1; // Last character index.
1252: while ((last >= first) && (charAt(last) <= ' ')) {
1253: last--;
1254: }
1255: return subtext(first, last + 1);
1256: }
1257:
1258: /**
1259: * Pads this text on the left with spaces to make the minimum total length
1260: * as specified.
1261: * The new length of the new text is equal to the original length plus
1262: * <code>(length()-len)</code> spaces.
1263: *
1264: * @param len the total number of characters to make this text equal to.
1265: * @return a new text or the same text if no padding required.
1266: * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1267: */
1268: public Text padLeft(int len) {
1269: return padLeft(len, ' ');
1270: }
1271:
1272: /**
1273: * Pads this text on the left to make the minimum total length as specified.
1274: * Spaces or the given Unicode character are used to pad with.
1275: * <br>
1276: * The new length of the new text is equal to the original length plus
1277: * <code>(length()-len)</code> pad characters.
1278: *
1279: * @param len the total number of characters to make this text equal to.
1280: * @param c the character to pad using.
1281: * @return a new text or the same text if no padding required.
1282: * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1283: */
1284: public Text padLeft(int len, char c) {
1285: final int padSize = (len <= length()) ? 0 : len - length();
1286: return insert(0, Text.valueOf(c, padSize));
1287: }
1288:
1289: /**
1290: * Pads this text on the right with spaces to make the minimum total length
1291: * as specified.
1292: * The new length of the new text is equal to the original length plus
1293: * <code>(length()-len)</code> spaces.
1294: *
1295: * @param len the total number of characters to make this text equal to.
1296: * @return a new text or the same text if no padding required.
1297: * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1298: */
1299: public Text padRight(int len) {
1300: return padRight(len, ' ');
1301: }
1302:
1303: /**
1304: * Pads this text on the right to make the minimum total length as specified.
1305: * Spaces or the given Unicode character are used to pad with.
1306: * <br>
1307: * The new length of the new text is equal to the original length plus
1308: * <code>(length()-len)</code> pad characters.
1309: *
1310: * @param len the total number of characters to make this text equal to.
1311: * @param c the character to pad using.
1312: * @return a new text or the same text if no padding required.
1313: * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1314: */
1315: public Text padRight(int len, char c) {
1316: final int padSize = (len <= length()) ? 0 : len - length();
1317: return concat(Text.valueOf(c, padSize));
1318: }
1319:
1320: /**
1321: * Returns the index within this text of the first occurrence
1322: * of any character in the specified character set.
1323: *
1324: * @param charSet the character set.
1325: * @return the index of the first character that matches one of the
1326: * characters in the supplied set; or <code>-1</code> if none.
1327: */
1328: public int indexOfAny(CharSet charSet) {
1329: return indexOfAny(charSet, 0, length());
1330: }
1331:
1332: /**
1333: * Returns the index within a region of this text of the first occurrence
1334: * of any character in the specified character set.
1335: *
1336: * @param charSet the character set.
1337: * @param start the index of the start of the search region in this text.
1338: * @return the index of the first character that matches one of the
1339: * characters in the supplied set; or <code>-1</code> if none.
1340: */
1341: public int indexOfAny(CharSet charSet, int start) {
1342: return indexOfAny(charSet, start, length() - start);
1343: }
1344:
1345: /**
1346: * Returns the index within a region of this text of the first occurrence
1347: * of any character in the specified character set.
1348: *
1349: * @param charSet the character set.
1350: * @param start the index of the start of the search region in this text.
1351: * @param length the length of the region to search.
1352: * @return the index of the first character that matches one of the
1353: * characters in the supplied array; or <code>-1</code> if none.
1354: */
1355: public int indexOfAny(CharSet charSet, int start, int length) {
1356: final int stop = start + length;
1357: for (int i = start; i < stop; i++) {
1358: if (charSet.contains(charAt(i)))
1359: return i;
1360: }
1361: return -1;
1362: }
1363:
1364: /**
1365: * Returns the index within this text of the last occurrence
1366: * of any character in the specified character set.
1367: *
1368: * @param charSet the character set.
1369: * @return the index of the last character that matches one of the
1370: * characters in the supplied array; or <code>-1</code> if none.
1371: */
1372: public int lastIndexOfAny(CharSet charSet) {
1373: return lastIndexOfAny(charSet, 0, length());
1374: }
1375:
1376: /**
1377: * Returns the index within a region of this text of the last occurrence
1378: * of any character in the specified character set.
1379: *
1380: * @param charSet the character set.
1381: * @param start the index of the start of the search region in this text.
1382: * @return the index of the last character that matches one of the
1383: * characters in the supplied array; or <code>-1</code> if none.
1384: */
1385: public int lastIndexOfAny(CharSet charSet, int start) {
1386: return lastIndexOfAny(charSet, start, length() - start);
1387: }
1388:
1389: /**
1390: * Returns the index within a region of this text of the last occurrence
1391: * of any character in the specified character set.
1392: *
1393: * @param charSet the character set.
1394: * @param start the index of the start of the search region in this text.
1395: * @param length the length of the region to search.
1396: * @return the index of the last character that matches one of the
1397: * characters in the supplied array; or <code>-1</code> if none.
1398: */
1399: public int lastIndexOfAny(CharSet charSet, int start, int length) {
1400: for (int i = start + length; --i >= start;) {
1401: if (charSet.contains(charAt(i)))
1402: return i;
1403: }
1404: return -1;
1405: }
1406:
1407: //
1408: ////////////////////////////////////////////////////////////////////////////
1409:
1410: /**
1411: * Returns a {@link javolution.context.AllocatorContext context allocated}
1412: * primitive text instance.
1413: *
1414: * @param length the primitive length.
1415: */
1416: private static Text newPrimitive(int length) {
1417: Text text = (Text) PRIMITIVE_FACTORY.object();
1418: text._count = length;
1419: return text;
1420: }
1421:
1422: private static final ObjectFactory PRIMITIVE_FACTORY = new ObjectFactory() {
1423:
1424: public Object create() {
1425: return new Text(true);
1426: }
1427: };
1428:
1429: /**
1430: * Returns a {@link javolution.context.AllocatorContext context allocated}
1431: * composite text instance.
1432: *
1433: * @param head the composite head.
1434: * @param tail the composite tail.
1435: */
1436: private static Text newComposite(Text head, Text tail) {
1437: Text text = (Text) COMPOSITE_FACTORY.object();
1438: text._count = head._count + tail._count;
1439: text._head = head;
1440: text._tail = tail;
1441: return text;
1442: }
1443:
1444: private static final ObjectFactory COMPOSITE_FACTORY = new ObjectFactory() {
1445:
1446: public Object create() {
1447: return new Text(false);
1448: }
1449: };
1450: }
|