KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javolution > lang > TextBuilder


1 /*
2  * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
3  * Copyright (C) 2005 - Javolution (http://javolution.org/)
4  * All rights reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software is
7  * freely granted, provided that this notice is preserved.
8  */

9 package javolution.lang;
10
11 import java.io.IOException;
12
13 import j2me.io.Serializable;
14 import j2me.lang.CharSequence;
15
16 import javolution.JavolutionError;
17 import javolution.realtime.ObjectFactory;
18 import javolution.realtime.Realtime;
19 import javolution.realtime.RealtimeObject;
20
21 /**
22  * <p> This class represents an {@link Appendable} text whose capacity expands
23  * gently without incurring expensive resize/copy operations ever.</p>
24  *
25  * <p> This class is not intended for large documents manipulations which
26  * should be performed with the {@link Text} class directly
27  * (<code>O(Log(n))</code> {@link Text#insert insertion} and
28  * {@link Text#delete deletion} capabilities).</p>
29  *
30  * <p> This implementation is not synchronized.</p>
31  *
32  * @author <a HREF="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
33  * @version 3.1, March 16, 2005
34  */

35 public class TextBuilder extends RealtimeObject implements Appendable,
36         CharSequence, Reusable, Serializable {
37
38     /**
39      * Holds the factory for this text builder.
40      */

41     private static final Factory FACTORY = new Factory() {
42         public Object create() {
43             return new TextBuilder();
44         }
45
46         public void cleanup(Object obj) {
47             ((TextBuilder) obj).reset();
48         }
49     };
50
51     //
52
// Holds the character buffers. The array sizes are adjusted to ensures that
53
// no more than 4 time the required space is ever allocated.
54
//
55
// char[1<<D3][1<<D2][1<<D1][1<<D0]
56
// with charAt(i) = char[(i>>R3)&M3][(i>>R2)&M2][(i>>R1)&M1][(i>>R0)&M0]
57
//
58

59     private static final int D0 = 5;
60
61     private static final int R0 = 0;
62
63     private static final int M0 = (1 << D0) - 1;
64
65     private static final int C0 = 1 << D0; // capacity chars0
66

67     private static final int D1 = D0 + 2;
68
69     private static final int R1 = D0;
70
71     private static final int M1 = (1 << D1) - 1;
72
73     private static final int C1 = 1 << (D0 + D1); // capacity chars1
74

75     private static final int D2 = D1 + 2;
76
77     private static final int R2 = D0 + D1;
78
79     private static final int M2 = (1 << D2) - 1;
80
81     private static final int C2 = 1 << (D0 + D1 + D2); // capacity chars2
82

83     private static final int D3 = D2 + 2;
84
85     private static final int R3 = D0 + D1 + D2;
86
87     private static final int M3 = (1 << D3) - 1;
88
89     private final char[] _chars0 = new char[1 << D0]; // 5 bits (32).
90

91     private char[][] _chars1; // new char[1<<7][1<<5]; // 12 bits (4096)
92

93     private char[][][] _chars2; // new char[1<<9][1<<7][1<<5]; // 21 bits (2097152)
94

95     private char[][][][] _chars3; // new char[1<<11][1<<9][1<<7][1<<5];
96

97     private static final ObjectFactory CHARS0_FACTORY = new ObjectFactory() {
98         public Object create() {
99             return new char[1 << D0];
100         }
101     };
102
103     private static final ObjectFactory CHARS1_FACTORY = new ObjectFactory() {
104         public Object create() {
105             return new char[1 << D1][];
106         }
107     };
108
109     private static final ObjectFactory CHARS2_FACTORY = new ObjectFactory() {
110         public Object create() {
111             return new char[1 << D2][][];
112         }
113     };
114
115     private static final ObjectFactory CHARS3_FACTORY = new ObjectFactory() {
116         public Object create() {
117             return new char[1 << D3][][][];
118         }
119     };
120
121     /**
122      * Holds the current capacity.
123      */

124     private int _capacity = 1 << D0;
125
126     /**
127      * Holds the current length.
128      */

129     private int _length;
130
131     /**
132      * Creates a text builder of small initial capacity.
133      */

134     public TextBuilder() {
135     }
136
137     /**
138      * Creates a text builder holding the specified character sequence.
139      *
140      * @param csq the initial character sequence of this text builder.
141      */

142     public TextBuilder(CharSequence csq) {
143         append(csq);
144     }
145
146     /**
147      * Creates a text builder of specified initial capacity.
148      * Unless the text length exceeds the specified capacity, operations
149      * on this text builder will not allocate memory.
150      *
151      * @param capacity the initial capacity.
152      */

153     public TextBuilder(int capacity) {
154         while (capacity > _capacity) {
155             increaseCapacity();
156         }
157     }
158
159     /**
160      * Returns a text builder allocated from the "stack" when executing in
161      * a {@link javolution.realtime.PoolContext PoolContext}).
162      *
163      * @return a new, preallocated or recycled text builder instance.
164      */

165     public static TextBuilder newInstance() {
166         return (TextBuilder) FACTORY.object();
167     }
168
169     /**
170      * Returns the length (character count) of this text builder.
171      *
172      * @return the number of characters (16-bits Unicode).
173      */

174     public final int length() {
175         return _length;
176     }
177
178     /**
179      * Returns the character at the specified index.
180      *
181      * @param index the index of the character.
182      * @return the character at the specified index.
183      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
184      * (index >= this.length())</code>.
185      */

186     public final char charAt(int index) {
187         if ((index < 0) || (index >= _length))
188             throw new IndexOutOfBoundsException("index: " + index);
189         if (index < C0) {
190             return _chars0[index];
191         } else if (index < C1) {
192             return _chars1[(index >> R1)][index & M0];
193         } else if (index < C2) {
194             return _chars2[(index >> R2)][(index >> R1) & M1][index & M0];
195         } else {
196             return _chars3[(index >> R3)][(index >> R2) & M2][(index >> R1)
197                     & M1][index & M0];
198         }
199     }
200
201     /**
202      * Copies the character from this text builder into the destination
203      * character array.
204      *
205      * @param srcBegin this text start index.
206      * @param srcEnd this text end index (not included).
207      * @param dst the destination array to copy the data into.
208      * @param dstBegin the offset into the destination array.
209      * @throws IndexOutOfBoundsException if <code>(srcBegin < 0) ||
210      * (dstBegin < 0) || (srcBegin > srcEnd) || (srcEnd > this.length())
211      * || ((dstBegin + srcEnd - srcBegin) > dst.length)</code>
212      */

213     public final void getChars(int srcBegin, int srcEnd, char[] dst,
214             int dstBegin) {
215         if ((srcBegin < 0) || (dstBegin < 0) || (srcBegin > srcEnd)
216                 || (srcEnd > this.length())
217                 || ((dstBegin + srcEnd - srcBegin) > dst.length))
218             throw new IndexOutOfBoundsException();
219         for (int i = srcBegin, j = dstBegin; i < srcEnd;) {
220             dst[j++] = charAt(i++);
221         }
222     }
223
224     /**
225      * Sets the character at the specified position.
226      *
227      * @param index the index of the character to modify.
228      * @param c the new character.
229      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
230      * (index >= this.length())</code>
231      */

232     public final void setCharAt(int index, char c) {
233         if ((index < 0) || (index >= _length))
234             throw new IndexOutOfBoundsException("index: " + index);
235         if (index < C0) {
236             _chars0[index] = c;
237         } else if (index < C1) {
238             _chars1[(index >> R1)][index & M0] = c;
239         } else if (index < C2) {
240             _chars2[(index >> R2)][(index >> R1) & M1][index & M0] = c;
241         } else {
242             _chars3[(index >> R3)][(index >> R2) & M2][(index >> R1) & M1][index
243                     & M0] = c;
244         }
245     }
246
247     /**
248      * Sets the length of this character builder.
249      * If the length is greater than the current length; the
250      * null character <code>'&#92;u0000'</code> is inserted.
251      *
252      * @param newLength the new length of this builder.
253      * @throws IndexOutOfBoundsException if <code>(newLength < 0)</code>
254      */

255     public final void setLength(int newLength) {
256         if (newLength < 0)
257             throw new IndexOutOfBoundsException();
258         if (newLength <= _length) {
259             _length = newLength;
260         } else {
261             for (int i = _length; i++ < newLength;) {
262                 append('\u0000');
263             }
264         }
265     }
266
267     /**
268      * Returns an instance of {@link Text} (immutable) corresponding
269      * to the character sequence between the specified indexes.
270      *
271      * @param start the index of the first character inclusive.
272      * @param end the index of the last character exclusive.
273      * @return an immutable character sequence.
274      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
275      * (start > end) || (end > this.length())</code>
276      */

277     public final CharSequence subSequence(int start, int end) {
278         if ((start < 0) || (end < 0) || (start > end) || (end > _length))
279             throw new IndexOutOfBoundsException();
280         return Text.valueOf(this, start, end);
281     }
282
283     /**
284      * Appends the specified character.
285      *
286      * @param c the character to append.
287      * @return <code>this</code>
288      */

289     public final Appendable/*TextBuilder*/ append(char c) {
290         if (_length >= _capacity)
291             increaseCapacity();
292         final int i = _length++;
293         if (i < C0) {
294             _chars0[i] = c;
295         } else if (i < C1) {
296             _chars1[(i >> R1)][i & M0] = c;
297         } else if (i < C2) {
298             _chars2[(i >> R2)][(i >> R1) & M1][i & M0] = c;
299         } else {
300             _chars3[(i >> R3)][(i >> R2) & M2][(i >> R1) & M1][i & M0] = c;
301         }
302         return this;
303     }
304
305     /**
306      * Appends the specified character sequence. If the specified character
307      * sequence is <code>null</code> this method is equivalent to
308      * <code>append("null")</code>.
309      *
310      * @param csq the character sequence to append or <code>null</code>.
311      * @return <code>this</code>
312      */

313     public final Appendable/*TextBuilder*/ append(CharSequence csq) {
314         return (csq == null) ? append("null") : append(csq, 0, csq.length());
315     }
316
317     /**
318      * Appends a subsequence of the specified character sequence.
319      * If the specified character sequence is <code>null</code> this method
320      * is equivalent to <code>append("null")</code>.
321      *
322      * @param csq the character sequence to append or <code>null</code>.
323      * @param start the index of the first character to append.
324      * @param end the index after the last character to append.
325      * @return <code>this</code>
326      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
327      * || (start > end) || (end > csq.length())</code>
328      */

329     public final Appendable/*TextBuilder*/ append(CharSequence csq, int start, int end) {
330         if (csq == null)
331             return append("null");
332         if ((start < 0) || (end < 0) || (start > end) || (end > csq.length()))
333             throw new IndexOutOfBoundsException();
334         for (int i = start; i < end;) {
335             append(csq.charAt(i++));
336         }
337         return this;
338     }
339
340     /**
341      * Appends the textual representation of the specified object.
342      * If the specified object is <code>null</code> this method
343      * is equivalent to <code>append("null")</code>.
344      *
345      * @param obj the object to represent or <code>null</code>.
346      * @return <code>this</code>
347      */

348     public final TextBuilder append(Object obj) {
349         if (obj instanceof String) {
350             return append((String) obj);
351         } else if (obj instanceof CharSequence) {
352             return (TextBuilder) append((CharSequence) obj);
353         } else if (obj instanceof Realtime) {
354             return append(((Realtime) obj).toText());
355         } else if (obj != null) {
356             return append(obj.toString());
357         } else {
358             return append("null");
359         }
360     }
361
362     /**
363      * Appends the specified string to this text builder.
364      * If the specified string is <code>null</code> this method
365      * is equivalent to <code>append("null")</code>.
366      *
367      * @param str the string to append or <code>null</code>.
368      * @return <code>this</code>
369      */

370     public final TextBuilder append(String str) {
371         if (str == null)
372             return append("null");
373         final int length = str.length();
374         for (int i = 0; i < length;) {
375             append(str.charAt(i++));
376         }
377         return this;
378     }
379
380     /**
381      * Appends a subsequence of the specified string.
382      * If the specified character sequence is <code>null</code> this method
383      * is equivalent to <code>append("null")</code>.
384      *
385      * @param str the string to append or <code>null</code>.
386      * @param start the index of the first character to append.
387      * @param end the index after the last character to append.
388      * @return <code>this</code>
389      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
390      * || (start > end) || (end > csq.length())</code>
391      */

392     public final TextBuilder append(String str, int start, int end) {
393         if (str == null)
394             return append("null");
395         if ((start < 0) || (end < 0) || (start > end) || (end > str.length()))
396             throw new IndexOutOfBoundsException();
397         for (int i = start; i < end;) {
398             append(str.charAt(i++));
399         }
400         return this;
401     }
402
403     /**
404      * Appends the specified text to this text builder.
405      * If the specified text is <code>null</code> this method
406      * is equivalent to <code>append("null")</code>.
407      *
408      * @param text the text to append or <code>null</code>.
409      * @return <code>this</code>
410      */

411     public TextBuilder append(Text text) {
412         if (text == null)
413             return append("null");
414         final int length = text.length();
415         for (int i = 0; i < length;) {
416             append(text.charAt(i++));
417         }
418         return this;
419     }
420
421     /**
422      * Appends the characters from the char array argument.
423      *
424      * @param chars the character array source.
425      * @return <code>this</code>
426      */

427     public final TextBuilder append(char chars[]) {
428         return append(chars, 0, chars.length);
429     }
430
431     /**
432      * Appends the characters from a subarray of the char array argument.
433      *
434      * @param chars the character array source.
435      * @param offset the index of the first character to append.
436      * @param length the number of character to append.
437      * @return <code>this</code>
438      * @throws IndexOutOfBoundsException if <code>(offset < 0) ||
439      * (length < 0) || ((offset + length) > chars.length)</code>
440      */

441     public final TextBuilder append(char chars[], int offset, int length) {
442         if ((offset < 0) || (length < 0) || ((offset + length) > chars.length))
443             throw new IndexOutOfBoundsException();
444         final int end = offset + length;
445         for (int i = offset; i < end;) {
446             append(chars[i++]);
447         }
448         return this;
449     }
450
451     /**
452      * Appends the textual representation of the specified <code>boolean</code>
453      * (equivalent to <code>TypeFormat.format(b, this)</code>).
454      *
455      * @param b the <code>boolean</code> to format.
456      * @return <code>this</code>
457      * @see TypeFormat
458      */

459     public final TextBuilder append(boolean b) {
460         try {
461             TypeFormat.format(b, this);
462             return this;
463         } catch (IOException e) {
464             throw new JavolutionError(e);
465         }
466     }
467
468     /**
469      * Appends the decimal representation of the specified <code>int</code>
470      * (equivalent to <code>TypeFormat.format(i, this)</code>).
471      *
472      * @param i the <code>int</code> to format.
473      * @return <code>this</code>
474      * @see TypeFormat
475      */

476     public final TextBuilder append(int i) {
477         try {
478             TypeFormat.format(i, this);
479             return this;
480         } catch (IOException e) {
481             throw new JavolutionError(e);
482         }
483     }
484
485     /**
486      * Appends the radix representation of the specified <code>int</code>
487      * argument.
488      *
489      * @param i the <code>int</code> to format.
490      * @param radix the radix (e.g. <code>16</code> for hexadecimal).
491      * @return <code>this</code>
492      * @see TypeFormat
493      */

494     public final TextBuilder append(int i, int radix) {
495         try {
496             TypeFormat.format(i, radix, this);
497             return this;
498         } catch (IOException e) {
499             throw new JavolutionError(e);
500         }
501     }
502
503     /**
504      * Appends the decimal representation of the specified <code>long</code>
505      * (equivalent to <code>TypeFormat.format(l, this)</code>).
506      *
507      * @param l the <code>long</code> to format.
508      * @return <code>this</code>
509      * @see TypeFormat
510      */

511     public final TextBuilder append(long l) {
512         try {
513             TypeFormat.format(l, this);
514             return this;
515         } catch (IOException e) {
516             throw new JavolutionError(e);
517         }
518     }
519
520     /**
521      * Appends the radix representation of the specified <code>long</code>
522      * argument.
523      *
524      * @param l the <code>long</code> to format.
525      * @param radix the radix (e.g. <code>16</code> for hexadecimal).
526      * @return <code>this</code>
527      * @see TypeFormat
528      */

529     public final TextBuilder append(long l, int radix) {
530         try {
531             TypeFormat.format(l, radix, this);
532             return this;
533         } catch (IOException e) {
534             throw new JavolutionError(e);
535         }
536     }
537
538     /**
539      * Appends the textual representation of the specified <code>float</code>
540      * (equivalent to <code>TypeFormat.format(f, this)</code>).
541      *
542      * @param f the <code>float</code> to format.
543      * @return <code>this</code>
544      * @see TypeFormat
545      /*@FLOATING_POINT@
546      public final TextBuilder append(float f) {
547      try {
548      TypeFormat.format(f, this);
549      return this;
550      } catch (IOException e) {
551      throw new JavolutionError(e);
552      }
553      }
554      /**/

555
556     /**
557      * Appends the textual representation of the specified <code>double</code>
558      * (equivalent to <code>TypeFormat.format(d, this)</code>).
559      *
560      * @param d the <code>double</code> to format.
561      * @return <code>this</code>
562      * @see TypeFormat
563      /*@FLOATING_POINT@
564      public final TextBuilder append(double d) {
565      try {
566      TypeFormat.format(d, this);
567      return this;
568      } catch (IOException e) {
569      throw new JavolutionError(e);
570      }
571      }
572      /**/

573
574     /**
575      * Appends the specified <code>double</code> value according to the
576      * specified formatting arguments.
577      *
578      * @param value the <code>double</code> value.
579      * @param digits the number of significative digits (excludes exponent).
580      * @param scientific <code>true</code> to forces the use of the scientific
581      * notation (e.g. <code>1.23E3</code>); <code>false</code>
582      * otherwise.
583      * @param showZero <code>true</code> if trailing fractional zeros are
584      * represented; <code>false</code> otherwise.
585      * @return <code>this</code>
586      * @throws IllegalArgumentException if <code>((digits > 19) ||
587      * (digits <= 0))</code>)
588      /*@FLOATING_POINT@
589     public final TextBuilder append(double value, int digits,
590             boolean scientific, boolean showZero) {
591      try {
592      TypeFormat.format(value, digits, scientific, showZero, this);
593      return this;
594      } catch (IOException e) {
595      throw new JavolutionError(e);
596      }
597      }
598      /**/

599     
600     /**
601      * Inserts the specified character sequence at the specified location.
602      *
603      * @param index the insertion position.
604      * @param csq the character sequence being inserted.
605      * @return <code>this</code>
606      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
607      * (index > this.length())</code>
608      */

609     public final TextBuilder insert(int index, CharSequence csq) {
610         if ((index < 0) || (index > _length))
611             throw new IndexOutOfBoundsException("index: " + index);
612         final int shift = csq.length();
613         _length += shift;
614         while (_length >= _capacity) {
615             increaseCapacity();
616         }
617         for (int i = _length - shift; --i >= index;) {
618             this.setCharAt(i + shift, this.charAt(i));
619         }
620         for (int i = csq.length(); --i >= 0;) {
621             this.setCharAt(index + i, csq.charAt(i));
622         }
623         return this;
624     }
625
626     /**
627      * Removes the characters between the specified indices.
628      *
629      * @param start the beginning index, inclusive.
630      * @param end the ending index, exclusive.
631      * @return <code>this</code>
632      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
633      * || (start > end) || (end > this.length())</code>
634      */

635     public final TextBuilder delete(int start, int end) {
636         if ((start < 0) || (end < 0) || (start > end) || (end > this.length()))
637             throw new IndexOutOfBoundsException();
638         for (int i = end, j = start; i < _length;) {
639             this.setCharAt(j++, this.charAt(i++));
640         }
641         _length -= end - start;
642         return this;
643     }
644
645     /**
646      * Reverses this character sequence.
647      *
648      * @return <code>this</code>
649      */

650     public final TextBuilder reverse() {
651         final int n = _length - 1;
652         for (int j = (n - 1) >> 1; j >= 0;) {
653             char c = charAt(j);
654             setCharAt(j, charAt(n - j));
655             setCharAt(n - j--, c);
656         }
657         return this;
658     }
659
660     /**
661      * Returns the {@link Text} corresponding to this {@link TextBuilder}
662      * (allocated on the "stack" when executing in a
663      * {@link javolution.realtime.PoolContext PoolContext}).
664      *
665      * @return the corresponding {@link Text} instance.
666      */

667     public final Text toText() {
668         return Text.valueOf(this, 0, this.length());
669     }
670
671     /**
672      * Resets this text builder for reuse (sets its length to <code>0</code>).
673      */

674     public final void reset() {
675         setLength(0);
676     }
677
678     /**
679      * Returns the hash code for this text builder.
680      *
681      * @return the hash code value.
682      */

683     public final int hashCode() {
684         int h = 0;
685         for (int i = 0; i < _length;) {
686             h = 31 * h + charAt(i++);
687         }
688         return h;
689     }
690
691     /**
692      * Compares this text builder against the specified object for equality.
693      * Returns <code>true</code> if the specified object is a text builder
694      * having the same character content.
695      *
696      * @param obj the object to compare with or <code>null</code>.
697      * @return <code>true</code> if that is a text builder with the same
698      * character content as this text; <code>false</code> otherwise.
699      */

700     public final boolean equals(Object obj) {
701         if (this == obj)
702             return true;
703         if (!(obj instanceof TextBuilder))
704             return false;
705         TextBuilder that = (TextBuilder) obj;
706         if (this._length != that._length)
707             return false;
708         for (int i = 0; i < _length;) {
709             if (this.charAt(i) != that.charAt(i++))
710                 return false;
711         }
712         return true;
713     }
714
715     /**
716      * Increases this text builder capacity.
717      */

718     private void increaseCapacity() {
719         final int c = _capacity;
720         _capacity += 1 << D0;
721         if (c < C1) {
722             if (_chars1 == null) {
723                 _chars1 = (char[][]) CHARS1_FACTORY.newObject();
724             }
725             _chars1[(c >> R1)] = (char[]) CHARS0_FACTORY.newObject();
726
727         } else if (c < C2) {
728             if (_chars2 == null) {
729                 _chars2 = (char[][][]) CHARS2_FACTORY.newObject();
730             }
731             if (_chars2[(c >> R2)] == null) {
732                 _chars2[(c >> R2)] = (char[][]) CHARS1_FACTORY.newObject();
733             }
734             _chars2[(c >> R2)][(c >> R1) & M1] = (char[]) CHARS0_FACTORY
735                     .newObject();
736
737         } else {
738             if (_chars3 == null) {
739                 _chars3 = (char[][][][]) CHARS3_FACTORY.newObject();
740             }
741             if (_chars3[(c >> R3)] == null) {
742                 _chars3[(c >> R3)] = (char[][][]) CHARS2_FACTORY.newObject();
743             }
744             if (_chars3[(c >> R3)][(c >> R2) & M2] == null) {
745                 _chars3[(c >> R3)][(c >> R2) & M2] = (char[][]) CHARS1_FACTORY
746                         .newObject();
747             }
748             _chars3[(c >> R3)][(c >> R2) & M2][(c >> R1) & M1] = (char[]) CHARS0_FACTORY
749                     .newObject();
750         }
751     }
752 }
Popular Tags