0001: /*
0002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
0003: * Copyright (C) 2006 - 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.io.Serializable;
0015: import j2me.lang.CharSequence;
0016: import j2mex.realtime.MemoryArea;
0017:
0018: import javolution.JavolutionError;
0019: import javolution.context.ObjectFactory;
0020: import javolution.io.UTF8StreamWriter;
0021: import javolution.lang.MathLib;
0022: import javolution.lang.Realtime;
0023: import javolution.lang.Reusable;
0024:
0025: /**
0026: * <p> This class represents an {@link Appendable} text whose capacity expands
0027: * gently without incurring expensive resize/copy operations ever.</p>
0028: *
0029: * <p> This class is not intended for large documents manipulations which
0030: * should be performed with the {@link Text} class directly
0031: * (<code>O(Log(n))</code> {@link Text#insert insertion} and
0032: * {@link Text#delete deletion} capabilities).</p>
0033: *
0034: * <p> This implementation is not synchronized.</p>
0035: *
0036: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
0037: * @version 3.7, January 1, 2006
0038: */
0039: public class TextBuilder implements Appendable, CharSequence, Reusable,
0040: Realtime, Serializable {
0041:
0042: /**
0043: * Holds the factory for this text builder.
0044: */
0045: private static final ObjectFactory FACTORY = new ObjectFactory() {
0046: public Object create() {
0047: return new TextBuilder();
0048: }
0049: };
0050:
0051: //
0052: // Holds the character buffers. The array sizes are adjusted to ensures that
0053: // no more than 4 time the required space is ever allocated.
0054: //
0055: // char[1<<D3][1<<D2][1<<D1][1<<D0]
0056: // with charAt(i) = char[(i>>R3)&M3][(i>>R2)&M2][(i>>R1)&M1][(i>>R0)&M0]
0057: //
0058:
0059: private static final int D0 = 5;
0060:
0061: private static final int M0 = (1 << D0) - 1;
0062:
0063: private static final int C0 = 1 << D0; // capacity chars0
0064:
0065: private static final int D1 = D0 + 2;
0066:
0067: private static final int R1 = D0;
0068:
0069: private static final int M1 = (1 << D1) - 1;
0070:
0071: private static final int C1 = 1 << (D0 + D1); // capacity chars1
0072:
0073: private static final int D2 = D1 + 2;
0074:
0075: private static final int R2 = D0 + D1;
0076:
0077: private static final int M2 = (1 << D2) - 1;
0078:
0079: private static final int C2 = 1 << (D0 + D1 + D2); // capacity chars2
0080:
0081: private static final int D3 = D2 + 2;
0082:
0083: private static final int R3 = D0 + D1 + D2;
0084:
0085: private final char[] _chars0 = new char[1 << D0]; // 5 bits (32).
0086:
0087: private char[][] _chars1; // new char[1<<7][1<<5]; // 12 bits (4096)
0088:
0089: private char[][][] _chars2; // new char[1<<9][1<<7][1<<5]; // 21 bits (2097152)
0090:
0091: private char[][][][] _chars3; // new char[1<<11][1<<9][1<<7][1<<5];
0092:
0093: /**
0094: * Holds the current capacity.
0095: */
0096: private int _capacity = C0;
0097:
0098: /**
0099: * Holds the current length.
0100: */
0101: private int _length;
0102:
0103: /**
0104: * Creates a text builder of small initial capacity.
0105: */
0106: public TextBuilder() {
0107: }
0108:
0109: /**
0110: * Creates a text builder holding the specified character sequence.
0111: *
0112: * @param csq the initial character sequence of this text builder.
0113: */
0114: public TextBuilder(CharSequence csq) {
0115: append(csq);
0116: }
0117:
0118: /**
0119: * Creates a text builder holding the specified <code>String</code>.
0120: *
0121: * @param str the initial string content of this text builder.
0122: */
0123: public TextBuilder(String str) {
0124: append(str);
0125: }
0126:
0127: /**
0128: * Creates a text builder of specified initial capacity.
0129: * Unless the text length exceeds the specified capacity, operations
0130: * on this text builder will not allocate memory.
0131: *
0132: * @param capacity the initial capacity.
0133: */
0134: public TextBuilder(int capacity) {
0135: while (capacity > _capacity) {
0136: increaseCapacity();
0137: }
0138: }
0139:
0140: /**
0141: * Returns a new, preallocated or {@link #recycle recycled} text builder
0142: * (on the stack when executing in a {@link javolution.context.StackContext
0143: * StackContext}).
0144: *
0145: * @return a new, preallocated or recycled text builder instance.
0146: */
0147: public static TextBuilder newInstance() {
0148: TextBuilder textBuilder = (TextBuilder) FACTORY.object();
0149: textBuilder._length = 0;
0150: return textBuilder;
0151: }
0152:
0153: /**
0154: * Recycles a text builder {@link #newInstance() instance} immediately
0155: * (on the stack when executing in a {@link javolution.context.StackContext
0156: * StackContext}).
0157: */
0158: public static void recycle(TextBuilder instance) {
0159: FACTORY.recycle(instance);
0160: }
0161:
0162: /**
0163: * Returns the length (character count) of this text builder.
0164: *
0165: * @return the number of characters (16-bits Unicode).
0166: */
0167: public final int length() {
0168: return _length;
0169: }
0170:
0171: /**
0172: * Returns the character at the specified index.
0173: *
0174: * @param i the index of the character.
0175: * @return the character at the specified index.
0176: * @throws IndexOutOfBoundsException if <code>(i < 0) || (i >= this.length())</code>.
0177: */
0178: public final char charAt(int i) {
0179: if ((i < 0) || (i >= _length))
0180: throw new IndexOutOfBoundsException();
0181: return charsAt(i)[i & M0];
0182: }
0183:
0184: // Returns character block.
0185: final char[] charsAt(int i) {
0186: return (i < C0) ? _chars0 : (i < C1) ? _chars1[(i >> R1)]
0187: : (i < C2) ? _chars2[(i >> R2)][(i >> R1) & M1]
0188: : _chars3[(i >> R3)][(i >> R2) & M2][(i >> R1)
0189: & M1];
0190: }
0191:
0192: /**
0193: * Copies the character from this text builder into the destination
0194: * character array.
0195: *
0196: * @param srcBegin this text start index.
0197: * @param srcEnd this text end index (not included).
0198: * @param dst the destination array to copy the data into.
0199: * @param dstBegin the offset into the destination array.
0200: * @throws IndexOutOfBoundsException if <code>(srcBegin < 0) ||
0201: * (dstBegin < 0) || (srcBegin > srcEnd) || (srcEnd > this.length())
0202: * || ((dstBegin + srcEnd - srcBegin) > dst.length)</code>
0203: */
0204: public final void getChars(int srcBegin, int srcEnd, char[] dst,
0205: int dstBegin) {
0206: if ((srcBegin < 0) || (srcBegin > srcEnd)
0207: || (srcEnd > this ._length))
0208: throw new IndexOutOfBoundsException();
0209: for (int i = srcBegin, j = dstBegin; i < srcEnd;) {
0210: char[] chars0 = charsAt(i); // Gets character block.
0211: int i0 = i & M0;
0212: int length = MathLib.min(C0 - i0, srcEnd - i);
0213: System.arraycopy(chars0, i0, dst, j, length);
0214: i += length;
0215: j += length;
0216: }
0217: }
0218:
0219: /**
0220: * Sets the character at the specified position.
0221: *
0222: * @param index the index of the character to modify.
0223: * @param c the new character.
0224: * @throws IndexOutOfBoundsException if <code>(index < 0) ||
0225: * (index >= this.length())</code>
0226: */
0227: public final void setCharAt(int index, char c) {
0228: if ((index < 0) || (index >= _length))
0229: throw new IndexOutOfBoundsException("index: " + index);
0230: charsAt(index)[index & M0] = c;
0231: }
0232:
0233: /**
0234: * Sets the length of this character builder.
0235: * If the length is greater than the current length; the
0236: * null character <code>'\u0000'</code> is inserted.
0237: *
0238: * @param newLength the new length of this builder.
0239: * @throws IndexOutOfBoundsException if <code>(newLength < 0)</code>
0240: */
0241: public final void setLength(int newLength) {
0242: if (newLength < 0)
0243: throw new IndexOutOfBoundsException();
0244: if (newLength <= _length) {
0245: _length = newLength;
0246: } else {
0247: for (int i = _length; i++ < newLength;) {
0248: append('\u0000');
0249: }
0250: }
0251: }
0252:
0253: /**
0254: * Returns an instance of {@link Text} (immutable) corresponding
0255: * to the character sequence between the specified indexes.
0256: *
0257: * @param start the index of the first character inclusive.
0258: * @param end the index of the last character exclusive.
0259: * @return an immutable character sequence.
0260: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
0261: * (start > end) || (end > this.length())</code>
0262: */
0263: public final CharSequence subSequence(int start, int end) {
0264: if ((start < 0) || (end < 0) || (start > end)
0265: || (end > _length))
0266: throw new IndexOutOfBoundsException();
0267: return Text.valueOf(this , start, end);
0268: }
0269:
0270: /**
0271: * Appends the specified character.
0272: *
0273: * @param c the character to append.
0274: * @return <code>this</code>
0275: */
0276: public final Appendable/*TextBuilder*/append(char c) { // Short to be inlined.
0277: if (_length >= C0)
0278: return append2(c);
0279: _chars0[_length++] = c;
0280: return this ;
0281: }
0282:
0283: private TextBuilder append2(char c) {
0284: if (_length >= _capacity)
0285: increaseCapacity();
0286: final int i = _length++;
0287: if (i < C1) {
0288: _chars1[(i >> R1)][i & M0] = c;
0289: } else if (i < C2) {
0290: _chars2[(i >> R2)][(i >> R1) & M1][i & M0] = c;
0291: } else {
0292: _chars3[(i >> R3)][(i >> R2) & M2][(i >> R1) & M1][i & M0] = c;
0293: }
0294: return this ;
0295: }
0296:
0297: /**
0298: * Appends the textual representation of the specified object.
0299: * If the specified object is <code>null</code> this method
0300: * is equivalent to <code>append("null")</code>.
0301: *
0302: * @param obj the object to represent or <code>null</code>.
0303: * @return <code>this</code>
0304: */
0305: public final TextBuilder append(Object obj) {
0306: if (obj instanceof String)
0307: return append((String) obj);
0308: return append(Text.valueOf(obj));
0309: }
0310:
0311: /**
0312: * Appends the specified character sequence. If the specified character
0313: * sequence is <code>null</code> this method is equivalent to
0314: * <code>append("null")</code>.
0315: *
0316: * @param csq the character sequence to append or <code>null</code>.
0317: * @return <code>this</code>
0318: */
0319: public final Appendable/*TextBuilder*/append(CharSequence csq) {
0320: return (csq == null) ? append("null") : append(csq, 0, csq
0321: .length());
0322: }
0323:
0324: /**
0325: * Appends a subsequence of the specified character sequence.
0326: * If the specified character sequence is <code>null</code> this method
0327: * is equivalent to <code>append("null")</code>.
0328: *
0329: * @param csq the character sequence to append or <code>null</code>.
0330: * @param start the index of the first character to append.
0331: * @param end the index after the last character to append.
0332: * @return <code>this</code>
0333: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
0334: * || (start > end) || (end > csq.length())</code>
0335: */
0336: public final Appendable/*TextBuilder*/append(CharSequence csq,
0337: int start, int end) {
0338: if (csq == null)
0339: return append("null");
0340: if ((start < 0) || (end < 0) || (start > end)
0341: || (end > csq.length()))
0342: throw new IndexOutOfBoundsException();
0343: for (int i = start; i < end;) {
0344: append(csq.charAt(i++));
0345: }
0346: return this ;
0347: }
0348:
0349: /**
0350: * Appends the specified string to this text builder.
0351: * If the specified string is <code>null</code> this method
0352: * is equivalent to <code>append("null")</code>.
0353: *
0354: * @param str the string to append or <code>null</code>.
0355: * @return <code>this</code>
0356: */
0357: public final TextBuilder append(String str) {
0358: return (str == null) ? append("null") : appendNoCheck(str, 0,
0359: str.length());
0360: }
0361:
0362: /**
0363: * Appends a subsequence of the specified string.
0364: * If the specified character sequence is <code>null</code> this method
0365: * is equivalent to <code>append("null")</code>.
0366: *
0367: * @param str the string to append or <code>null</code>.
0368: * @param start the index of the first character to append.
0369: * @param end the index after the last character to append.
0370: * @return <code>this</code>
0371: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
0372: * || (start > end) || (end > str.length())</code>
0373: */
0374: public final TextBuilder append(String str, int start, int end) {
0375: if (str == null)
0376: return append("null");
0377: if ((start < 0) || (end < 0) || (start > end)
0378: || (end > str.length()))
0379: throw new IndexOutOfBoundsException();
0380: return appendNoCheck(str, start, end);
0381: }
0382:
0383: private final TextBuilder appendNoCheck(String str, int start,
0384: int end) {
0385: int newLength = _length + end - start;
0386: while (_capacity < newLength) {
0387: increaseCapacity();
0388: }
0389: for (int i = start, j = _length; i < end;) {
0390: char[] chars = charsAt(j);
0391: int inc = MathLib.min(C0 - (j & M0), end - i);
0392: str.getChars(i, (i += inc), chars, j & M0);
0393: j += inc;
0394: }
0395: _length = newLength;
0396: return this ;
0397: }
0398:
0399: /**
0400: * Appends the specified text to this text builder.
0401: * If the specified text is <code>null</code> this method
0402: * is equivalent to <code>append("null")</code>.
0403: *
0404: * @param txt the text to append or <code>null</code>.
0405: * @return <code>this</code>
0406: */
0407: public final TextBuilder append(Text txt) {
0408: return (txt == null) ? append("null") : appendNoCheck(txt, 0,
0409: txt.length());
0410: }
0411:
0412: /**
0413: * Appends a subsequence of the specified text.
0414: * If the specified character sequence is <code>null</code> this method
0415: * is equivalent to <code>append("null")</code>.
0416: *
0417: * @param txt the text to append or <code>null</code>.
0418: * @param start the index of the first character to append.
0419: * @param end the index after the last character to append.
0420: * @return <code>this</code>
0421: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
0422: * || (start > end) || (end > txt.length())</code>
0423: */
0424: public final TextBuilder append(Text txt, int start, int end) {
0425: if (txt == null)
0426: return append("null");
0427: if ((start < 0) || (end < 0) || (start > end)
0428: || (end > txt.length()))
0429: throw new IndexOutOfBoundsException();
0430: return appendNoCheck(txt, start, end);
0431: }
0432:
0433: private final TextBuilder appendNoCheck(Text txt, int start, int end) {
0434: int newLength = _length + end - start;
0435: while (_capacity < newLength) {
0436: increaseCapacity();
0437: }
0438: for (int i = start, j = _length; i < end;) {
0439: char[] chars = charsAt(j);
0440: int inc = MathLib.min(C0 - (j & M0), end - i);
0441: txt.getChars(i, (i += inc), chars, j & M0);
0442: j += inc;
0443: }
0444: _length = newLength;
0445: return this ;
0446: }
0447:
0448: /**
0449: * Appends the characters from the char array argument.
0450: *
0451: * @param chars the character array source.
0452: * @return <code>this</code>
0453: */
0454: public final TextBuilder append(char chars[]) {
0455: return append(chars, 0, chars.length);
0456: }
0457:
0458: /**
0459: * Appends the characters from a subarray of the char array argument.
0460: *
0461: * @param chars the character array source.
0462: * @param offset the index of the first character to append.
0463: * @param length the number of character to append.
0464: * @return <code>this</code>
0465: * @throws IndexOutOfBoundsException if <code>(offset < 0) ||
0466: * (length < 0) || ((offset + length) > chars.length)</code>
0467: */
0468: public final TextBuilder append(char chars[], int offset, int length) {
0469: if ((offset < 0) || (length < 0)
0470: || ((offset + length) > chars.length))
0471: throw new IndexOutOfBoundsException();
0472: final int end = offset + length;
0473: for (int i = offset; i < end;) {
0474: append(chars[i++]);
0475: }
0476: return this ;
0477: }
0478:
0479: /**
0480: * Appends the textual representation of the specified <code>boolean</code>
0481: * argument.
0482: *
0483: * @param b the <code>boolean</code> to format.
0484: * @return <code>this</code>
0485: * @see TypeFormat
0486: */
0487: public final TextBuilder append(boolean b) {
0488: return b ? append("true") : append("false");
0489: }
0490:
0491: /**
0492: * Appends the decimal representation of the specified <code>int</code>
0493: * argument.
0494: *
0495: * @param i the <code>int</code> to format.
0496: * @return <code>this</code>
0497: */
0498: public final TextBuilder append(int i) {
0499: if (i < 0) {
0500: if (i == Integer.MIN_VALUE) // Negation would overflow.
0501: return append("-2147483648"); // 11 char max.
0502: i = -i;
0503: append('-');
0504: }
0505: if (i >= 100000) {
0506: int high = i / 100000;
0507: writeDigits(high, false);
0508: i -= high * 100000;
0509: writeDigits(i, true);
0510: } else {
0511: writeDigits(i, false);
0512: }
0513: return this ;
0514: }
0515:
0516: /**
0517: * Appends the radix representation of the specified <code>int</code>
0518: * argument.
0519: *
0520: * @param i the <code>int</code> to format.
0521: * @param radix the radix (e.g. <code>16</code> for hexadecimal).
0522: * @return <code>this</code>
0523: */
0524: public final TextBuilder append(int i, int radix) {
0525: if (radix == 10)
0526: return append(i); // Faster version.
0527: if (radix < 2 || radix > 36)
0528: throw new IllegalArgumentException("radix: " + radix);
0529: if (i < 0) {
0530: append('-');
0531: } else {
0532: i = -i;
0533: }
0534: append2(i, radix);
0535: return this ;
0536: }
0537:
0538: private void append2(int i, int radix) {
0539: if (i <= -radix) {
0540: append2(i / radix, radix);
0541: append(DIGIT_TO_CHAR[-(i % radix)]);
0542: } else {
0543: append(DIGIT_TO_CHAR[-i]);
0544: }
0545: }
0546:
0547: /**
0548: * Appends the decimal representation of the specified <code>long</code>
0549: * argument.
0550: *
0551: * @param l the <code>long</code> to format.
0552: * @return <code>this</code>
0553: */
0554: public final TextBuilder append(long l) {
0555: if (l < 0) {
0556: if (l == Long.MIN_VALUE) // Negation would overflow.
0557: return append("-9223372036854775808"); // 20 characters max.
0558: l = -l;
0559: append('-');
0560: }
0561: boolean writeZero = false;
0562: if (l >= 1000000000000000L) {
0563: int high = (int) (l / 1000000000000000L);
0564: writeDigits(high, writeZero);
0565: l -= high * 1000000000000000L;
0566: writeZero = true;
0567: }
0568: if (writeZero || (l >= 10000000000L)) {
0569: int high = (int) (l / 10000000000L);
0570: writeDigits(high, writeZero);
0571: l -= high * 10000000000L;
0572: writeZero = true;
0573: }
0574: if (writeZero || (l >= 100000)) {
0575: int high = (int) (l / 100000);
0576: writeDigits(high, writeZero);
0577: l -= high * 100000L;
0578: writeZero = true;
0579: }
0580: writeDigits((int) l, writeZero);
0581: return this ;
0582: }
0583:
0584: /**
0585: * Appends the radix representation of the specified <code>long</code>
0586: * argument.
0587: *
0588: * @param l the <code>long</code> to format.
0589: * @param radix the radix (e.g. <code>16</code> for hexadecimal).
0590: * @return <code>this</code>
0591: */
0592: public final TextBuilder append(long l, int radix) {
0593: if (radix == 10)
0594: return append(l); // Faster version.
0595: if (radix < 2 || radix > 36)
0596: throw new IllegalArgumentException("radix: " + radix);
0597: if (l < 0) {
0598: append('-');
0599: } else {
0600: l = -l;
0601: }
0602: append2(l, radix);
0603: return this ;
0604: }
0605:
0606: private void append2(long l, int radix) {
0607: if (l <= -radix) {
0608: append2(l / radix, radix);
0609: append(DIGIT_TO_CHAR[(int) -(l % radix)]);
0610: } else {
0611: append(DIGIT_TO_CHAR[(int) -l]);
0612: }
0613: }
0614:
0615: /**
0616: * Appends the textual representation of the specified <code>float</code>
0617: * (equivalent to
0618: * <code>append(f, 10, (abs(f) >= 1E7) || (abs(f) < 0.001), false)</code>).
0619: *
0620: * @param f the <code>float</code> to format.
0621: * @return <code>this</code>
0622: * @JVM-1.1+@
0623: public final TextBuilder append(float f) {
0624: return append(f, 10, (MathLib.abs(f) >= 1E7) || (MathLib.abs(f) < 0.001), false);
0625: }
0626: /**/
0627:
0628: /**
0629: * Appends the textual representation of the specified <code>double</code>;
0630: * the number of digits is 17 or 16 when the 16 digits representation
0631: * can be parsed back to the same <code>double</code> (mimic the standard
0632: * library formatting).
0633: *
0634: * @param d the <code>double</code> to format.
0635: * @return <code>append(d, -1, (MathLib.abs(d) >= 1E7) ||
0636: * (MathLib.abs(d) < 0.001), false)</code>
0637: * @JVM-1.1+@
0638: public final TextBuilder append(double d) {
0639: return append(d, -1, (MathLib.abs(d) >= 1E7) || (MathLib.abs(d) < 0.001), false);
0640: }
0641: /**/
0642:
0643: /**
0644: * Appends the textual representation of the specified <code>double</code>
0645: * according to the specified formatting arguments.
0646: *
0647: * @param d the <code>double</code> value.
0648: * @param digits the number of significative digits (excludes exponent) or
0649: * <code>-1</code> to mimic the standard library (16 or 17 digits).
0650: * @param scientific <code>true</code> to forces the use of the scientific
0651: * notation (e.g. <code>1.23E3</code>); <code>false</code>
0652: * otherwise.
0653: * @param showZero <code>true</code> if trailing fractional zeros are
0654: * represented; <code>false</code> otherwise.
0655: * @return <code>this</code>
0656: * @throws IllegalArgumentException if <code>(digits > 19)</code>)
0657: * @JVM-1.1+@
0658: public final TextBuilder append(double d, int digits,
0659: boolean scientific, boolean showZero) {
0660: if (digits > 19)
0661: throw new IllegalArgumentException("digits: " + digits);
0662: if (d != d) // NaN
0663: return append("NaN");
0664: if (d < 0) { // Work with positive number.
0665: d = -d;
0666: append('-');
0667: }
0668: if (d == Double.POSITIVE_INFINITY) // Infinity.
0669: return append("Infinity");
0670: if (d == 0.0) { // Zero.
0671: if (digits == 1)
0672: return append("0.");
0673: if (!showZero)
0674: return append("0.0");
0675: append("0.0");
0676: for (int i = 2; i < digits; i++) {
0677: append('0');
0678: }
0679: return this;
0680: }
0681:
0682: // Find the exponent e such as: value == x.xxx * 10^e
0683: int e = MathLib.floorLog10(d);
0684:
0685: long m;
0686: if (digits < 0) { // Use 16 or 17 digits.
0687: // Try 17 digits.
0688: long m17 = MathLib.toLongPow10(d, (17 - 1) - e);
0689: // Check if we can use 16 digits.
0690: long m16 = m17 / 10;
0691: double dd = MathLib.toDoublePow10(m16, e - 16 + 1);
0692: if (dd == d) { // 16 digits is enough.
0693: digits = 16;
0694: m = m16;
0695: } else { // We cannot remove the last digit.
0696: digits = 17;
0697: m = m17;
0698: }
0699: } else { // Use the specified number of digits.
0700: m = MathLib.toLongPow10(d, (digits - 1) - e);
0701: }
0702:
0703: // Formats.
0704: if (scientific || (e >= digits)) {
0705: // Scientific notation has to be used ("x.xxxEyy").
0706: long pow10 = POW10_LONG[digits - 1];
0707: int i = (int) (m / pow10); // Single digit.
0708: append(DIGIT_TO_CHAR[i]);
0709: m = m - pow10 * i;
0710: appendFraction(m, digits - 1, showZero);
0711: append('E');
0712: append(e);
0713: } else { // Dot within the string ("xxxx.xxxxx").
0714: if (e < 0) {
0715: append('0');
0716: } else {
0717: long pow10 = POW10_LONG[digits - e - 1];
0718: long l = m / pow10;
0719: append(l);
0720: m = m - pow10 * l;
0721: }
0722: appendFraction(m, digits - e - 1, showZero);
0723: }
0724: return this;
0725: }
0726: private static final long[] POW10_LONG = new long[] { 1L, 10L, 100L, 1000L,
0727: 10000L, 100000L, 1000000L, 10000000L, 100000000L, 1000000000L,
0728: 10000000000L, 100000000000L, 1000000000000L, 10000000000000L,
0729: 100000000000000L, 1000000000000000L, 10000000000000000L,
0730: 100000000000000000L, 1000000000000000000L };
0731: /**/
0732:
0733: final void appendFraction(long l, int digits, boolean showZero) {
0734: append('.');
0735: int length = MathLib.digitLength(l);
0736: if ((length == 0) && !showZero) { // Only one zero shown xxx.0
0737: append('0');
0738: return;
0739: }
0740: for (int i = length; i < digits; i++) {
0741: append('0');
0742: }
0743: if (l != 0) {
0744: append(l);
0745: }
0746: if (!showZero) { // Remove trailing zeros.
0747: int trailingZeros = 0;
0748: while (true) {
0749: char c = charAt(_length - trailingZeros - 1);
0750: if (c != '0')
0751: break;
0752: trailingZeros++;
0753: }
0754: this .setLength(_length - trailingZeros);
0755: }
0756: }
0757:
0758: /**
0759: * Inserts the specified character sequence at the specified location.
0760: *
0761: * @param index the insertion position.
0762: * @param csq the character sequence being inserted.
0763: * @return <code>this</code>
0764: * @throws IndexOutOfBoundsException if <code>(index < 0) ||
0765: * (index > this.length())</code>
0766: */
0767: public final TextBuilder insert(int index, CharSequence csq) {
0768: if ((index < 0) || (index > _length))
0769: throw new IndexOutOfBoundsException("index: " + index);
0770: final int shift = csq.length();
0771: _length += shift;
0772: while (_length >= _capacity) {
0773: increaseCapacity();
0774: }
0775: for (int i = _length - shift; --i >= index;) {
0776: this .setCharAt(i + shift, this .charAt(i));
0777: }
0778: for (int i = csq.length(); --i >= 0;) {
0779: this .setCharAt(index + i, csq.charAt(i));
0780: }
0781: return this ;
0782: }
0783:
0784: /**
0785: * Removes all the characters of this text builder
0786: * (equivalent to <code>this.delete(start, this.length())</code>).
0787: *
0788: * @return <code>this.delete(0, this.length())</code>
0789: */
0790: public final TextBuilder clear() {
0791: _length = 0;
0792: return this ;
0793: }
0794:
0795: /**
0796: * Removes the characters between the specified indices.
0797: *
0798: * @param start the beginning index, inclusive.
0799: * @param end the ending index, exclusive.
0800: * @return <code>this</code>
0801: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
0802: * || (start > end) || (end > this.length())</code>
0803: */
0804: public final TextBuilder delete(int start, int end) {
0805: if ((start < 0) || (end < 0) || (start > end)
0806: || (end > this .length()))
0807: throw new IndexOutOfBoundsException();
0808: for (int i = end, j = start; i < _length;) {
0809: this .setCharAt(j++, this .charAt(i++));
0810: }
0811: _length -= end - start;
0812: return this ;
0813: }
0814:
0815: /**
0816: * Reverses this character sequence.
0817: *
0818: * @return <code>this</code>
0819: */
0820: public final TextBuilder reverse() {
0821: final int n = _length - 1;
0822: for (int j = (n - 1) >> 1; j >= 0;) {
0823: char c = charAt(j);
0824: setCharAt(j, charAt(n - j));
0825: setCharAt(n - j--, c);
0826: }
0827: return this ;
0828: }
0829:
0830: /**
0831: * Returns the {@link Text} corresponding to this {@link TextBuilder}
0832: * (allocated on the "stack" when executing in a
0833: * {@link javolution.context.StackContext StackContext}).
0834: *
0835: * @return the corresponding {@link Text} instance.
0836: */
0837: public final Text toText() {
0838: // TODO Avoids copying the arrays and makes it immutable ??
0839: return Text.valueOf(this , 0, _length);
0840: }
0841:
0842: /**
0843: * Returns the <code>String</code> representation of this
0844: * {@link TextBuilder}.
0845: *
0846: * @return the <code>java.lang.String</code> for this text builder.
0847: */
0848: public final String toString() {
0849: char[] data = new char[_length];
0850: this .getChars(0, _length, data, 0);
0851: return new String(data, 0, _length);
0852: }
0853:
0854: /**
0855: * Resets this text builder for reuse (equivalent to {@link #clear}).
0856: */
0857: public final void reset() {
0858: _length = 0;
0859: }
0860:
0861: /**
0862: * Returns the hash code for this text builder.
0863: *
0864: * @return the hash code value.
0865: */
0866: public final int hashCode() {
0867: int h = 0;
0868: for (int i = 0; i < _length;) {
0869: h = 31 * h + charAt(i++);
0870: }
0871: return h;
0872: }
0873:
0874: /**
0875: * Compares this text builder against the specified object for equality.
0876: * Returns <code>true</code> if the specified object is a text builder
0877: * having the same character content.
0878: *
0879: * @param obj the object to compare with or <code>null</code>.
0880: * @return <code>true</code> if that is a text builder with the same
0881: * character content as this text; <code>false</code> otherwise.
0882: */
0883: public final boolean equals(Object obj) {
0884: if (this == obj)
0885: return true;
0886: if (!(obj instanceof TextBuilder))
0887: return false;
0888: TextBuilder that = (TextBuilder) obj;
0889: if (this ._length != that._length)
0890: return false;
0891: for (int i = 0; i < _length;) {
0892: if (this .charAt(i) != that.charAt(i++))
0893: return false;
0894: }
0895: return true;
0896: }
0897:
0898: /**
0899: * Prints out this text builder to <code>System.out</code> (UTF-8 encoding).
0900: * This method is equivalent to:[code]
0901: * synchronized(OUT) {
0902: * print(OUT);
0903: * OUT.flush();
0904: * }
0905: * ...
0906: * static final OUT = new UTF8StreamWriter().setOutput(System.out);
0907: * [/code]
0908: */
0909: public void print() {
0910: try {
0911: synchronized (SYSTEM_OUT_WRITER) {
0912: print(SYSTEM_OUT_WRITER);
0913: SYSTEM_OUT_WRITER.flush();
0914: }
0915: } catch (IOException e) { // Should never happen.
0916: throw new JavolutionError(e);
0917: }
0918: }
0919:
0920: private static final UTF8StreamWriter SYSTEM_OUT_WRITER = new UTF8StreamWriter()
0921: .setOutput(System.out);
0922:
0923: /**
0924: * Prints out this text builder to <code>System.out</code> and then
0925: * terminates the line. This method is equivalent to:[code]
0926: * synchronized(OUT) {
0927: * println(OUT);
0928: * OUT.flush();
0929: * }
0930: * ...
0931: * static final OUT = new UTF8StreamWriter().setOutput(System.out);
0932: * [/code]
0933: */
0934: public void println() {
0935: try {
0936: synchronized (SYSTEM_OUT_WRITER) {
0937: println(SYSTEM_OUT_WRITER);
0938: SYSTEM_OUT_WRITER.flush();
0939: }
0940: } catch (IOException e) { // Should never happen.
0941: throw new JavolutionError(e);
0942: }
0943: }
0944:
0945: /**
0946: * Prints out this text builder to the specified writer.
0947: *
0948: * @param writer the destination writer.
0949: */
0950: public void print(Writer writer) throws IOException {
0951: for (int i = 0; i < _length; i += C0) {
0952: char[] chars = charsAt(i);
0953: writer.write(chars, 0, MathLib.min(C0, _length - i));
0954: }
0955: }
0956:
0957: /**
0958: * Prints out this text builder to the specified writer and then terminates
0959: * the line.
0960: *
0961: * @param writer the destination writer.
0962: */
0963: public void println(Writer writer) throws IOException {
0964: print(writer);
0965: writer.write('\n');
0966: }
0967:
0968: /**
0969: * Appends the content of this text builder to the specified
0970: * string buffer (only for text builder with length less than 32).
0971: *
0972: * @param sb the string buffer.
0973: */
0974: final void appendTo(StringBuffer sb) {
0975: sb.append(_chars0, 0, _length);
0976: }
0977:
0978: /**
0979: * Appends the content of this text builder to the specified
0980: * string buffer (only for text builder with length less than 32).
0981: *
0982: * @param sb the string builder.
0983: * @JVM-1.5+@
0984: final void appendTo(StringBuilder sb) {
0985: sb.append(_chars0, 0, _length);
0986: }
0987: /**/
0988:
0989: /**
0990: * Indicates if this text builder has the same character content as the
0991: * specified character sequence.
0992: *
0993: * @param csq the character sequence to compare with.
0994: * @return <code>true</code> if the specified character sequence has the
0995: * same character content as this text; <code>false</code> otherwise.
0996: */
0997: public final boolean contentEquals(CharSequence csq) {
0998: if (csq.length() != _length)
0999: return false;
1000: char[] chars = charsAt(0); // Gets character block.
1001: for (int i = 0; i < _length;) {
1002: if (chars[i & M0] != csq.charAt(i++))
1003: return false;
1004: if ((i & M0) == 0) { // Changes character block.
1005: chars = charsAt(i);
1006: }
1007: }
1008: return true;
1009: }
1010:
1011: /**
1012: * Equivalent to {@link #contentEquals(CharSequence)}
1013: * (for J2ME compability).
1014: *
1015: * @param csq the string character sequence to compare with.
1016: * @return <code>true</code> if the specified string has the
1017: * same character content as this text; <code>false</code> otherwise.
1018: */
1019: public final boolean contentEquals(String csq) {
1020: if (csq.length() != _length)
1021: return false;
1022: char[] chars = charsAt(0); // Gets character block.
1023: for (int i = 0; i < _length;) {
1024: if (chars[i & M0] != csq.charAt(i++))
1025: return false;
1026: if ((i & M0) == 0) { // Changes character block.
1027: chars = charsAt(i);
1028: }
1029: }
1030: return true;
1031: }
1032:
1033: /**
1034: * Increases this text builder capacity.
1035: */
1036: private void increaseCapacity() {
1037: MemoryArea.getMemoryArea(this ).executeInArea(new Runnable() {
1038: public void run() {
1039: final int c = _capacity;
1040: _capacity += 1 << D0;
1041: if (c < C1) {
1042: if (_chars1 == null) {
1043: _chars1 = new char[1 << D1][];
1044: }
1045: _chars1[(c >> R1)] = new char[1 << D0];
1046:
1047: } else if (c < C2) {
1048: if (_chars2 == null) {
1049: _chars2 = new char[1 << D2][][];
1050: }
1051: if (_chars2[(c >> R2)] == null) {
1052: _chars2[(c >> R2)] = new char[1 << D1][];
1053: }
1054: _chars2[(c >> R2)][(c >> R1) & M1] = new char[1 << D0];
1055:
1056: } else {
1057: if (_chars3 == null) {
1058: _chars3 = new char[1 << D3][][][];
1059: }
1060: if (_chars3[(c >> R3)] == null) {
1061: _chars3[(c >> R3)] = new char[1 << D2][][];
1062: }
1063: if (_chars3[(c >> R3)][(c >> R2) & M2] == null) {
1064: _chars3[(c >> R3)][(c >> R2) & M2] = new char[1 << D1][];
1065: }
1066: _chars3[(c >> R3)][(c >> R2) & M2][(c >> R1) & M1] = new char[1 << D0];
1067: }
1068: }
1069: });
1070: }
1071:
1072: /**
1073: * Appends the specified positive integer [0 .. 99999] in decimal
1074: * representation to this text builder.
1075: *
1076: * @param i the integer to write.
1077: * @param writeZero <code>true</code> if leading zero are included;
1078: * <code>false</code> otherwise.
1079: */
1080: private void writeDigits(int i, boolean writeZero) {
1081: {
1082: final int e = 10000;
1083: if (i >= e) {
1084: writeZero = true;
1085: if (i >= e * 5) {
1086: if (i >= e * 8) {
1087: if (i >= e * 9) {
1088: append('9');
1089: i -= e * 9;
1090: } else {
1091: append('8');
1092: i -= e * 8;
1093: }
1094: } else { // [5 .. 8[
1095: if (i >= e * 7) {
1096: append('7');
1097: i -= e * 7;
1098: } else if (i >= e * 6) {
1099: append('6');
1100: i -= e * 6;
1101: } else {
1102: append('5');
1103: i -= e * 5;
1104: }
1105: }
1106: } else { // [1 ..5[
1107: if (i >= e * 3) {
1108: if (i >= e * 4) {
1109: append('4');
1110: i -= e * 4;
1111: } else {
1112: append('3');
1113: i -= e * 3;
1114: }
1115: } else { // [1 .. 2[
1116: if (i >= e * 2) {
1117: append('2');
1118: i -= e * 2;
1119: } else {
1120: append('1');
1121: i -= e;
1122: }
1123: }
1124: }
1125: } else if (writeZero) {
1126: append('0');
1127: }
1128: }
1129: {
1130: final int e = 1000;
1131: if (i >= e) {
1132: writeZero = true;
1133: if (i >= e * 5) {
1134: if (i >= e * 8) {
1135: if (i >= e * 9) {
1136: append('9');
1137: i -= e * 9;
1138: } else {
1139: append('8');
1140: i -= e * 8;
1141: }
1142: } else { // [5 .. 8[
1143: if (i >= e * 7) {
1144: append('7');
1145: i -= e * 7;
1146: } else if (i >= e * 6) {
1147: append('6');
1148: i -= e * 6;
1149: } else {
1150: append('5');
1151: i -= e * 5;
1152: }
1153: }
1154: } else { // [1 ..5[
1155: if (i >= e * 3) {
1156: if (i >= e * 4) {
1157: append('4');
1158: i -= e * 4;
1159: } else {
1160: append('3');
1161: i -= e * 3;
1162: }
1163: } else { // [1 .. 2[
1164: if (i >= e * 2) {
1165: append('2');
1166: i -= e * 2;
1167: } else {
1168: append('1');
1169: i -= e;
1170: }
1171: }
1172: }
1173: } else if (writeZero) {
1174: append('0');
1175: }
1176: }
1177: {
1178: final int e = 100;
1179: if (i >= e) {
1180: writeZero = true;
1181: if (i >= e * 5) {
1182: if (i >= e * 8) {
1183: if (i >= e * 9) {
1184: append('9');
1185: i -= e * 9;
1186: } else {
1187: append('8');
1188: i -= e * 8;
1189: }
1190: } else { // [5 .. 8[
1191: if (i >= e * 7) {
1192: append('7');
1193: i -= e * 7;
1194: } else if (i >= e * 6) {
1195: append('6');
1196: i -= e * 6;
1197: } else {
1198: append('5');
1199: i -= e * 5;
1200: }
1201: }
1202: } else { // [1 ..5[
1203: if (i >= e * 3) {
1204: if (i >= e * 4) {
1205: append('4');
1206: i -= e * 4;
1207: } else {
1208: append('3');
1209: i -= e * 3;
1210: }
1211: } else { // [1 .. 2[
1212: if (i >= e * 2) {
1213: append('2');
1214: i -= e * 2;
1215: } else {
1216: append('1');
1217: i -= e;
1218: }
1219: }
1220: }
1221: } else if (writeZero) {
1222: append('0');
1223: }
1224: }
1225: {
1226: final int e = 10;
1227: if (i >= e) {
1228: writeZero = true;
1229: if (i >= e * 5) {
1230: if (i >= e * 8) {
1231: if (i >= e * 9) {
1232: append('9');
1233: i -= e * 9;
1234: } else {
1235: append('8');
1236: i -= e * 8;
1237: }
1238: } else { // [5 .. 8[
1239: if (i >= e * 7) {
1240: append('7');
1241: i -= e * 7;
1242: } else if (i >= e * 6) {
1243: append('6');
1244: i -= e * 6;
1245: } else {
1246: append('5');
1247: i -= e * 5;
1248: }
1249: }
1250: } else { // [1 ..5[
1251: if (i >= e * 3) {
1252: if (i >= e * 4) {
1253: append('4');
1254: i -= e * 4;
1255: } else {
1256: append('3');
1257: i -= e * 3;
1258: }
1259: } else { // [1 .. 2[
1260: if (i >= e * 2) {
1261: append('2');
1262: i -= e * 2;
1263: } else {
1264: append('1');
1265: i -= e;
1266: }
1267: }
1268: }
1269: } else if (writeZero) {
1270: append('0');
1271: }
1272: }
1273: append(DIGIT_TO_CHAR[i]);
1274: }
1275:
1276: /**
1277: * Holds the digits to character mapping.
1278: */
1279: private final static char[] DIGIT_TO_CHAR = { '0', '1', '2', '3',
1280: '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
1281: 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
1282: 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
1283:
1284: }
|