KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javolution > text > Text


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.text;
10
11 import j2me.io.Serializable;
12 import j2me.lang.CharSequence;
13 import j2me.lang.Comparable;
14 import j2mex.realtime.MemoryArea;
15
16 import javolution.Javolution;
17 import javolution.context.Realtime;
18 import javolution.context.RealtimeObject;
19 import javolution.lang.Immutable;
20 import javolution.util.FastComparator;
21 import javolution.util.FastMap;
22 import javolution.xml.XMLFormat;
23 import javolution.xml.stream.XMLStreamException;
24
25 /**
26  * <p> This class represents an immutable character sequence with extremely
27  * fast {@link #concat concatenation}, {@link #insert insertion} and
28  * {@link #delete deletion} capabilities (O[Log(n)]) instead of
29  * O[n] for StringBuffer/StringBuilder).</p>
30  * <p> This class has the same methods as
31  * <a HREF="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html">
32  * Java String</a> and
33  * <a HREF="http://msdn2.microsoft.com/en-us/library/system.string.aspx">
34  * .NET String</a> with the following benefits:<ul>
35  * <li> No need for an intermediate
36  * {@link StringBuffer}/{@link StringBuilder} in order to manipulate
37  * textual documents (insertion, deletion, concatenation). {@link Text}
38  * methods are also much faster for large documents.</li>
39  * <li> Bug free. They are not plagued by the {@link String#substring} <a
40  * HREF="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4513622">
41  * memory leak bug</a> (when small substrings prevent memory from
42  * larger string from being garbage collected).</li>
43  * <li> More flexible as they allows for search and comparison with any
44  * <code>java.lang.String</code> or <code>CharSequence</code>.</li>
45  * <li> Easy {@link TextFormat formatting} using the {@link TextBuilder}
46  * class (no need to specify the buffer capacity as
47  * it gently increases without incurring expensive resize/copy
48  * operations).</li>
49  * <li> Real-time compliant (instances allocated on the "stack" when
50  * executing in a {@link javolution.context.PoolContext PoolContext}).</li>
51  * </ul></p>
52  * <p> As for any <code>CharSequence</code>, parsing of primitive types can
53  * be achieved using the {@link javolution.text.TypeFormat} utility class.</p>
54  * <p> {@link Text} literals should be explicitely {@link #intern interned}.
55  * Unlike strings literals and strings-value constant expressions,
56  * interning is not implicit. For example:[code]
57  * final static Text TRUE = Text.intern("true");
58  * final static Text FALSE = Text.intern("false");
59  * [/code]
60  * Interned texts are always allocated in ImmortalMemory (RTSJ VMs).</p>
61  * <p><i> Implementation Note: To avoid expensive copy operations ,
62  * {@link Text} instances are broken down into smaller immutable
63  * sequences (they form a minimal-depth binary tree).
64  * Internal copies are then performed in <code>O[Log(n)]</code>
65  * instead of <code>O[n]</code>).</i></p>
66  *
67  * @author <a HREF="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
68  * @author Wilfried Middleton
69  * @version 4.2, January 11, 2007
70  */

71 public abstract class Text extends RealtimeObject implements CharSequence JavaDoc,
72         Comparable JavaDoc, Serializable, Immutable {
73
74     /**
75      * Holds the texts interned in ImmortalMemory
76      */

77     private static final FastMap INTERN_INSTANCES = new FastMap()
78             .setKeyComparator(FastComparator.LEXICAL);
79
80     private static String JavaDoc _tmpString;
81
82     private static final Runnable JavaDoc INTERN_STRING = new Runnable JavaDoc() {
83         public void run() {
84             StringWrapper txt = new StringWrapper();
85             txt._count = _tmpString.length();
86             txt._string = _tmpString;
87             INTERN_INSTANCES.put(_tmpString, txt);
88         }
89     };
90
91     /**
92      * Holds an empty character sequence.
93      */

94     public static final Text EMPTY = Text.intern("");
95
96     /**
97      * Holds the <code>"null"</code> character sequence.
98      */

99     public static final Text NULL = Text.intern("null");
100
101     /**
102      * Holds the default XML representation for Text instances.
103      * This representation consists of a <code>"value"</code> attribute
104      * holding the characters.
105      */

106     protected static final XMLFormat/*<Text>*/XML = new XMLFormat(Javolution
107             .j2meGetClass("javolution.text.Text")) {
108
109         public Object JavaDoc newInstance(Class JavaDoc cls,
110                 javolution.xml.XMLFormat.InputElement xml)
111                 throws XMLStreamException {
112             CharSequence JavaDoc csq = xml.getAttribute("value");
113             return csq != null ? Text.valueOf(csq) : Text.EMPTY;
114         }
115
116         public void read(InputElement xml, Object JavaDoc obj)
117                 throws XMLStreamException {
118             // Do nothing.
119
}
120
121         public void write(Object JavaDoc obj, OutputElement xml)
122                 throws XMLStreamException {
123             xml.setAttribute("value", (Text) obj);
124         }
125     };
126
127     /**
128      * Holds the total number of characters.
129      */

130     int _count;
131
132     /**
133      * Holds the hash code or <code>0</code> if unknown.
134      */

135     int _hashCode;
136
137     /**
138      * Default constructor.
139      */

140     private Text() {
141     }
142
143     /**
144      * Returns the text representing the specified object.
145      * If the specified object is <code>null</code> this method
146      * returns {@link #NULL}.
147      *
148      * @param obj the object to represent as text.
149      * @return the textual representation of the specified object.
150      * @see Realtime#toText
151      */

152     public static Text valueOf(Object JavaDoc obj) {
153         if (obj instanceof Realtime) {
154             return ((Realtime) obj).toText();
155         } else if (obj instanceof String JavaDoc) {
156             return StringWrapper.newInstance((String JavaDoc) obj);
157         } else if (obj instanceof CharSequence JavaDoc) {
158             final CharSequence JavaDoc csq = (CharSequence JavaDoc) obj;
159             return Text.valueOf(csq, 0, csq.length());
160         } else if (obj != null) {
161             return StringWrapper.newInstance(obj.toString());
162         } else {
163             return NULL;
164         }
165     }
166
167     /**
168      * Returns the text that contains the specified subsequence of characters.
169      *
170      * @param csq the character sequence source.
171      * @param start the index of the first character.
172      * @param end the index after the last character.
173      * @return the corresponding text instance.
174      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
175      * || (start > end) || (end > csq.length())</code>
176      */

177     public static Text valueOf(CharSequence JavaDoc csq, int start, int end) {
178         if ((start < 0) || (end < 0) || (start > end) || (end > csq.length()))
179             throw new IndexOutOfBoundsException JavaDoc();
180         if (csq instanceof TextBuilder)
181             return valueOf((TextBuilder) csq, start, end);
182         final int length = end - start;
183         if (length <= Primitive.BLOCK_SIZE) {
184             Primitive text = Primitive.newInstance(length);
185             for (int i = 0; i < length;) {
186                 text._data[i] = csq.charAt(start + i++);
187             }
188             return text;
189         } else {
190             final int middle = start + (length >> 1);
191             Composite text = Composite.newInstance(Text.valueOf(csq, start,
192                     middle), Text.valueOf(csq, middle, end));
193             return text;
194         }
195     }
196
197     // Optimized version for TextBuilder.
198
static Text valueOf(TextBuilder tb, int start, int end) {
199         final int length = end - start;
200         if (length <= Primitive.BLOCK_SIZE) {
201             Primitive text = Primitive.newInstance(length);
202             if ((start & TextBuilder.M0) + length <= TextBuilder.C0) {
203                 // Single block copy.
204
char[] chars = tb.charsAt(start);
205                 System.arraycopy(chars, start & TextBuilder.M0, text._data, 0,
206                         length);
207             } else { // Crossing of block boundary.
208
for (int i = 0; i < length;) {
209                     text._data[i] = tb.charAt(start + i++);
210                 }
211             }
212             return text;
213         } else { // Cut on the middle on a block boundary if possible.
214
int middle = (start + (length >> 1));
215             if ((middle & ~TextBuilder.M0) > start) {
216                 middle &= ~TextBuilder.M0;
217             }
218             Composite text = Composite.newInstance(Text.valueOf(tb, start,
219                     middle), Text.valueOf(tb, middle, end));
220             return text;
221         }
222     }
223
224     /**
225      * Returns the text that contains the characters from the specified
226      * array.
227      *
228      * @param chars the array source of the characters.
229      * @return the corresponding instance.
230      */

231     public static Text valueOf(char[] chars) {
232         return valueOf(chars, 0, chars.length);
233     }
234
235     /**
236      * Returns the text that contains the characters from the specified
237      * subarray of characters.
238      *
239      * @param chars the source of the characters.
240      * @param offset the index of the first character in the data soure.
241      * @param length the length of the text returned.
242      * @return the corresponding instance.
243      * @throws IndexOutOfBoundsException if <code>(offset < 0) ||
244      * (length < 0) || ((offset + length) > chars.length)</code>
245      */

246     public static Text valueOf(char[] chars, int offset, int length) {
247         if ((offset < 0) || (length < 0) || ((offset + length) > chars.length))
248             throw new IndexOutOfBoundsException JavaDoc();
249         if (length <= Primitive.BLOCK_SIZE) {
250             Primitive text = Primitive.newInstance(length);
251             for (int i = 0; i < length;) {
252                 text._data[i] = chars[offset + i++];
253             }
254             return text;
255         } else {
256             final int middle = offset + (length >> 1);
257             Composite text = Composite.newInstance(Text.valueOf(chars, offset,
258                     middle - offset), Text.valueOf(chars, middle, offset
259                     + length - middle));
260             return text;
261         }
262     }
263
264     /**
265      * Returns the text representation of the <code>boolean</code> argument.
266      *
267      * @param b a <code>boolean</code>.
268      * @return if the argument is <code>true</code>, the text
269      * <code>"true"</code> is returned; otherwise, the text
270      * <code>"false"</code> is returned.
271      */

272     public static Text valueOf(boolean b) {
273         return b ? TRUE : FALSE;
274     }
275
276     private static final Text TRUE = Text.intern("true");
277
278     private static final Text FALSE = Text.intern("false");
279
280     /**
281      * Returns the {@link #intern unique} text instance corresponding to the
282      * specified character.
283      *
284      * @param c a character.
285      * @return a text of length <code>1</code> containing <code>'c'</code>.
286      */

287     public static Text valueOf(char c) {
288         if ((c < 128) && (ASCII[c] != null))
289             return ASCII[c];
290         Primitive text = Primitive.newInstance(1);
291         text._data[0] = c;
292         Text textIntern = Text.intern(text);
293         if (c < 128) {
294             ASCII[c] = textIntern;
295         }
296         return textIntern;
297     }
298
299     private static final Text[] ASCII = new Text[128];
300
301     /**
302      * Returns the decimal representation of the specified <code>int</code>
303      * argument.
304      *
305      * @param i the <code>int</code> to format.
306      * @return the corresponding text instance.
307      */

308     public static Text valueOf(int i) {
309         TextBuilder tmp = TextBuilder.newInstance();
310         tmp.append(i);
311         Text txt = tmp.toText();
312         TextBuilder.recycle(tmp);
313         return txt;
314     }
315
316     /**
317      * Returns the radix representation of the specified <code>int</code>
318      * argument.
319      *
320      * @param i the <code>int</code> to format.
321      * @param radix the radix (e.g. <code>16</code> for hexadecimal).
322      * @return the corresponding text instance.
323      */

324     public static Text valueOf(int i, int radix) {
325         TextBuilder tmp = TextBuilder.newInstance();
326         tmp.append(i, radix);
327         Text txt = tmp.toText();
328         TextBuilder.recycle(tmp);
329         return txt;
330     }
331
332     /**
333      * Returns the decimal representation of the specified <code>long</code>
334      * argument.
335      *
336      * @param l the <code>long</code> to format.
337      * @return the corresponding text instance.
338      */

339     public static Text valueOf(long l) {
340         TextBuilder tmp = TextBuilder.newInstance();
341         tmp.append(l);
342         Text txt = tmp.toText();
343         TextBuilder.recycle(tmp);
344         return txt;
345     }
346
347     /**
348      * Returns the radix representation of the specified <code>long</code>
349      * argument.
350      *
351      * @param l the <code>long</code> to format.
352      * @param radix the radix (e.g. <code>16</code> for hexadecimal).
353      * @return the corresponding text instance.
354      */

355     public static Text valueOf(long l, int radix) {
356         TextBuilder tmp = TextBuilder.newInstance();
357         tmp.append(l, radix);
358         Text txt = tmp.toText();
359         TextBuilder.recycle(tmp);
360         return txt;
361     }
362
363     /**
364      * Returns the textual representation of the specified <code>float</code>
365      * instance.
366      *
367      * @param f the <code>float</code> to format.
368      * @return the corresponding text instance.
369      /*@JVM-1.1+@
370      public static Text valueOf(float f) {
371      TextBuilder tmp = TextBuilder.newInstance();
372      tmp.append(f);
373      Text txt = tmp.toText();
374      TextBuilder.recycle(tmp);
375      return txt;
376      }
377      /**/

378
379     /**
380      * Returns the textual representation of the specified <code>double</code>
381      * argument.
382      *
383      * @param d the <code>double</code> to format.
384      * @return the corresponding text instance.
385      /*@JVM-1.1+@
386      public static Text valueOf(double d) {
387      TextBuilder tmp = TextBuilder.newInstance();
388      tmp.append(d);
389      Text txt = tmp.toText();
390      TextBuilder.recycle(tmp);
391      return txt;
392      }
393      /**/

394
395     /**
396      * Returns the length of this text.
397      *
398      * @return the number of characters (16-bits Unicode) composing this text.
399      */

400     public final int length() {
401         return _count;
402     }
403
404     /**
405      * Returns the concatenation of this text and the textual
406      * representation of the specified object.
407      *
408      * @param obj the object whose textual representation is appended.
409      * @return <code>this.concat(Text.valueOf(obj))</code>
410      */

411     public final Text plus(Object JavaDoc obj) {
412         return this.concat(Text.valueOf(obj));
413     }
414
415     /**
416      * Concatenates the specified text to the end of this text.
417      * This method is extremely fast (faster even than
418      * <code>StringBuffer.append(String)</code>) and still returns
419      * a text instance with an internal binary tree of minimal depth!
420      *
421      * @param that the text that is concatenated.
422      * @return <code>this + that</code>
423      */

424     public final Text concat(Text that) {
425         if (this._count == 0) {
426             return that;
427         } else if (that._count == 0) {
428             return this;
429         } else {
430             return this.concat(that, (Factory.Pool) Composite.FACTORY
431                     .currentPool());
432         }
433     }
434
435     private final Text concat(Text that, Factory.Pool pool) {
436         // All Text instances are maintained balanced:
437
// (head < tail * 2) & (tail < head * 2)
438
if (((this._count << 1) < that._count) && (that instanceof Composite)) {
439             // this too small, returns (this + that/2) + (that/2)
440
Composite thatComposite = (Composite) that;
441             if (thatComposite._head._count > thatComposite._tail._count) {
442                 // Rotates to concatenate with smaller part.
443
thatComposite = thatComposite.rightRotation(pool);
444             }
445             return Composite.newInstance(
446                     this.concat(thatComposite._head, pool),
447                     thatComposite._tail, pool);
448         } else if (((that._count << 1) < this._count)
449                 && (this instanceof Composite)) {
450             // that too small, returns (this/2) + (this/2 concat that)
451
Composite thisComposite = (Composite) this;
452             if (thisComposite._tail._count > thisComposite._head._count) {
453                 // Rotates to concatenate with smaller part.
454
thisComposite = thisComposite.leftRotation(pool);
455             }
456             return Composite.newInstance(thisComposite._head,
457                     thisComposite._tail.concat(that, pool), pool);
458         } else { // this and that balanced (or not composite).
459
return Composite.newInstance(this, that, pool);
460         }
461     }
462
463     /**
464      * Returns a portion of this text.
465      *
466      * @param start the index of the first character inclusive.
467      * @return the sub-text starting at the specified position.
468      * @throws IndexOutOfBoundsException if <code>(start < 0) ||
469      * (start > this.length())</code>
470      */

471     public final Text subtext(int start) {
472         return subtext(start, length());
473     }
474
475     /**
476      * Returns the text having the specified text inserted at
477      * the specified location.
478      *
479      * @param index the insertion position.
480      * @param txt the text being inserted.
481      * @return <code>subtext(0, index).concat(txt).concat(subtext(index))</code>
482      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
483      * (index > this.length())</code>
484      */

485     public final Text insert(int index, Text txt) {
486         // Concatenates the smallest part first.
487
return ((index << 1) < _count) ? this.subtext(0, index).concat(txt)
488                 .concat(this.subtext(index)) : this.subtext(0, index).concat(
489                 txt.concat(this.subtext(index)));
490     }
491
492     /**
493      * Returns the text without the characters between the specified indexes.
494      *
495      * @param start the beginning index, inclusive.
496      * @param end the ending index, exclusive.
497      * @return <code>subtext(0, start).concat(subtext(end))</code>
498      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
499      * (start > end) || (end > this.length()</code>
500      */

501     public final Text delete(int start, int end) {
502         return this.subtext(0, start).concat(this.subtext(end));
503     }
504
505     /**
506      * Replaces each character sequence of this text that matches the specified
507      * target sequence with the specified replacement sequence.
508      *
509      * @param target the character sequence to be replaced.
510      * @param replacement the replacement sequence.
511      * @return the resulting text.
512      */

513     public final Text replace(CharSequence JavaDoc target, CharSequence JavaDoc replacement) {
514         int i = indexOf(target);
515         return (i < 0) ? this : // No target sequence found.
516
this.subtext(0, i).concat(Text.valueOf(replacement)).concat(
517                         this.subtext(i + target.length()).replace(target,
518                                 replacement));
519     }
520
521     /**
522      * Replaces the specified characters in this text with the specified
523      * replacement sequence.
524      *
525      * @param charSet the set of characters to be replaced.
526      * @param replacement the replacement sequence.
527      * @return the resulting text.
528      */

529     public final Text replace(CharSet charSet, CharSequence JavaDoc replacement) {
530         int i = indexOfAny(charSet);
531         return (i < 0) ? this : // No character to replace.
532
this.subtext(0, i).concat(Text.valueOf(replacement)).concat(
533                         this.subtext(i + 1).replace(charSet, replacement));
534     }
535
536     /**
537      * Returns {@link #subtext(int, int) subtext(start, end)}.
538      *
539      * @param start the index of the first character inclusive.
540      * @param end the index of the last character exclusive.
541      * @return <code>this.subtext(start, end)</code>
542      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
543      * (start > end) || (end > this.length())</code>
544      */

545     public final CharSequence JavaDoc subSequence(int start, int end) {
546         return this.subtext(start, end);
547     }
548
549     /**
550      * Returns the index within this text of the first occurrence
551      * of the specified character sequence searching forward.
552      *
553      * @param csq a character sequence.
554      * @return the index of the first character of the character sequence found;
555      * or <code>-1</code> if the character sequence is not found.
556      */

557     public final int indexOf(CharSequence JavaDoc csq) {
558         return indexOf(csq, 0);
559     }
560
561     /**
562      * Returns the index within this text of the first occurrence
563      * of the specified characters sequence searching forward from
564      * the specified index.
565      *
566      * @param csq a character sequence.
567      * @param fromIndex the index to start the search from.
568      * @return the index in the range
569      * <code>[fromIndex, length() - csq.length()]</code>
570      * or <code>-1</code> if the character sequence is not found.
571      */

572     public final int indexOf(CharSequence JavaDoc csq, int fromIndex) {
573
574         // Limit cases.
575
final int csqLength = csq.length();
576         final int min = Math.max(0, fromIndex);
577         final int max = _count - csqLength;
578         if (csqLength == 0) {
579             return (min > max) ? -1 : min;
580         }
581
582         // Searches for csq.
583
final char c = csq.charAt(0);
584         for (int i = indexOf(c, min); (i >= 0) && (i <= max); i = indexOf(c,
585                 ++i)) {
586             boolean match = true;
587             for (int j = 1; j < csqLength; j++) {
588                 if (this.charAt(i + j) != csq.charAt(j)) {
589                     match = false;
590                     break;
591                 }
592             }
593             if (match) {
594                 return i;
595             }
596         }
597         return -1;
598     }
599
600     /**
601      * Returns the index within this text of the last occurrence of
602      * the specified characters sequence searching backward.
603      *
604      * @param csq a character sequence.
605      * @return the index of the first character of the character sequence found;
606      * or <code>-1</code> if the character sequence is not found.
607      */

608     public final int lastIndexOf(CharSequence JavaDoc csq) {
609         return lastIndexOf(csq, _count);
610     }
611
612     /**
613      * Returns the index within this text of the last occurrence of
614      * the specified character sequence searching backward from the specified
615      * index.
616      *
617      * @param csq a character sequence.
618      * @param fromIndex the index to start the backward search from.
619      * @return the index in the range <code>[0, fromIndex]</code> or
620      * <code>-1</code> if the character sequence is not found.
621      */

622     public final int lastIndexOf(CharSequence JavaDoc csq, int fromIndex) {
623
624         // Limit cases.
625
final int csqLength = csq.length();
626         final int min = 0;
627         final int max = Math.min(fromIndex, _count - csqLength);
628         if (csqLength == 0) {
629             return (min > max) ? -1 : max;
630         }
631
632         // Searches for csq.
633
final char c = csq.charAt(0);
634         for (int i = lastIndexOf(c, max); (i >= 0); i = lastIndexOf(c, --i)) {
635             boolean match = true;
636             for (int j = 1; j < csqLength; j++) {
637                 if (this.charAt(i + j) != csq.charAt(j)) {
638                     match = false;
639                     break;
640                 }
641             }
642             if (match) {
643                 return i;
644             }
645         }
646         return -1;
647
648     }
649
650     /**
651      * Indicates if this text starts with the specified prefix.
652      *
653      * @param prefix the prefix.
654      * @return <code>true</code> if the character sequence represented by the
655      * argument is a prefix of the character sequence represented by
656      * this text; <code>false</code> otherwise.
657      */

658     public final boolean startsWith(CharSequence JavaDoc prefix) {
659         return startsWith(prefix, 0);
660     }
661
662     /**
663      * Indicates if this text ends with the specified suffix.
664      *
665      * @param suffix the suffix.
666      * @return <code>true</code> if the character sequence represented by the
667      * argument is a suffix of the character sequence represented by
668      * this text; <code>false</code> otherwise.
669      */

670     public final boolean endsWith(CharSequence JavaDoc suffix) {
671         return startsWith(suffix, length() - suffix.length());
672     }
673
674     /**
675      * Indicates if this text starts with the specified prefix
676      * at the specified index.
677      *
678      * @param prefix the prefix.
679      * @param index the index of the prefix location in this string.
680      * @return <code>this.substring(index).startsWith(prefix)</code>
681      */

682     public final boolean startsWith(CharSequence JavaDoc prefix, int index) {
683         final int prefixLength = prefix.length();
684         if ((index >= 0) && (index <= (this.length() - prefixLength))) {
685             for (int i = 0, j = index; i < prefixLength;) {
686                 if (prefix.charAt(i++) != this.charAt(j++)) {
687                     return false;
688                 }
689             }
690             return true;
691         } else {
692             return false;
693         }
694     }
695
696     /**
697      * Returns a copy of this text, with leading and trailing
698      * whitespace omitted.
699      *
700      * @return a copy of this text with leading and trailing white
701      * space removed, or this text if it has no leading or
702      * trailing white space.
703      */

704     public final Text trim() {
705         int first = 0; // First character index.
706
int last = length() - 1; // Last character index.
707
while ((first <= last) && (charAt(first) <= ' ')) {
708             first++;
709         }
710         while ((last >= first) && (charAt(last) <= ' ')) {
711             last--;
712         }
713         return subtext(first, last + 1);
714     }
715
716     /**
717      * Returns a text equals to the specified character sequence from a pool of
718      * unique text instances in <code>ImmortalMemory</code>.
719      *
720      * @return an unique text instance allocated in <code>ImmortalMemory</code>.
721      */

722     public static Text intern(final CharSequence JavaDoc csq) {
723         Text text = (Text) INTERN_INSTANCES.get(csq); // Thread-Safe - No entry removed.
724
if (text != null)
725             return text;
726         return Text.intern(csq.toString());
727     }
728
729     /**
730      * Returns a text equals to the specified string from a pool of
731      * unique text instances in <code>ImmortalMemory</code>.
732      *
733      * @return an unique text instance allocated in <code>ImmortalMemory</code>.
734      */

735     public static Text intern(final String JavaDoc str) {
736         Text text = (Text) INTERN_INSTANCES.get(str); // Thread-Safe - No entry removed.
737
if (text != null)
738             return text;
739         synchronized (INTERN_INSTANCES) {
740             // Synchronized check to ensure unicity.
741
text = (Text) INTERN_INSTANCES.get(str);
742             if (text != null)
743                 return text;
744             _tmpString = str;
745             MemoryArea.getMemoryArea(INTERN_INSTANCES).executeInArea(
746                     INTERN_STRING);
747         }
748         return (Text) INTERN_INSTANCES.get(str);
749     }
750
751     /**
752      * Indicates if this text has the same character content as the specified
753      * character sequence.
754      *
755      * @param csq the character sequence to compare with.
756      * @return <code>true</code> if the specified character sequence has the
757      * same character content as this text; <code>false</code> otherwise.
758      */

759     public final boolean contentEquals(CharSequence JavaDoc csq) {
760         if (csq.length() != _count)
761             return false;
762         for (int i = 0; i < _count;) {
763             if (this.charAt(i) != csq.charAt(i++))
764                 return false;
765         }
766         return true;
767     }
768
769     /**
770      * Indicates if this text has the same character contend as the specified
771      * character sequence ignoring case considerations.
772      *
773      * @param csq the <code>CharSequence</code> to compare this text against.
774      * @return <code>true</code> if the argument and this text are equal,
775      * ignoring case; <code>false</code> otherwise.
776      */

777     public final boolean contentEqualsIgnoreCase(CharSequence JavaDoc csq) {
778         if (this._count != csq.length())
779             return false;
780         for (int i = 0; i < _count;) {
781             char u1 = this.charAt(i);
782             char u2 = csq.charAt(i++);
783             if (u1 != u2) {
784                 u1 = Character.toUpperCase(u1);
785                 u2 = Character.toUpperCase(u2);
786                 if ((u1 != u2)
787                         && (Character.toLowerCase(u1) != Character
788                                 .toLowerCase(u2)))
789                     return false;
790
791             }
792         }
793         return true;
794     }
795
796     /**
797      * Compares this text against the specified object for equality.
798      * Returns <code>true</code> if the specified object is a text having
799      * the same character sequence as this text.
800      * For generic comparaison with any character sequence the
801      * {@link #contentEquals(CharSequence)} should be used.
802      *
803      * @param obj the object to compare with or <code>null</code>.
804      * @return <code>true</code> if that is a text with the same character
805      * sequence as this text; <code>false</code> otherwise.
806      */

807     public final boolean equals(Object JavaDoc obj) {
808         if (this == obj)
809             return true;
810         if (!(obj instanceof Text))
811             return false;
812         final Text that = (Text) obj;
813         if (this._count != that._count)
814             return false;
815         for (int i = 0; i < _count;) {
816             if (this.charAt(i) != that.charAt(i++))
817                 return false;
818         }
819         return true;
820     }
821
822     /**
823      * Returns the hash code for this text.
824      *
825      * @return the hash code value.
826      */

827     public final int hashCode() {
828         if (_hashCode != 0)
829             return _hashCode;
830         int h = _hashCode;
831         final int length = this.length();
832         for (int i = 0; i < length;) {
833             h = 31 * h + charAt(i++);
834         }
835         return _hashCode = h;
836     }
837
838     /**
839      * Compares this text to another character sequence or string
840      * lexicographically.
841      *
842      * @param csq the character sequence to be compared.
843      * @return <code>TypeFormat.LEXICAL_COMPARATOR.compare(this, csq)</code>
844      * @throws ClassCastException if the specifed object is not a
845      * <code>CharSequence</code> or a <code>String</code>.
846      */

847     public final int compareTo(Object JavaDoc csq) {
848         return ((FastComparator) FastComparator.LEXICAL).compare(this, csq);
849     }
850
851     /**
852      * Returns <code>this</code> (implements
853      * {@link javolution.context.Realtime Realtime} interface).
854      *
855      * @return <code>this</code>
856      */

857     public final Text toText() {
858         return this;
859     }
860
861     /**
862      * Returns a compact copy of this text allocated on the stack when
863      * executing in a {@link javolution.context.PoolContext PoolContext}.
864      *
865      * @return a local compact copy of this text.
866      */

867     public final Text copy() {
868         return Text.valueOf(this, 0, _count);
869     }
870
871     /**
872      * Returns the depth of the internal tree used to represent this text.
873      *
874      * @return the maximum depth of the text internal binary tree.
875      */

876     public abstract int depth();
877
878     /**
879      * Returns the character at the specified index.
880      *
881      * @param index the index of the character.
882      * @return the character at the specified index.
883      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
884      * (index >= this.length())</code>
885      */

886     public abstract char charAt(int index);
887
888     /**
889      * Returns the index within this text of the first occurrence of the
890      * specified character, starting the search at the specified index.
891      *
892      * @param c the character to search for.
893      * @param fromIndex the index to start the search from.
894      * @return the index of the first occurrence of the character in this text
895      * that is greater than or equal to <code>fromIndex</code>,
896      * or <code>-1</code> if the character does not occur.
897      */

898     public abstract int indexOf(char c, int fromIndex);
899
900     /**
901      * Returns the index within this text of the first occurrence of the
902      * specified character, searching backward and starting at the specified
903      * index.
904      *
905      * @param c the character to search for.
906      * @param fromIndex the index to start the search backward from.
907      * @return the index of the first occurrence of the character in this text
908      * that is less than or equal to <code>fromIndex</code>,
909      * or <code>-1</code> if the character does not occur.
910      */

911     public abstract int lastIndexOf(char c, int fromIndex);
912
913     /**
914      * Returns a portion of this text.
915      *
916      * @param start the index of the first character inclusive.
917      * @param end the index of the last character exclusive.
918      * @return the sub-text starting at the specified start position and
919      * ending just before the specified end position.
920      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
921      * (start > end) || (end > this.length())</code>
922      */

923     public abstract Text subtext(int start, int end);
924
925     /**
926      * Copies the characters from this text into the destination
927      * character array.
928      *
929      * @param start the index of the first character to copy.
930      * @param end the index after the last character to copy.
931      * @param dest the destination array.
932      * @param destPos the start offset in the destination array.
933      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
934      * (start > end) || (end > this.length())</code>
935      */

936     public abstract void getChars(int start, int end, char dest[], int destPos);
937
938     /**
939      * Converts the characters of this text to lower case.
940      *
941      * @return the text in lower case.
942      * @see Character#toLowerCase(char)
943      */

944     public abstract Text toLowerCase();
945
946     /**
947      * Converts the characters of this text to upper case.
948      *
949      * @return the text in lower case.
950      * @see Character#toUpperCase(char)
951      */

952     public abstract Text toUpperCase();
953
954     /**
955      * Returns the <code>String</code> value corresponding to this text.
956      *
957      * @return the <code>java.lang.String</code> for this text.
958      */

959     public abstract String JavaDoc stringValue();
960
961     //////////////////////////////////////////////////////////////////
962
// Wilfried add-ons (methods provided by Microsoft .Net in C#)
963
//
964

965     /**
966      * Returns the text that contains a specific length sequence of the
967      * character specified.
968      *
969      * @param c the character to fill this text with.
970      * @param length the length of the text returned.
971      * @return the corresponding instance.
972      * @throws IndexOutOfBoundsException if <code>(length < 0)</code>
973      */

974     public static Text valueOf(char c, int length) {
975         if (length < 0)
976             throw new IndexOutOfBoundsException JavaDoc();
977         if (length <= Primitive.BLOCK_SIZE) {
978             Primitive text = Primitive.newInstance(length);
979             for (int i = 0; i < length;) {
980                 text._data[i++] = c;
981             }
982             return text;
983         } else {
984             final int middle = (length >> 1);
985             return Composite.newInstance(Text.valueOf(c, middle), Text.valueOf(
986                     c, length - middle));
987         }
988     }
989
990     /**
991      * Indicates if all characters of this text are whitespaces
992      * (no characters greater than the space character).
993      *
994      *@return <code>true</code> if this text contains only whitespace.
995      */

996     public final boolean isBlank() {
997         return isBlank(0, length());
998     }
999
1000    /**
1001     * Indicates if the specified sub-range of characters of this text
1002     * are whitespaces (no characters greater than the space character).
1003     *
1004     *@param start the start index.
1005     *@param length the number of characters to inspect.
1006     */

1007    public final boolean isBlank(int start, int length) {
1008        for (; start < length; start++) {
1009            if (charAt(start) > ' ')
1010                return false;
1011        }
1012        return true;
1013    }
1014
1015    /**
1016     * Returns a copy of this text, with leading whitespace omitted.
1017     *
1018     * @return a copy of this text with leading white space removed,
1019     * or this text if it has no leading white space.
1020     */

1021    public final Text trimStart() {
1022        int first = 0; // First character index.
1023
int last = length() - 1; // Last character index.
1024
while ((first <= last) && (charAt(first) <= ' ')) {
1025            first++;
1026        }
1027        return subtext(first, last + 1);
1028    }
1029
1030    /**
1031     * Returns a copy of this text, with trailing
1032     * whitespace omitted.
1033     *
1034     * @return a copy of this text with trailing white space removed,
1035     * or this text if it has no trailing white space.
1036     */

1037    public final Text trimEnd() {
1038        int first = 0; // First character index.
1039
int last = length() - 1; // Last character index.
1040
while ((last >= first) && (charAt(last) <= ' ')) {
1041            last--;
1042        }
1043        return subtext(first, last + 1);
1044    }
1045
1046    /**
1047     * Pads this text on the left with spaces to make the minimum total length
1048     * as specified.
1049     * The new length of the new text is equal to the original length plus
1050     * <code>(length()-len)</code> spaces.
1051     *
1052     * @param len the total number of characters to make this text equal to.
1053     * @return a new text or the same text if no padding required.
1054     * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1055     */

1056    public final Text padLeft(int len) {
1057        return padLeft(len, ' ');
1058    }
1059
1060    /**
1061     * Pads this text on the left to make the minimum total length as specified.
1062     * Spaces or the given Unicode character are used to pad with.
1063     * <br>
1064     * The new length of the new text is equal to the original length plus
1065     * <code>(length()-len)</code> pad characters.
1066     *
1067     * @param len the total number of characters to make this text equal to.
1068     * @param c the character to pad using.
1069     * @return a new text or the same text if no padding required.
1070     * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1071     */

1072    public final Text padLeft(int len, char c) {
1073        final int padSize = (len <= length()) ? 0 : len - length();
1074        return this.insert(0, Text.valueOf(c, padSize));
1075    }
1076
1077    /**
1078     * Pads this text on the right with spaces to make the minimum total length
1079     * as specified.
1080     * The new length of the new text is equal to the original length plus
1081     * <code>(length()-len)</code> spaces.
1082     *
1083     * @param len the total number of characters to make this text equal to.
1084     * @return a new text or the same text if no padding required.
1085     * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1086     */

1087    public final Text padRight(int len) {
1088        return padRight(len, ' ');
1089    }
1090
1091    /**
1092     * Pads this text on the right to make the minimum total length as specified.
1093     * Spaces or the given Unicode character are used to pad with.
1094     * <br>
1095     * The new length of the new text is equal to the original length plus
1096     * <code>(length()-len)</code> pad characters.
1097     *
1098     * @param len the total number of characters to make this text equal to.
1099     * @param c the character to pad using.
1100     * @return a new text or the same text if no padding required.
1101     * @throws an IllegalArgumentException if the <code>(len<0)</code>.
1102     */

1103    public final Text padRight(int len, char c) {
1104        final int padSize = (len <= length()) ? 0 : len - length();
1105        return this.concat(Text.valueOf(c, padSize));
1106    }
1107
1108    /**
1109     * Returns the index within this text of the first occurrence
1110     * of any character in the specified character set.
1111     *
1112     * @param charSet the character set.
1113     * @return the index of the first character that matches one of the
1114     * characters in the supplied set; or <code>-1</code> if none.
1115     */

1116    public final int indexOfAny(CharSet charSet) {
1117        return indexOfAny(charSet, 0, length());
1118    }
1119
1120    /**
1121     * Returns the index within a region of this text of the first occurrence
1122     * of any character in the specified character set.
1123     *
1124     * @param charSet the character set.
1125     * @param start the index of the start of the search region in this text.
1126     * @return the index of the first character that matches one of the
1127     * characters in the supplied set; or <code>-1</code> if none.
1128     */

1129    public final int indexOfAny(CharSet charSet, int start) {
1130        return indexOfAny(charSet, start, length() - start);
1131    }
1132
1133    /**
1134     * Returns the index within a region of this text of the first occurrence
1135     * of any character in the specified character set.
1136     *
1137     * @param charSet the character set.
1138     * @param start the index of the start of the search region in this text.
1139     * @param length the length of the region to search.
1140     * @return the index of the first character that matches one of the
1141     * characters in the supplied array; or <code>-1</code> if none.
1142     */

1143    public final int indexOfAny(CharSet charSet, int start, int length) {
1144        final int stop = start + length;
1145        for (int i = start; i < stop; i++) {
1146            if (charSet.contains(charAt(i)))
1147                return i;
1148        }
1149        return -1;
1150    }
1151
1152    /**
1153     * Returns the index within this text of the last occurrence
1154     * of any character in the specified character set.
1155     *
1156     * @param charSet the character set.
1157     * @return the index of the last character that matches one of the
1158     * characters in the supplied array; or <code>-1</code> if none.
1159     */

1160    public final int lastIndexOfAny(CharSet charSet) {
1161        return lastIndexOfAny(charSet, 0, length());
1162    }
1163
1164    /**
1165     * Returns the index within a region of this text of the last occurrence
1166     * of any character in the specified character set.
1167     *
1168     * @param charSet the character set.
1169     * @param start the index of the start of the search region in this text.
1170     * @return the index of the last character that matches one of the
1171     * characters in the supplied array; or <code>-1</code> if none.
1172     */

1173    public final int lastIndexOfAny(CharSet charSet, int start) {
1174        return lastIndexOfAny(charSet, start, length() - start);
1175    }
1176
1177    /**
1178     * Returns the index within a region of this text of the last occurrence
1179     * of any character in the specified character set.
1180     *
1181     * @param charSet the character set.
1182     * @param start the index of the start of the search region in this text.
1183     * @param length the length of the region to search.
1184     * @return the index of the last character that matches one of the
1185     * characters in the supplied array; or <code>-1</code> if none.
1186     */

1187    public final int lastIndexOfAny(CharSet charSet, int start, int length) {
1188        for (int i = start + length; --i >= start;) {
1189            if (charSet.contains(charAt(i)))
1190                return i;
1191        }
1192        return -1;
1193    }
1194
1195    //
1196
////////////////////////////////////////////////////////////////////////////
1197

1198    /**
1199     * This class represents a text block (up to 32 characters).
1200     */

1201    private static final class Primitive extends Text {
1202
1203        /**
1204         * Holds the default size for primitive blocks of characters.
1205         */

1206        private static final int BLOCK_SIZE = 1 << TextBuilder.D0;
1207
1208        /**
1209         * Holds the associated factory.
1210         */

1211        private static final Factory FACTORY = new Factory() {
1212
1213            public Object JavaDoc create() {
1214                return new Primitive();
1215            }
1216        };
1217
1218        /**
1219         * Holds the raw data (primitive).
1220         */

1221        private final char[] _data = new char[BLOCK_SIZE];
1222
1223        /**
1224         * Default constructor.
1225         */

1226        private Primitive() {
1227        }
1228
1229        /**
1230         * Returns a new/recycled primitive text of specified length.
1231         *
1232         * @param the length of this primitive text.
1233         */

1234        private static Primitive newInstance(int length) {
1235            return newInstance(length, (Factory.Pool) FACTORY.currentPool());
1236        }
1237
1238        private static Primitive newInstance(int length, Factory.Pool pool) {
1239            Primitive text = (Primitive) pool.next();
1240            text._count = length;
1241            text._hashCode = 0;
1242            return text;
1243        }
1244
1245        // Implements abstract method.
1246
public int depth() {
1247            return 0;
1248        }
1249
1250        // Implements abstract method.
1251
public char charAt(int index) {
1252            if (index >= _count)
1253                throw new IndexOutOfBoundsException JavaDoc();
1254            return _data[index];
1255        }
1256
1257        // Implements abstract method.
1258
public int indexOf(char c, int fromIndex) {
1259            for (int i = Math.max(fromIndex, 0); i < _count; i++) {
1260                if (_data[i] == c)
1261                    return i;
1262            }
1263            return -1;
1264        }
1265
1266        // Implements abstract method.
1267
public int lastIndexOf(char c, int fromIndex) {
1268            for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) {
1269                if (_data[i] == c)
1270                    return i;
1271            }
1272            return -1;
1273        }
1274
1275        // Implements abstract method.
1276
public Text subtext(int start, int end) {
1277            if ((start == 0) && (end == _count))
1278                return this;
1279            if ((start < 0) || (start > end) || (end > _count))
1280                throw new IndexOutOfBoundsException JavaDoc();
1281            if (start == end)
1282                return Text.EMPTY;
1283            Primitive text = Primitive.newInstance(end - start);
1284            for (int i = start, j = 0; i < end;) {
1285                text._data[j++] = _data[i++];
1286            }
1287            return text;
1288        }
1289
1290        // Implements abstract method.
1291
public void getChars(int start, int end, char dest[], int destPos) {
1292            if ((end > _count) || (end < start))
1293                throw new IndexOutOfBoundsException JavaDoc();
1294            for (int i = start, j = destPos; i < end;) {
1295                dest[j++] = _data[i++];
1296            }
1297        }
1298
1299        // Implements abstract method.
1300
public Text toLowerCase() {
1301            Primitive text = newInstance(_count);
1302            for (int i = 0; i < _count;) {
1303                text._data[i] = Character.toLowerCase(_data[i++]);
1304            }
1305            return text;
1306        }
1307
1308        // Implements abstract method.
1309
public Text toUpperCase() {
1310            Primitive text = newInstance(_count);
1311            for (int i = 0; i < _count;) {
1312                text._data[i] = Character.toUpperCase(_data[i++]);
1313            }
1314            return text;
1315        }
1316
1317        // Implements abstract method.
1318
public String JavaDoc stringValue() {
1319            return new String JavaDoc(_data, 0, _count);
1320        }
1321    }
1322
1323    /**
1324     * This class represents a text composite.
1325     */

1326    private static final class Composite extends Text {
1327
1328        /**
1329         * Holds the associtate factory.
1330         */

1331        private static final Factory FACTORY = new Factory() {
1332
1333            public Object JavaDoc create() {
1334                return new Composite();
1335            }
1336        };
1337
1338        /**
1339         * Holds the head block of character (composite).
1340         */

1341        private Text _head;
1342
1343        /**
1344         * Holds the tail block of character (composite).
1345         */

1346        private Text _tail;
1347
1348        /**
1349         * Default constructor.
1350         */

1351        private Composite() {
1352        }
1353
1354        /**
1355         * Returns a new/recycled composite text.
1356         *
1357         * @param head the head of this composite.
1358         * @param tail the tail of this composite.
1359         * @return the corresponding composite text.
1360         */

1361        private static Composite newInstance(Text head, Text tail) {
1362            return newInstance(head, tail, (Factory.Pool) FACTORY.currentPool());
1363        }
1364
1365        private static Composite newInstance(Text head, Text tail,
1366                Factory.Pool pool) {
1367            Composite text = (Composite) pool.next();
1368            text._hashCode = 0;
1369            text._count = head._count + tail._count;
1370            text._head = head;
1371            text._tail = tail;
1372            return text;
1373        }
1374
1375        /**
1376         * Returns the right rotation of this composite.
1377         * The resulting text is still balanced if head > tail.
1378         *
1379         * @param pool the current pool.
1380         * @return the same text with a different structure.
1381         */

1382        private Composite rightRotation(Factory.Pool pool) {
1383            // See: http://en.wikipedia.org/wiki/Tree_rotation
1384
if (!(this._head instanceof Composite))
1385                return this; // Cannot rotate.
1386
Composite P = (Composite) this._head;
1387            Text A = P._head;
1388            Text B = P._tail;
1389            Text C = this._tail;
1390            return Composite.newInstance(A, Composite.newInstance(B, C, pool),
1391                    pool);
1392        }
1393
1394        /**
1395         * Returns the left rotation of this composite.
1396         * The resulting text is still balanced if tail > head.
1397         *
1398         * @param pool the current pool.
1399         * @return the same text with a different structure.
1400         */

1401        private Composite leftRotation(Factory.Pool pool) {
1402            // See: http://en.wikipedia.org/wiki/Tree_rotation
1403
if (!(this._tail instanceof Composite))
1404                return this; // Cannot rotate.
1405
Composite Q = (Composite) this._tail;
1406            Text B = Q._head;
1407            Text C = Q._tail;
1408            Text A = this._head;
1409            return Composite.newInstance(Composite.newInstance(A, B, pool), C,
1410                    pool);
1411        }
1412
1413        // Implements abstract method.
1414
public int depth() {
1415            return Math.max(_head.depth(), _tail.depth()) + 1;
1416        }
1417
1418        // Implements abstract method.
1419
public char charAt(int index) {
1420            return (index < _head._count) ? _head.charAt(index) : _tail
1421                    .charAt(index - _head._count);
1422        }
1423
1424        // Implements abstract method.
1425
public int indexOf(char c, int fromIndex) {
1426            final int cesure = _head._count;
1427            if (fromIndex < cesure) {
1428                final int headIndex = _head.indexOf(c, fromIndex);
1429                if (headIndex >= 0)
1430                    return headIndex; // Found in head.
1431
}
1432            final int tailIndex = _tail.indexOf(c, fromIndex - cesure);
1433            return (tailIndex >= 0) ? tailIndex + cesure : -1;
1434        }
1435
1436        // Implements abstract method.
1437
public int lastIndexOf(char c, int fromIndex) {
1438            final int cesure = _head._count;
1439            if (fromIndex >= cesure) {
1440                final int tailIndex = _tail.lastIndexOf(c, fromIndex - cesure);
1441                if (tailIndex >= 0)
1442                    return tailIndex + cesure; // Found in tail.
1443
}
1444            return _head.lastIndexOf(c, fromIndex);
1445        }
1446
1447        // Implements abstract method.
1448
public Text subtext(int start, int end) {
1449            final int cesure = _head._count;
1450            if (end <= cesure)
1451                return _head.subtext(start, end);
1452            if (start >= cesure)
1453                return _tail.subtext(start - cesure, end - cesure);
1454            if ((start == 0) && (end == _count))
1455                return this;
1456            // Overlaps head and tail.
1457
return _head.subtext(start, cesure).concat(
1458                    _tail.subtext(0, end - cesure));
1459        }
1460
1461        // Implements abstract method.
1462
public void getChars(int start, int end, char dest[], int destPos) {
1463            final int cesure = _head._count;
1464            if (end <= cesure) {
1465                _head.getChars(start, end, dest, destPos);
1466            } else if (start >= cesure) {
1467                _tail.getChars(start - cesure, end - cesure, dest, destPos);
1468            } else { // Overlaps head and tail.
1469
_head.getChars(start, cesure, dest, destPos);
1470                _tail.getChars(0, end - cesure, dest, destPos + cesure - start);
1471            }
1472        }
1473
1474        // Implements abstract method.
1475
public Text toLowerCase() {
1476            return newInstance(_head.toLowerCase(), _tail.toLowerCase());
1477        }
1478
1479        // Implements abstract method.
1480
public Text toUpperCase() {
1481            return newInstance(_head.toUpperCase(), _tail.toUpperCase());
1482        }
1483
1484        // Implements abstract method.
1485
public String JavaDoc stringValue() {
1486            char[] data = new char[_count];
1487            this.getChars(0, _count, data, 0);
1488            return new String JavaDoc(data, 0, _count);
1489        }
1490
1491        // Overrides.
1492
public boolean move(ObjectSpace os) {
1493            if (super.move(os)) {
1494                _head.move(os);
1495                _tail.move(os);
1496                return true;
1497            }
1498            return false;
1499        }
1500    }
1501
1502    /**
1503     * This class represents a String Wrapper.
1504     */

1505    private static final class StringWrapper extends Text {
1506
1507        /**
1508         * Holds the associated factory.
1509         */

1510        private static final Factory FACTORY = new Factory() {
1511
1512            public Object JavaDoc create() {
1513                return new StringWrapper();
1514            }
1515        };
1516
1517        /**
1518         * Holds the string.
1519         */

1520        private String JavaDoc _string;
1521
1522        /**
1523         * Holds the offset.
1524         */

1525        private int _offset;
1526
1527        /**
1528         * Default constructor.
1529         */

1530        private StringWrapper() {
1531        }
1532
1533        /**
1534         * Returns a new/recycled text wrapping the specified string.
1535         *
1536         * @param str the string to wrap.
1537         * @return the corresponding text instance.
1538         */

1539        private static StringWrapper newInstance(String JavaDoc str) {
1540            StringWrapper text = (StringWrapper) FACTORY.object();
1541            text._count = str.length();
1542            text._hashCode = 0;
1543            text._string = str;
1544            text._offset = 0;
1545            return text;
1546        }
1547
1548        // Implements abstract method.
1549
public int depth() {
1550            return 0;
1551        }
1552
1553        // Implements abstract method.
1554
public char charAt(int index) {
1555            if ((index >= _count) || (index < 0))
1556                throw new IndexOutOfBoundsException JavaDoc();
1557            return _string.charAt(_offset + index);
1558        }
1559
1560        // Implements abstract method.
1561
public int indexOf(char c, int fromIndex) {
1562            for (int i = Math.max(fromIndex, 0); i < _count; i++) {
1563                if (_string.charAt(_offset + i) == c)
1564                    return i;
1565            }
1566            return -1;
1567        }
1568
1569        // Implements abstract method.
1570
public int lastIndexOf(char c, int fromIndex) {
1571            for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) {
1572                if (_string.charAt(_offset + i) == c)
1573                    return i;
1574            }
1575            return -1;
1576        }
1577
1578        // Implements abstract method.
1579
public Text subtext(int start, int end) {
1580            if ((start == 0) && (end == _count))
1581                return this;
1582            if ((start < 0) || (start > end) || (end > _count))
1583                throw new IndexOutOfBoundsException JavaDoc();
1584            if (start == end)
1585                return Text.EMPTY;
1586            StringWrapper text = (StringWrapper) FACTORY.object();
1587            text._count = end - start;
1588            text._hashCode = 0;
1589            text._string = _string;
1590            text._offset = _offset + start;
1591            return text;
1592        }
1593
1594        // Implements abstract method.
1595
public void getChars(int start, int end, char dest[], int destPos) {
1596            if ((end > _count) || (end < start) || (start < 0))
1597                throw new IndexOutOfBoundsException JavaDoc();
1598            _string.getChars(start + _offset, end + _offset, dest, destPos);
1599        }
1600
1601        // Implements abstract method.
1602
public Text toLowerCase() {
1603            return copy().toLowerCase(); // To avoid dynamic heap allocation.
1604
}
1605
1606        // Implements abstract method.
1607
public Text toUpperCase() {
1608            return copy().toUpperCase(); // To avoid dynamic heap allocation.
1609
}
1610
1611        // Implements abstract method.
1612
public String JavaDoc stringValue() {
1613            if ((_offset == 0) && (_count == _string.length()))
1614                return _string;
1615            return _string.substring(_offset, _offset + _count);
1616        }
1617
1618    }
1619}
Popular Tags