KickJava   Java API By Example, From Geeks To Geeks.

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

63 public abstract class Text extends RealtimeObject implements CharSequence,
64         Comparable, Serializable {
65
66     /**
67      * Holds the text interned.
68      */

69     private static final FastMap INTERN_TEXT = new FastMap();
70
71     /**
72      * Holds an empty character sequence.
73      */

74     public static final Text EMPTY = Text.valueOf("").intern();
75
76     /**
77      * Holds the <code>"null"</code> character sequence.
78      */

79     public static final Text NULL = Text.valueOf("null").intern();
80
81     /**
82      * Holds the total number of characters.
83      */

84     int _count;
85
86     /**
87      * Holds the hash code or <code>0</code> if unknown.
88      */

89     int _hashCode;
90
91     /**
92      * Default constructor.
93      */

94     private Text() {
95     }
96
97     /**
98      * Returns the text representing the specified object.
99      * If the specified object is <code>null</code> this method
100      * returns {@link #NULL}.
101      *
102      * @param obj the object to represent as text.
103      * @return the textual representation of the specified object.
104      * @see Realtime#toText
105      */

106     public static Text valueOf(Object obj) {
107         if (obj instanceof String) {
108             return StringWrapper.newInstance((String) obj);
109         } else if (obj instanceof Realtime) {
110             return ((Realtime) obj).toText();
111         } else if (obj instanceof CharSequence) {
112             final CharSequence csq = (CharSequence) obj;
113             return Text.valueOf(csq, 0, csq.length());
114         } else if (obj != null) {
115             return StringWrapper.newInstance(obj.toString());
116         } else {
117             return NULL;
118         }
119     }
120
121     /**
122      * Returns the text that contains the specified subsequence of characters.
123      *
124      * @param csq the character sequence source.
125      * @param start the index of the first character.
126      * @param end the index after the last character.
127      * @return the corresponding text instance.
128      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0)
129      * || (start > end) || (end > csq.length())</code>
130      */

131     public static Text valueOf(CharSequence csq, int start, int end) {
132         if ((start < 0) || (end < 0) || (start > end) || (end > csq.length()))
133             throw new IndexOutOfBoundsException();
134         final int length = end - start;
135         if (length <= Primitive.BLOCK_SIZE) {
136             Primitive text = Primitive.newInstance(length);
137             for (int i = 0; i < length;) {
138                 text._data[i] = csq.charAt(start + i++);
139             }
140             return text;
141         } else {
142             final int middle = start + (length >> 1);
143             Composite text = Composite.newInstance(Text.valueOf(csq, start,
144                     middle), Text.valueOf(csq, middle, end));
145             return text;
146         }
147     }
148
149     /**
150      * Returns the text that contains the characters from the specified
151      * array.
152      *
153      * @param chars the array source of the characters.
154      * @return the corresponding instance.
155      */

156     public static Text valueOf(char[] chars) {
157         return valueOf(chars, 0, chars.length);
158     }
159
160     /**
161      * Returns the text that contains the characters from the specified
162      * subarray of characters.
163      *
164      * @param chars the source of the characters.
165      * @param offset the index of the first character in the data soure.
166      * @param length the length of the text returned.
167      * @return the corresponding instance.
168      * @throws IndexOutOfBoundsException if <code>(offset < 0) ||
169      * (length < 0) || ((offset + length) > chars.length)</code>
170      */

171     public static Text valueOf(char[] chars, int offset, int length) {
172         if ((offset < 0) || (length < 0) || ((offset + length) > chars.length))
173             throw new IndexOutOfBoundsException();
174         if (length <= Primitive.BLOCK_SIZE) {
175             Primitive text = Primitive.newInstance(length);
176             for (int i = 0; i < length;) {
177                 text._data[i] = chars[offset + i++];
178             }
179             return text;
180         } else {
181             final int middle = offset + (length >> 1);
182             Composite text = Composite.newInstance(Text.valueOf(chars, offset,
183                     middle - offset), Text.valueOf(chars, middle, offset
184                     + length - middle));
185             return text;
186         }
187     }
188
189     /**
190      * Returns the text representation of the <code>boolean</code> argument.
191      *
192      * @param b a <code>boolean</code>.
193      * @return if the argument is <code>true</code>, the text
194      * <code>"true"</code> is returned; otherwise, the text
195      * <code>"false"</code> is returned.
196      */

197     public static Text valueOf(boolean b) {
198         return b ? TRUE : FALSE;
199     }
200
201     private static final Text TRUE = Text.valueOf("true").intern();
202
203     private static final Text FALSE = Text.valueOf("false").intern();
204
205     /**
206      * Returns the {@link #intern unique} text instance corresponding to the
207      * specified character.
208      *
209      * @param c a character.
210      * @return a text of length <code>1</code> containing <code>'c'</code>.
211      */

212     public static Text valueOf(char c) {
213         if ((c < 128) && (ASCII[c] != null))
214             return ASCII[c];
215         Primitive text = Primitive.newInstance(1);
216         text._data[0] = c;
217         Text textIntern = text.intern();
218         if (c < 128) {
219             ASCII[c] = textIntern;
220         }
221         return textIntern;
222     }
223     private static final Text[] ASCII = new Text[128];
224
225     /**
226      * Returns the decimal representation of the specified <code>int</code>
227      * argument.
228      *
229      * @param i the <code>int</code> to format.
230      * @return the corresponding text instance.
231      */

232     public static Text valueOf(int i) {
233         try {
234             Primitive text = Primitive.newInstance(0);
235             TypeFormat.format(i, text);
236             return text;
237         } catch (IOException e) {
238             throw new JavolutionError(e);
239         }
240     }
241
242     /**
243      * Returns the radix representation of the specified <code>int</code>
244      * argument.
245      *
246      * @param i the <code>int</code> to format.
247      * @param radix the radix (e.g. <code>16</code> for hexadecimal).
248      * @return the corresponding text instance.
249      */

250     public static Text valueOf(int i, int radix) {
251         try {
252             Primitive text = Primitive.newInstance(0);
253             TypeFormat.format(i, radix, text);
254             return text;
255         } catch (IOException e) {
256             throw new JavolutionError(e);
257         }
258     }
259
260     /**
261      * Returns the decimal representation of the specified <code>long</code>
262      * argument.
263      *
264      * @param l the <code>long</code> to format.
265      * @return the corresponding text instance.
266      */

267     public static Text valueOf(long l) {
268         try {
269             Primitive text = Primitive.newInstance(0);
270             TypeFormat.format(l, text);
271             return text;
272         } catch (IOException e) {
273             throw new JavolutionError(e);
274         }
275     }
276
277     /**
278      * Returns the radix representation of the specified <code>long</code>
279      * argument.
280      *
281      * @param l the <code>long</code> to format.
282      * @param radix the radix (e.g. <code>16</code> for hexadecimal).
283      * @return the corresponding text instance.
284      */

285     public static Text valueOf(long l, int radix) {
286         try {
287             Primitive text = Primitive.newInstance(0);
288             TypeFormat.format(l, radix, text);
289             return text;
290         } catch (IOException e) {
291             throw new JavolutionError(e);
292         }
293     }
294
295     /**
296      * Returns the textual representation of the specified <code>float</code>
297      * instance.
298      *
299      * @param f the <code>float</code> to format.
300      * @return the corresponding text instance.
301      /*@FLOATING_POINT@
302     public static Text valueOf(float f) {
303         try {
304             Primitive text = Primitive.newInstance(0);
305             TypeFormat.format(f, text);
306             return text;
307         } catch (IOException e) {
308             throw new JavolutionError(e);
309         }
310     }
311      /**/

312
313     /**
314      * Returns the textual representation of the specified <code>double</code>
315      * argument.
316      *
317      * @param d the <code>double</code> to format.
318      * @return the corresponding text instance.
319      /*@FLOATING_POINT@
320     public static Text valueOf(double d) {
321         try {
322             Primitive text = Primitive.newInstance(0);
323             TypeFormat.format(d, text);
324             return text;
325         } catch (IOException e) {
326             throw new JavolutionError(e);
327         }
328     }
329      /**/

330
331     /**
332      * Returns the textual representation of the specified <code>double</code>
333      * value according to the specified formatting arguments.
334      *
335      * @param value the <code>double</code> value.
336      * @param digits the number of significative digits (excludes exponent).
337      * @param scientific <code>true</code> to forces the use of the scientific
338      * notation (e.g. <code>1.23E3</code>); <code>false</code>
339      * otherwise.
340      * @param showZero <code>true</code> if trailing fractional zeros are
341      * represented; <code>false</code> otherwise.
342      * @return the corresponding text instance.
343      * @throws IllegalArgumentException if <code>((digits > 19) ||
344      * (digits <= 0))</code>)
345      /*@FLOATING_POINT@
346     public static Text valueOf(double value, int digits,
347             boolean scientific, boolean showZero) {
348         try {
349             Primitive text = Primitive.newInstance(0);
350             TypeFormat.format(value, digits, scientific, showZero, text);
351             return text;
352         } catch (IOException e) {
353             throw new JavolutionError(e);
354         }
355     }
356     /**/

357
358     /**
359      * Returns the length of this text.
360      *
361      * @return the number of characters (16-bits Unicode) composing this text.
362      */

363     public final int length() {
364         return _count;
365     }
366
367     /**
368      * Concatenates the specified text to the end of this text.
369      * This method is extremely fast (faster even than
370      * <code>StringBuffer.append(String)</code>) and still returns
371      * a text instance with an internal binary tree of minimal depth!
372      *
373      * @param that the text that is concatenated.
374      * @return <code>this + that</code>
375      */

376     public final Text concat(Text that) {
377         if (this._count == 0) {
378             return that;
379         } else if (that._count == 0) {
380             return this;
381         } else if (((that._count << 1) < this._count)
382                 && (this instanceof Composite)) { // this too large, break up?
383
Composite thisComposite = (Composite) this;
384             if (thisComposite._head._count > thisComposite._tail._count) {
385                 return Composite.newInstance(thisComposite._head,
386                         thisComposite._tail.concat(that));
387             } else {
388                 return Composite.newInstance(this, that);
389             }
390         } else if (((this._count << 1) < that._count)
391                 && (that instanceof Composite)) { // that too large, break up?
392
Composite thatComposite = (Composite) that;
393             if (thatComposite._head._count < thatComposite._tail._count) {
394                 return Composite.newInstance(this.concat(thatComposite._head),
395                         thatComposite._tail);
396             } else {
397                 return Composite.newInstance(this, that);
398             }
399         } else { //
400
return Composite.newInstance(this, that);
401         }
402     }
403
404     /**
405      * Returns a portion of this text.
406      *
407      * @param start the index of the first character inclusive.
408      * @return the sub-text starting at the specified position.
409      * @throws IndexOutOfBoundsException if <code>(start < 0) ||
410      * (start > this.length())</code>
411      */

412     public final Text subtext(int start) {
413         return subtext(start, length());
414     }
415
416     /**
417      * Returns the text having the specified text inserted at
418      * the specified location.
419      *
420      * @param index the insertion position.
421      * @param txt the text being inserted.
422      * @return <code>subtext(0, index).concat(txt).concat(subtext(index))</code>
423      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
424      * (index > this.length())</code>
425      */

426     public final Text insert(int index, Text txt) {
427         // Concatenates the smallest part first.
428
return ((index << 1) < _count) ? this.subtext(0, index).concat(txt)
429                 .concat(this.subtext(index)) : this.subtext(0, index).concat(
430                 txt.concat(this.subtext(index)));
431     }
432
433     /**
434      * Returns the text without the characters between the specified indexes.
435      *
436      * @param start the beginning index, inclusive.
437      * @param end the ending index, exclusive.
438      * @return <code>subtext(0, start).concat(subtext(end))</code>
439      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
440      * (start > end) || (end > this.length()</code>
441      */

442     public final Text delete(int start, int end) {
443         return this.subtext(0, start).concat(this.subtext(end));
444     }
445
446     /**
447      * Replaces each character sequence of this text that matches the specified
448      * target sequence with the specified replacement sequence.
449      *
450      * @param target the text to be replaced.
451      * @param replacement the replacement text.
452      * @return the resulting text.
453      */

454     public final Text replace(Text target, Text replacement) {
455         int i = indexOf(target);
456         return (i < 0) ? this : // No target sequence found.
457
this.subtext(0, i).concat(replacement).concat(
458                         this.subtext(i + target.length()).replace(target,
459                                 replacement));
460     }
461
462     /**
463      * Returns {@link #subtext(int, int) subtext(start, end)}.
464      *
465      * @param start the index of the first character inclusive.
466      * @param end the index of the last character exclusive.
467      * @return <code>this.subtext(start, end)</code>
468      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
469      * (start > end) || (end > this.length())</code>
470      */

471     public final CharSequence subSequence(int start, int end) {
472         return this.subtext(start, end);
473     }
474
475     /**
476      * Returns the index within this text of the first occurrence
477      * of the specified character sequence searching forward.
478      *
479      * @param csq a character sequence.
480      * @return the index of the first character of the character sequence found;
481      * or <code>-1</code> if the character sequence is not found.
482      */

483     public final int indexOf(CharSequence csq) {
484         return indexOf(csq, 0);
485     }
486
487     /**
488      * Returns the index within this text of the first occurrence
489      * of the specified characters sequence searching forward from
490      * the specified index.
491      *
492      * @param csq a character sequence.
493      * @param fromIndex the index to start the search from.
494      * @return the index in the range
495      * <code>[fromIndex, length() - csq.length()]</code>
496      * or <code>-1</code> if the character sequence is not found.
497      */

498     public final int indexOf(CharSequence csq, int fromIndex) {
499
500         // Limit cases.
501
final int csqLength = csq.length();
502         final int min = Math.max(0, fromIndex);
503         final int max = _count - csqLength;
504         if (csqLength == 0) {
505             return (min > max) ? -1 : min;
506         }
507
508         // Searches for csq.
509
final char c = csq.charAt(0);
510         for (int i = indexOf(c, min); (i >= 0) && (i <= max); i = indexOf(c,
511                 ++i)) {
512             boolean match = true;
513             for (int j = 1; j < csqLength; j++) {
514                 if (this.charAt(i + j) != csq.charAt(j)) {
515                     match = false;
516                     break;
517                 }
518             }
519             if (match) {
520                 return i;
521             }
522         }
523         return -1;
524     }
525
526     /**
527      * Returns the index within this text of the last occurrence of
528      * the specified characters sequence searching backward.
529      *
530      * @param csq a character sequence.
531      * @return the index of the first character of the character sequence found;
532      * or <code>-1</code> if the character sequence is not found.
533      */

534     public final int lastIndexOf(CharSequence csq) {
535         return lastIndexOf(csq, _count);
536     }
537
538     /**
539      * Returns the index within this text of the last occurrence of
540      * the specified character sequence searching backward from the specified
541      * index.
542      *
543      * @param csq a character sequence.
544      * @param fromIndex the index to start the backward search from.
545      * @return the index in the range <code>[0, fromIndex]</code> or
546      * <code>-1</code> if the character sequence is not found.
547      */

548     public final int lastIndexOf(CharSequence csq, int fromIndex) {
549
550         // Limit cases.
551
final int csqLength = csq.length();
552         final int min = 0;
553         final int max = Math.min(fromIndex, _count - csqLength);
554         if (csqLength == 0) {
555             return (min > max) ? -1 : max;
556         }
557
558         // Searches for csq.
559
final char c = csq.charAt(0);
560         for (int i = lastIndexOf(c, max); (i >= 0); i = lastIndexOf(c, --i)) {
561             boolean match = true;
562             for (int j = 1; j < csqLength; j++) {
563                 if (this.charAt(i + j) != csq.charAt(j)) {
564                     match = false;
565                     break;
566                 }
567             }
568             if (match) {
569                 return i;
570             }
571         }
572         return -1;
573
574     }
575
576     /**
577      * Indicates if this text starts with the specified prefix.
578      *
579      * @param prefix the prefix.
580      * @return <code>true</code> if the character sequence represented by the
581      * argument is a prefix of the character sequence represented by
582      * this text; <code>false</code> otherwise.
583      */

584     public final boolean startsWith(CharSequence prefix) {
585         return startsWith(prefix, 0);
586     }
587
588     /**
589      * Indicates if this text ends with the specified suffix.
590      *
591      * @param suffix the suffix.
592      * @return <code>true</code> if the character sequence represented by the
593      * argument is a suffix of the character sequence represented by
594      * this text; <code>false</code> otherwise.
595      */

596     public final boolean endsWith(CharSequence suffix) {
597         return startsWith(suffix, length() - suffix.length());
598     }
599
600     /**
601      * Indicates if this text starts with the specified prefix
602      * at the specified index.
603      *
604      * @param prefix the prefix.
605      * @param index the index of the prefix location in this string.
606      * @return <code>this.substring(index).startsWith(prefix)</code>
607      */

608     public final boolean startsWith(CharSequence prefix, int index) {
609         final int prefixLength = prefix.length();
610         if ((index >= 0) && (index <= (this.length() - prefixLength))) {
611             for (int i = 0, j = index; i < prefixLength;) {
612                 if (prefix.charAt(i++) != this.charAt(j++)) {
613                     return false;
614                 }
615             }
616             return true;
617         } else {
618             return false;
619         }
620     }
621
622     /**
623      * Returns a copy of this text, with leading and trailing
624      * whitespace omitted.
625      *
626      * @return a copy of this text with leading and trailing white
627      * space removed, or this text if it has no leading or
628      * trailing white space.
629      */

630     public final Text trim() {
631         int first = 0; // First character index.
632
int last = length() - 1; // Last character index.
633
while ((first <= last) && (charAt(first) <= ' ')) {
634             first++;
635         }
636         while ((last >= first) && (charAt(last) <= ' ')) {
637             last--;
638         }
639         return subtext(first, last + 1);
640     }
641
642     /**
643      * Returns a text equals to this text from a pool of unique text
644      * instances.
645      * For any two text t1 and t2, <code>(t1.intern() == t2.intern())</code>
646      * if and only if <code>(t1.equals(t2))</code>.
647      *
648      * @return an unique instance allocated on the heap and equals to this text.
649      */

650     public final Text intern() {
651         Text text = (Text) INTERN_TEXT.get(this); // FastMap supports concurrency.
652
if (text == null) {
653             synchronized (INTERN_TEXT) {
654                 text = (Text) INTERN_TEXT.get(this); // Ensures unicity.
655
if (text == null) {
656                     text = this;
657                     text.moveHeap();
658                     INTERN_TEXT.put(text, text);
659                 }
660             }
661         }
662         return text;
663     }
664
665     /**
666      * Indicates if this text has the same character content as the specified
667      * character sequence.
668      *
669      * @param csq the character sequence to compare with.
670      * @return <code>true</code> if the specified character sequence has the
671      * same character content as this text; <code>false</code> otherwise.
672      */

673     public final boolean contentEquals(CharSequence csq) {
674         if (csq.length() != _count)
675             return false;
676         for (int i = 0; i < _count;) {
677             if (this.charAt(i) != csq.charAt(i++))
678                 return false;
679         }
680         return true;
681     }
682
683     /**
684      * Indicates if this text has the same character contend as the specified
685      * character sequence ignoring case considerations.
686      *
687      * @param csq the <code>CharSequence</code> to compare this text against.
688      * @return <code>true</code> if the argument and this text are equal,
689      * ignoring case; <code>false</code> otherwise.
690      */

691     public final boolean contentEqualsIgnoreCase(CharSequence csq) {
692         if (this._count != csq.length())
693             return false;
694         for (int i = 0; i < _count;) {
695             char u1 = this.charAt(i);
696             char u2 = csq.charAt(i++);
697             if (u1 != u2) {
698                 u1 = Character.toUpperCase(u1);
699                 u2 = Character.toUpperCase(u2);
700                 if ((u1 != u2)
701                         && (Character.toLowerCase(u1) != Character
702                                 .toLowerCase(u2)))
703                     return false;
704
705             }
706         }
707         return true;
708     }
709
710     /**
711      * Compares this text against the specified object for equality.
712      * Returns <code>true</code> if the specified object is a text having
713      * the same character sequence as this text.
714      * For generic comparaison with any character sequence the
715      * {@link #contentEquals(CharSequence)} should be used.
716      *
717      * @param obj the object to compare with or <code>null</code>.
718      * @return <code>true</code> if that is a text with the same character
719      * sequence as this text; <code>false</code> otherwise.
720      */

721     public final boolean equals(Object obj) {
722         if (this == obj)
723             return true;
724         if (!(obj instanceof Text))
725             return false;
726         final Text that = (Text) obj;
727         if (this._count != that._count)
728             return false;
729         for (int i = 0; i < _count;) {
730             if (this.charAt(i) != that.charAt(i++))
731                 return false;
732         }
733         return true;
734     }
735
736     /**
737      * Returns the hash code for this text.
738      *
739      * @return the hash code value.
740      */

741     public final int hashCode() {
742         if (_hashCode != 0)
743             return _hashCode;
744         int h = _hashCode;
745         final int length = this.length();
746         for (int i = 0; i < length;) {
747             h = 31 * h + charAt(i++);
748         }
749         return _hashCode = h;
750     }
751
752     /**
753      * Compares this text to another character sequence or string
754      * lexicographically.
755      *
756      * @param csq the character sequence to be compared.
757      * @return <code>TypeFormat.LEXICAL_COMPARATOR.compare(this, csq)</code>
758      * @throws ClassCastException if the specifed object is not a
759      * <code>CharSequence</code> or a <code>String</code>.
760      */

761     public final int compareTo(Object csq) {
762         return FastComparator.LEXICAL.compare(this, csq);
763     }
764
765     /**
766      * Returns <code>this</code> (implements
767      * {@link javolution.realtime.Realtime Realtime} interface).
768      *
769      * @return <code>this</code>
770      */

771     public final Text toText() {
772         return this;
773     }
774
775     /**
776      * Returns a compact copy of this text allocated on the stack when
777      * executing in a {@link javolution.realtime.PoolContext PoolContext}.
778      *
779      * @return a local compact copy of this text.
780      */

781     public final Text copy() {
782         return Text.valueOf(this, 0, _count);
783     }
784
785     /**
786      * Returns the depth of the internal tree used to represent this text.
787      *
788      * @return the maximum depth of the text internal binary tree.
789      */

790     public abstract int depth();
791
792     /**
793      * Returns the character at the specified index.
794      *
795      * @param index the index of the character.
796      * @return the character at the specified index.
797      * @throws IndexOutOfBoundsException if <code>(index < 0) ||
798      * (index >= this.length())</code>
799      */

800     public abstract char charAt(int index);
801
802     /**
803      * Returns the index within this text of the first occurrence of the
804      * specified character, starting the search at the specified index.
805      *
806      * @param c the character to search for.
807      * @param fromIndex the index to start the search from.
808      * @return the index of the first occurrence of the character in this text
809      * that is greater than or equal to <code>fromIndex</code>,
810      * or <code>-1</code> if the character does not occur.
811      */

812     public abstract int indexOf(char c, int fromIndex);
813
814     /**
815      * Returns the index within this text of the first occurrence of the
816      * specified character, searching backward and starting at the specified
817      * index.
818      *
819      * @param c the character to search for.
820      * @param fromIndex the index to start the search backward from.
821      * @return the index of the first occurrence of the character in this text
822      * that is less than or equal to <code>fromIndex</code>,
823      * or <code>-1</code> if the character does not occur.
824      */

825     public abstract int lastIndexOf(char c, int fromIndex);
826
827     /**
828      * Returns a portion of this text.
829      *
830      * @param start the index of the first character inclusive.
831      * @param end the index of the last character exclusive.
832      * @return the sub-text starting at the specified start position and
833      * ending just before the specified end position.
834      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
835      * (start > end) || (end > this.length())</code>
836      */

837     public abstract Text subtext(int start, int end);
838
839     /**
840      * Copies the characters from this text into the destination
841      * character array.
842      *
843      * @param start the index of the first character to copy.
844      * @param end the index after the last character to copy.
845      * @param dest the destination array.
846      * @param destPos the start offset in the destination array.
847      * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
848      * (start > end) || (end > this.length())</code>
849      */

850     public abstract void getChars(int start, int end, char dest[], int destPos);
851
852     /**
853      * Converts the characters of this text to lower case.
854      *
855      * @return the text in lower case.
856      * @see Character#toLowerCase(char)
857      */

858     public abstract Text toLowerCase();
859
860     /**
861      * Converts the characters of this text to upper case.
862      *
863      * @return the text in lower case.
864      * @see Character#toUpperCase(char)
865      */

866     public abstract Text toUpperCase();
867
868     /**
869      * Returns the <code>String</code> value corresponding to this text.
870      *
871      * @return the <code>java.lang.String</code> for this text.
872      */

873     public abstract String stringValue();
874
875     /**
876      * This class represents a text block (up to 32 characters).
877      */

878     private static final class Primitive extends Text implements Appendable {
879
880         /**
881          * Holds the default size for primitive blocks of characters.
882          */

883         private static final int BLOCK_SIZE = 32;
884
885         /**
886          * Holds the associated factory.
887          */

888         private static final Factory FACTORY = new Factory() {
889
890             public Object create() {
891                 return new Primitive();
892             }
893         };
894
895         /**
896          * Holds the raw data (primitive).
897          */

898         private final char[] _data = new char[BLOCK_SIZE];
899
900         /**
901          * Default constructor.
902          */

903         private Primitive() {
904         }
905
906         /**
907          * Returns a new/recycled primitive text of specified length.
908          *
909          * @param the length of this primitive text.
910          */

911         private static Primitive newInstance(int length) {
912             Primitive text = (Primitive) FACTORY.object();
913             text._count = length;
914             text._hashCode = 0;
915             return text;
916         }
917
918         // Implements abstract method.
919
public int depth() {
920             return 0;
921         }
922
923         // Implements abstract method.
924
public char charAt(int index) {
925             if (index >= _count)
926                 throw new IndexOutOfBoundsException();
927             return _data[index];
928         }
929
930         // Implements abstract method.
931
public int indexOf(char c, int fromIndex) {
932             for (int i = Math.max(fromIndex, 0); i < _count; i++) {
933                 if (_data[i] == c)
934                     return i;
935             }
936             return -1;
937         }
938
939         // Implements abstract method.
940
public int lastIndexOf(char c, int fromIndex) {
941             for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) {
942                 if (_data[i] == c)
943                     return i;
944             }
945             return -1;
946         }
947
948         // Implements abstract method.
949
public Text subtext(int start, int end) {
950             if ((start == 0) && (end == _count))
951                 return this;
952             if ((start < 0) || (start > end) || (end > _count))
953                 throw new IndexOutOfBoundsException();
954             if (start == end)
955                 return Text.EMPTY;
956             Primitive text = Primitive.newInstance(end - start);
957             for (int i = start, j = 0; i < end;) {
958                 text._data[j++] = _data[i++];
959             }
960             return text;
961         }
962
963         // Implements abstract method.
964
public void getChars(int start, int end, char dest[], int destPos) {
965             if ((end > _count) || (end < start))
966                 throw new IndexOutOfBoundsException();
967             for (int i = start, j = destPos; i < end;) {
968                 dest[j++] = _data[i++];
969             }
970         }
971
972         // Implements abstract method.
973
public Text toLowerCase() {
974             Primitive text = newInstance(_count);
975             for (int i = 0; i < _count;) {
976                 text._data[i] = Character.toLowerCase(_data[i++]);
977             }
978             return text;
979         }
980
981         // Implements abstract method.
982
public Text toUpperCase() {
983             Primitive text = newInstance(_count);
984             for (int i = 0; i < _count;) {
985                 text._data[i] = Character.toUpperCase(_data[i++]);
986             }
987             return text;
988         }
989
990         // Implements abstract method.
991
public String stringValue() {
992             return new String(_data, 0, _count);
993         }
994
995         // Implements appendable (for primitive types formatting).
996
public Appendable append(char c) throws IOException {
997             _data[_count++] = c;
998             return this;
999         }
1000
1001        // Implements appendable (for primitive types formatting).
1002
public Appendable append(CharSequence csq) throws IOException {
1003            return append(csq, 0, csq.length());
1004        }
1005
1006        // Implements appendable (for primitive types formatting).
1007
public Appendable append(CharSequence csq, int start, int end)
1008                throws IOException {
1009            for (int i = start; i < end;) {
1010                _data[_count++] = csq.charAt(i++);
1011            }
1012            return this;
1013        }
1014
1015    }
1016
1017    /**
1018     * This class represents a text composite.
1019     */

1020    private static final class Composite extends Text {
1021
1022        /**
1023         * Holds the associtate factory.
1024         */

1025        private static final Factory FACTORY = new Factory() {
1026
1027            public Object create() {
1028                return new Composite();
1029            }
1030        };
1031
1032        /**
1033         * Holds the head block of character (composite).
1034         */

1035        private Text _head;
1036
1037        /**
1038         * Holds the tail block of character (composite).
1039         */

1040        private Text _tail;
1041
1042        /**
1043         * Default constructor.
1044         */

1045        private Composite() {
1046        }
1047
1048        /**
1049         * Returns a new/recycled composite text.
1050         *
1051         * @param head the head of this composite.
1052         * @param tail the tail of this composite.
1053         * @return the corresponding composite text.
1054         */

1055        private static Composite newInstance(Text head, Text tail) {
1056            Composite text = (Composite) FACTORY.object();
1057            text._hashCode = 0;
1058            text._count = head._count + tail._count;
1059            text._head = head;
1060            text._tail = tail;
1061            return text;
1062        }
1063
1064        // Implements abstract method.
1065
public int depth() {
1066            return Math.max(_head.depth(), _tail.depth()) + 1;
1067        }
1068
1069        // Implements abstract method.
1070
public char charAt(int index) {
1071            return (index < _head._count) ? _head.charAt(index) : _tail
1072                    .charAt(index - _head._count);
1073        }
1074
1075        // Implements abstract method.
1076
public int indexOf(char c, int fromIndex) {
1077            final int cesure = _head._count;
1078            if (fromIndex < cesure) {
1079                final int headIndex = _head.indexOf(c, fromIndex);
1080                if (headIndex >= 0)
1081                    return headIndex; // Found in head.
1082
}
1083            final int tailIndex = _tail.indexOf(c, fromIndex - cesure);
1084            return (tailIndex >= 0) ? tailIndex + cesure : -1;
1085        }
1086
1087        // Implements abstract method.
1088
public int lastIndexOf(char c, int fromIndex) {
1089            final int cesure = _head._count;
1090            if (fromIndex >= cesure) {
1091                final int tailIndex = _tail.lastIndexOf(c, fromIndex - cesure);
1092                if (tailIndex >= 0)
1093                    return tailIndex + cesure; // Found in tail.
1094
}
1095            return _head.lastIndexOf(c, fromIndex);
1096        }
1097
1098        // Implements abstract method.
1099
public Text subtext(int start, int end) {
1100            final int cesure = _head._count;
1101            if (end <= cesure)
1102                return _head.subtext(start, end);
1103            if (start >= cesure)
1104                return _tail.subtext(start - cesure, end - cesure);
1105            if ((start == 0) && (end == _count))
1106                return this;
1107            // Overlaps head and tail.
1108
return _head.subtext(start, cesure).concat(
1109                    _tail.subtext(0, end - cesure));
1110        }
1111
1112        // Implements abstract method.
1113
public void getChars(int start, int end, char dest[], int destPos) {
1114            final int cesure = _head._count;
1115            if (end <= cesure) {
1116                _head.getChars(start, end, dest, destPos);
1117            } else if (start >= cesure) {
1118                _tail.getChars(start - cesure, end - cesure, dest, destPos);
1119            } else { // Overlaps head and tail.
1120
_head.getChars(start, cesure, dest, destPos);
1121                _tail.getChars(0, end - cesure, dest, destPos + cesure - start);
1122            }
1123        }
1124
1125        // Implements abstract method.
1126
public Text toLowerCase() {
1127            return newInstance(_head.toLowerCase(), _tail.toLowerCase());
1128        }
1129
1130        // Implements abstract method.
1131
public Text toUpperCase() {
1132            return newInstance(_head.toUpperCase(), _tail.toUpperCase());
1133        }
1134
1135        // Implements abstract method.
1136
public String stringValue() {
1137            char[] data = new char[_count];
1138            this.getChars(0, _count, data, 0);
1139            return new String(data, 0, _count);
1140        }
1141
1142        // Overrides.
1143
public boolean move(ObjectSpace os) {
1144            if (super.move(os)) {
1145                _head.move(os);
1146                _tail.move(os);
1147                return true;
1148            }
1149            return false;
1150        }
1151    }
1152
1153    /**
1154     * This class represents a String Wrapper.
1155     */

1156    private static final class StringWrapper extends Text {
1157
1158        /**
1159         * Holds the associated factory.
1160         */

1161        private static final Factory FACTORY = new Factory() {
1162
1163            public Object create() {
1164                return new StringWrapper();
1165            }
1166        };
1167
1168        /**
1169         * Holds the string.
1170         */

1171        private String _string;
1172
1173        /**
1174         * Holds the offset.
1175         */

1176        private int _offset;
1177
1178        /**
1179         * Default constructor.
1180         */

1181        private StringWrapper() {
1182        }
1183
1184        /**
1185         * Returns a new/recycled text wrapping the specified string.
1186         *
1187         * @param str the string to wrap.
1188         * @return the corresponding text instance.
1189         */

1190        private static StringWrapper newInstance(String str) {
1191            StringWrapper text = (StringWrapper) FACTORY.object();
1192            text._count = str.length();
1193            text._hashCode = 0;
1194            text._string = str;
1195            text._offset = 0;
1196            return text;
1197        }
1198
1199        // Implements abstract method.
1200
public int depth() {
1201            return 0;
1202        }
1203
1204        // Implements abstract method.
1205
public char charAt(int index) {
1206            if ((index >= _count) || (index < 0))
1207                throw new IndexOutOfBoundsException();
1208            return _string.charAt(_offset + index);
1209        }
1210
1211        // Implements abstract method.
1212
public int indexOf(char c, int fromIndex) {
1213            for (int i = Math.max(fromIndex, 0); i < _count; i++) {
1214                if (_string.charAt(_offset + i) == c)
1215                    return i;
1216            }
1217            return -1;
1218        }
1219
1220        // Implements abstract method.
1221
public int lastIndexOf(char c, int fromIndex) {
1222            for (int i = Math.min(fromIndex, _count - 1); i >= 0; i--) {
1223                if (_string.charAt(_offset + i) == c)
1224                    return i;
1225            }
1226            return -1;
1227        }
1228
1229        // Implements abstract method.
1230
public Text subtext(int start, int end) {
1231            if ((start == 0) && (end == _count))
1232                return this;
1233            if ((start < 0) || (start > end) || (end > _count))
1234                throw new IndexOutOfBoundsException();
1235            if (start == end)
1236                return Text.EMPTY;
1237            StringWrapper text = (StringWrapper) FACTORY.object();
1238            text._count = end - start;
1239            text._hashCode = 0;
1240            text._string = _string;
1241            text._offset = _offset + start;
1242            return text;
1243        }
1244
1245        // Implements abstract method.
1246
public void getChars(int start, int end, char dest[], int destPos) {
1247            if ((end > _count) || (end < start) || (start < 0))
1248                throw new IndexOutOfBoundsException();
1249            _string.getChars(start + _offset, end + _offset, dest, destPos);
1250        }
1251
1252        // Implements abstract method.
1253
public Text toLowerCase() {
1254            return copy().toLowerCase(); // To avoid dynamic heap allocation.
1255
}
1256
1257        // Implements abstract method.
1258
public Text toUpperCase() {
1259            return copy().toUpperCase(); // To avoid dynamic heap allocation.
1260
}
1261
1262        // Implements abstract method.
1263
public String stringValue() {
1264            if ((_offset == 0) && (_count == _string.length()))
1265                return _string;
1266            return _string.substring(_offset, _offset + _count);
1267        }
1268
1269    }
1270}
Popular Tags