• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.text;
18 
19 import com.android.internal.util.ArrayUtils;
20 import android.graphics.Paint;
21 import android.graphics.Canvas;
22 
23 import java.lang.reflect.Array;
24 
25 /**
26  * This is the class for text whose content and markup can both be changed.
27  */
28 public class SpannableStringBuilder
29 implements CharSequence, GetChars, Spannable, Editable, Appendable,
30            GraphicsOperations
31 {
32     /**
33      * Create a new SpannableStringBuilder with empty contents
34      */
SpannableStringBuilder()35     public SpannableStringBuilder() {
36         this("");
37     }
38 
39     /**
40      * Create a new SpannableStringBuilder containing a copy of the
41      * specified text, including its spans if any.
42      */
SpannableStringBuilder(CharSequence text)43     public SpannableStringBuilder(CharSequence text) {
44         this(text, 0, text.length());
45     }
46 
47     /**
48      * Create a new SpannableStringBuilder containing a copy of the
49      * specified slice of the specified text, including its spans if any.
50      */
SpannableStringBuilder(CharSequence text, int start, int end)51     public SpannableStringBuilder(CharSequence text, int start, int end) {
52         int srclen = end - start;
53 
54         int len = ArrayUtils.idealCharArraySize(srclen + 1);
55         mText = new char[len];
56         mGapStart = srclen;
57         mGapLength = len - srclen;
58 
59         TextUtils.getChars(text, start, end, mText, 0);
60 
61         mSpanCount = 0;
62         int alloc = ArrayUtils.idealIntArraySize(0);
63         mSpans = new Object[alloc];
64         mSpanStarts = new int[alloc];
65         mSpanEnds = new int[alloc];
66         mSpanFlags = new int[alloc];
67 
68         if (text instanceof Spanned) {
69             Spanned sp = (Spanned) text;
70             Object[] spans = sp.getSpans(start, end, Object.class);
71 
72             for (int i = 0; i < spans.length; i++) {
73                 if (spans[i] instanceof NoCopySpan) {
74                     continue;
75                 }
76 
77                 int st = sp.getSpanStart(spans[i]) - start;
78                 int en = sp.getSpanEnd(spans[i]) - start;
79                 int fl = sp.getSpanFlags(spans[i]);
80 
81                 if (st < 0)
82                     st = 0;
83                 if (st > end - start)
84                     st = end - start;
85 
86                 if (en < 0)
87                     en = 0;
88                 if (en > end - start)
89                     en = end - start;
90 
91                 setSpan(spans[i], st, en, fl);
92             }
93         }
94     }
95 
valueOf(CharSequence source)96     public static SpannableStringBuilder valueOf(CharSequence source) {
97         if (source instanceof SpannableStringBuilder) {
98             return (SpannableStringBuilder) source;
99         } else {
100             return new SpannableStringBuilder(source);
101         }
102     }
103 
104     /**
105      * Return the char at the specified offset within the buffer.
106      */
charAt(int where)107     public char charAt(int where) {
108         int len = length();
109         if (where < 0) {
110             throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
111         } else if (where >= len) {
112             throw new IndexOutOfBoundsException("charAt: " + where +
113                                                 " >= length " + len);
114         }
115 
116         if (where >= mGapStart)
117             return mText[where + mGapLength];
118         else
119             return mText[where];
120     }
121 
122     /**
123      * Return the number of chars in the buffer.
124      */
length()125     public int length() {
126         return mText.length - mGapLength;
127     }
128 
resizeFor(int size)129     private void resizeFor(int size) {
130         int newlen = ArrayUtils.idealCharArraySize(size + 1);
131         char[] newtext = new char[newlen];
132 
133         int after = mText.length - (mGapStart + mGapLength);
134 
135         System.arraycopy(mText, 0, newtext, 0, mGapStart);
136         System.arraycopy(mText, mText.length - after,
137                          newtext, newlen - after, after);
138 
139         for (int i = 0; i < mSpanCount; i++) {
140             if (mSpanStarts[i] > mGapStart)
141                 mSpanStarts[i] += newlen - mText.length;
142             if (mSpanEnds[i] > mGapStart)
143                 mSpanEnds[i] += newlen - mText.length;
144         }
145 
146         int oldlen = mText.length;
147         mText = newtext;
148         mGapLength += mText.length - oldlen;
149 
150         if (mGapLength < 1)
151             new Exception("mGapLength < 1").printStackTrace();
152     }
153 
moveGapTo(int where)154     private void moveGapTo(int where) {
155         if (where == mGapStart)
156             return;
157 
158         boolean atend = (where == length());
159 
160         if (where < mGapStart) {
161             int overlap = mGapStart - where;
162 
163             System.arraycopy(mText, where,
164                              mText, mGapStart + mGapLength - overlap, overlap);
165         } else /* where > mGapStart */ {
166             int overlap = where - mGapStart;
167 
168             System.arraycopy(mText, where + mGapLength - overlap,
169                              mText, mGapStart, overlap);
170         }
171 
172         // XXX be more clever
173         for (int i = 0; i < mSpanCount; i++) {
174             int start = mSpanStarts[i];
175             int end = mSpanEnds[i];
176 
177             if (start > mGapStart)
178                 start -= mGapLength;
179             if (start > where)
180                 start += mGapLength;
181             else if (start == where) {
182                 int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
183 
184                 if (flag == POINT || (atend && flag == PARAGRAPH))
185                     start += mGapLength;
186             }
187 
188             if (end > mGapStart)
189                 end -= mGapLength;
190             if (end > where)
191                 end += mGapLength;
192             else if (end == where) {
193                 int flag = (mSpanFlags[i] & END_MASK);
194 
195                 if (flag == POINT || (atend && flag == PARAGRAPH))
196                     end += mGapLength;
197             }
198 
199             mSpanStarts[i] = start;
200             mSpanEnds[i] = end;
201         }
202 
203         mGapStart = where;
204     }
205 
206     // Documentation from interface
insert(int where, CharSequence tb, int start, int end)207     public SpannableStringBuilder insert(int where, CharSequence tb, int start, int end) {
208         return replace(where, where, tb, start, end);
209     }
210 
211     // Documentation from interface
insert(int where, CharSequence tb)212     public SpannableStringBuilder insert(int where, CharSequence tb) {
213         return replace(where, where, tb, 0, tb.length());
214     }
215 
216     // Documentation from interface
delete(int start, int end)217     public SpannableStringBuilder delete(int start, int end) {
218         SpannableStringBuilder ret = replace(start, end, "", 0, 0);
219 
220         if (mGapLength > 2 * length())
221             resizeFor(length());
222 
223         return ret; // == this
224     }
225 
226     // Documentation from interface
clear()227     public void clear() {
228         replace(0, length(), "", 0, 0);
229     }
230 
231     // Documentation from interface
clearSpans()232     public void clearSpans() {
233         for (int i = mSpanCount - 1; i >= 0; i--) {
234             Object what = mSpans[i];
235             int ostart = mSpanStarts[i];
236             int oend = mSpanEnds[i];
237 
238             if (ostart > mGapStart)
239                 ostart -= mGapLength;
240             if (oend > mGapStart)
241                 oend -= mGapLength;
242 
243             mSpanCount = i;
244             mSpans[i] = null;
245 
246             sendSpanRemoved(what, ostart, oend);
247         }
248     }
249 
250     // Documentation from interface
append(CharSequence text)251     public SpannableStringBuilder append(CharSequence text) {
252         int length = length();
253         return replace(length, length, text, 0, text.length());
254     }
255 
256     // Documentation from interface
append(CharSequence text, int start, int end)257     public SpannableStringBuilder append(CharSequence text, int start, int end) {
258         int length = length();
259         return replace(length, length, text, start, end);
260     }
261 
262     // Documentation from interface
append(char text)263     public SpannableStringBuilder append(char text) {
264         return append(String.valueOf(text));
265     }
266 
change(int start, int end, CharSequence tb, int tbstart, int tbend)267     private int change(int start, int end,
268                        CharSequence tb, int tbstart, int tbend) {
269         return change(true, start, end, tb, tbstart, tbend);
270     }
271 
change(boolean notify, int start, int end, CharSequence tb, int tbstart, int tbend)272     private int change(boolean notify, int start, int end,
273                        CharSequence tb, int tbstart, int tbend) {
274         checkRange("replace", start, end);
275         int ret = tbend - tbstart;
276         TextWatcher[] recipients = null;
277 
278         if (notify)
279             recipients = sendTextWillChange(start, end - start,
280                                             tbend - tbstart);
281 
282         for (int i = mSpanCount - 1; i >= 0; i--) {
283             if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
284                 int st = mSpanStarts[i];
285                 if (st > mGapStart)
286                     st -= mGapLength;
287 
288                 int en = mSpanEnds[i];
289                 if (en > mGapStart)
290                     en -= mGapLength;
291 
292                 int ost = st;
293                 int oen = en;
294                 int clen = length();
295 
296                 if (st > start && st <= end) {
297                     for (st = end; st < clen; st++)
298                         if (st > end && charAt(st - 1) == '\n')
299                             break;
300                 }
301 
302                 if (en > start && en <= end) {
303                     for (en = end; en < clen; en++)
304                         if (en > end && charAt(en - 1) == '\n')
305                             break;
306                 }
307 
308                 if (st != ost || en != oen)
309                     setSpan(mSpans[i], st, en, mSpanFlags[i]);
310             }
311         }
312 
313         moveGapTo(end);
314 
315         if (tbend - tbstart >= mGapLength + (end - start))
316             resizeFor(mText.length - mGapLength +
317                       tbend - tbstart - (end - start));
318 
319         mGapStart += tbend - tbstart - (end - start);
320         mGapLength -= tbend - tbstart - (end - start);
321 
322         if (mGapLength < 1)
323             new Exception("mGapLength < 1").printStackTrace();
324 
325         TextUtils.getChars(tb, tbstart, tbend, mText, start);
326 
327         if (tb instanceof Spanned) {
328             Spanned sp = (Spanned) tb;
329             Object[] spans = sp.getSpans(tbstart, tbend, Object.class);
330 
331             for (int i = 0; i < spans.length; i++) {
332                 int st = sp.getSpanStart(spans[i]);
333                 int en = sp.getSpanEnd(spans[i]);
334 
335                 if (st < tbstart)
336                     st = tbstart;
337                 if (en > tbend)
338                     en = tbend;
339 
340                 if (getSpanStart(spans[i]) < 0) {
341                     setSpan(false, spans[i],
342                             st - tbstart + start,
343                             en - tbstart + start,
344                             sp.getSpanFlags(spans[i]));
345                 }
346             }
347         }
348 
349         // no need for span fixup on pure insertion
350         if (tbend > tbstart && end - start == 0) {
351             if (notify) {
352                 sendTextChange(recipients, start, end - start, tbend - tbstart);
353                 sendTextHasChanged(recipients);
354             }
355 
356             return ret;
357         }
358 
359         boolean atend = (mGapStart + mGapLength == mText.length);
360 
361         for (int i = mSpanCount - 1; i >= 0; i--) {
362             if (mSpanStarts[i] >= start &&
363                 mSpanStarts[i] < mGapStart + mGapLength) {
364                 int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
365 
366                 if (flag == POINT || (flag == PARAGRAPH && atend))
367                     mSpanStarts[i] = mGapStart + mGapLength;
368                 else
369                     mSpanStarts[i] = start;
370             }
371 
372             if (mSpanEnds[i] >= start &&
373                 mSpanEnds[i] < mGapStart + mGapLength) {
374                 int flag = (mSpanFlags[i] & END_MASK);
375 
376                 if (flag == POINT || (flag == PARAGRAPH && atend))
377                     mSpanEnds[i] = mGapStart + mGapLength;
378                 else
379                     mSpanEnds[i] = start;
380             }
381 
382             // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE
383             // XXX send notification on removal
384 
385             if (mSpanEnds[i] < mSpanStarts[i]) {
386                 System.arraycopy(mSpans, i + 1,
387                                  mSpans, i, mSpanCount - (i + 1));
388                 System.arraycopy(mSpanStarts, i + 1,
389                                  mSpanStarts, i, mSpanCount - (i + 1));
390                 System.arraycopy(mSpanEnds, i + 1,
391                                  mSpanEnds, i, mSpanCount - (i + 1));
392                 System.arraycopy(mSpanFlags, i + 1,
393                                  mSpanFlags, i, mSpanCount - (i + 1));
394 
395                 mSpanCount--;
396             }
397         }
398 
399         if (notify) {
400             sendTextChange(recipients, start, end - start, tbend - tbstart);
401             sendTextHasChanged(recipients);
402         }
403 
404         return ret;
405     }
406 
407     // Documentation from interface
replace(int start, int end, CharSequence tb)408     public SpannableStringBuilder replace(int start, int end, CharSequence tb) {
409         return replace(start, end, tb, 0, tb.length());
410     }
411 
412     // Documentation from interface
replace(final int start, final int end, CharSequence tb, int tbstart, int tbend)413     public SpannableStringBuilder replace(final int start, final int end,
414                         CharSequence tb, int tbstart, int tbend) {
415         int filtercount = mFilters.length;
416         for (int i = 0; i < filtercount; i++) {
417             CharSequence repl = mFilters[i].filter(tb, tbstart, tbend,
418                                                    this, start, end);
419 
420             if (repl != null) {
421                 tb = repl;
422                 tbstart = 0;
423                 tbend = repl.length();
424             }
425         }
426 
427         if (end == start && tbstart == tbend) {
428             return this;
429         }
430 
431         if (end == start || tbstart == tbend) {
432             change(start, end, tb, tbstart, tbend);
433         } else {
434             int selstart = Selection.getSelectionStart(this);
435             int selend = Selection.getSelectionEnd(this);
436 
437             // XXX just make the span fixups in change() do the right thing
438             // instead of this madness!
439 
440             checkRange("replace", start, end);
441             moveGapTo(end);
442             TextWatcher[] recipients;
443 
444             recipients = sendTextWillChange(start, end - start,
445                                             tbend - tbstart);
446 
447             int origlen = end - start;
448 
449             if (mGapLength < 2)
450                 resizeFor(length() + 1);
451 
452             for (int i = mSpanCount - 1; i >= 0; i--) {
453                 if (mSpanStarts[i] == mGapStart)
454                     mSpanStarts[i]++;
455 
456                 if (mSpanEnds[i] == mGapStart)
457                     mSpanEnds[i]++;
458             }
459 
460             mText[mGapStart] = ' ';
461             mGapStart++;
462             mGapLength--;
463 
464             if (mGapLength < 1)
465                 new Exception("mGapLength < 1").printStackTrace();
466 
467             int oldlen = (end + 1) - start;
468 
469             int inserted = change(false, start + 1, start + 1,
470                                   tb, tbstart, tbend);
471             change(false, start, start + 1, "", 0, 0);
472             change(false, start + inserted, start + inserted + oldlen - 1,
473                    "", 0, 0);
474 
475             /*
476              * Special case to keep the cursor in the same position
477              * if it was somewhere in the middle of the replaced region.
478              * If it was at the start or the end or crossing the whole
479              * replacement, it should already be where it belongs.
480              * TODO: Is there some more general mechanism that could
481              * accomplish this?
482              */
483             if (selstart > start && selstart < end) {
484                 long off = selstart - start;
485 
486                 off = off * inserted / (end - start);
487                 selstart = (int) off + start;
488 
489                 setSpan(false, Selection.SELECTION_START, selstart, selstart,
490                         Spanned.SPAN_POINT_POINT);
491             }
492             if (selend > start && selend < end) {
493                 long off = selend - start;
494 
495                 off = off * inserted / (end - start);
496                 selend = (int) off + start;
497 
498                 setSpan(false, Selection.SELECTION_END, selend, selend,
499                         Spanned.SPAN_POINT_POINT);
500             }
501 
502             sendTextChange(recipients, start, origlen, inserted);
503             sendTextHasChanged(recipients);
504         }
505         return this;
506     }
507 
508     /**
509      * Mark the specified range of text with the specified object.
510      * The flags determine how the span will behave when text is
511      * inserted at the start or end of the span's range.
512      */
setSpan(Object what, int start, int end, int flags)513     public void setSpan(Object what, int start, int end, int flags) {
514         setSpan(true, what, start, end, flags);
515     }
516 
setSpan(boolean send, Object what, int start, int end, int flags)517     private void setSpan(boolean send,
518                          Object what, int start, int end, int flags) {
519         int nstart = start;
520         int nend = end;
521 
522         checkRange("setSpan", start, end);
523 
524         if ((flags & START_MASK) == (PARAGRAPH << START_SHIFT)) {
525             if (start != 0 && start != length()) {
526                 char c = charAt(start - 1);
527 
528                 if (c != '\n')
529                     throw new RuntimeException(
530                             "PARAGRAPH span must start at paragraph boundary");
531             }
532         }
533 
534         if ((flags & END_MASK) == PARAGRAPH) {
535             if (end != 0 && end != length()) {
536                 char c = charAt(end - 1);
537 
538                 if (c != '\n')
539                     throw new RuntimeException(
540                             "PARAGRAPH span must end at paragraph boundary");
541             }
542         }
543 
544         if (start > mGapStart)
545             start += mGapLength;
546         else if (start == mGapStart) {
547             int flag = (flags & START_MASK) >> START_SHIFT;
548 
549             if (flag == POINT || (flag == PARAGRAPH && start == length()))
550                 start += mGapLength;
551         }
552 
553         if (end > mGapStart)
554             end += mGapLength;
555         else if (end == mGapStart) {
556             int flag = (flags & END_MASK);
557 
558             if (flag == POINT || (flag == PARAGRAPH && end == length()))
559                 end += mGapLength;
560         }
561 
562         int count = mSpanCount;
563         Object[] spans = mSpans;
564 
565         for (int i = 0; i < count; i++) {
566             if (spans[i] == what) {
567                 int ostart = mSpanStarts[i];
568                 int oend = mSpanEnds[i];
569 
570                 if (ostart > mGapStart)
571                     ostart -= mGapLength;
572                 if (oend > mGapStart)
573                     oend -= mGapLength;
574 
575                 mSpanStarts[i] = start;
576                 mSpanEnds[i] = end;
577                 mSpanFlags[i] = flags;
578 
579                 if (send)
580                     sendSpanChanged(what, ostart, oend, nstart, nend);
581 
582                 return;
583             }
584         }
585 
586         if (mSpanCount + 1 >= mSpans.length) {
587             int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1);
588             Object[] newspans = new Object[newsize];
589             int[] newspanstarts = new int[newsize];
590             int[] newspanends = new int[newsize];
591             int[] newspanflags = new int[newsize];
592 
593             System.arraycopy(mSpans, 0, newspans, 0, mSpanCount);
594             System.arraycopy(mSpanStarts, 0, newspanstarts, 0, mSpanCount);
595             System.arraycopy(mSpanEnds, 0, newspanends, 0, mSpanCount);
596             System.arraycopy(mSpanFlags, 0, newspanflags, 0, mSpanCount);
597 
598             mSpans = newspans;
599             mSpanStarts = newspanstarts;
600             mSpanEnds = newspanends;
601             mSpanFlags = newspanflags;
602         }
603 
604         mSpans[mSpanCount] = what;
605         mSpanStarts[mSpanCount] = start;
606         mSpanEnds[mSpanCount] = end;
607         mSpanFlags[mSpanCount] = flags;
608         mSpanCount++;
609 
610         if (send)
611             sendSpanAdded(what, nstart, nend);
612     }
613 
614     /**
615      * Remove the specified markup object from the buffer.
616      */
removeSpan(Object what)617     public void removeSpan(Object what) {
618         for (int i = mSpanCount - 1; i >= 0; i--) {
619             if (mSpans[i] == what) {
620                 int ostart = mSpanStarts[i];
621                 int oend = mSpanEnds[i];
622 
623                 if (ostart > mGapStart)
624                     ostart -= mGapLength;
625                 if (oend > mGapStart)
626                     oend -= mGapLength;
627 
628                 int count = mSpanCount - (i + 1);
629 
630                 System.arraycopy(mSpans, i + 1, mSpans, i, count);
631                 System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
632                 System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
633                 System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);
634 
635                 mSpanCount--;
636                 mSpans[mSpanCount] = null;
637 
638                 sendSpanRemoved(what, ostart, oend);
639                 return;
640             }
641         }
642     }
643 
644     /**
645      * Return the buffer offset of the beginning of the specified
646      * markup object, or -1 if it is not attached to this buffer.
647      */
getSpanStart(Object what)648     public int getSpanStart(Object what) {
649         int count = mSpanCount;
650         Object[] spans = mSpans;
651 
652         for (int i = count - 1; i >= 0; i--) {
653             if (spans[i] == what) {
654                 int where = mSpanStarts[i];
655 
656                 if (where > mGapStart)
657                     where -= mGapLength;
658 
659                 return where;
660             }
661         }
662 
663         return -1;
664     }
665 
666     /**
667      * Return the buffer offset of the end of the specified
668      * markup object, or -1 if it is not attached to this buffer.
669      */
getSpanEnd(Object what)670     public int getSpanEnd(Object what) {
671         int count = mSpanCount;
672         Object[] spans = mSpans;
673 
674         for (int i = count - 1; i >= 0; i--) {
675             if (spans[i] == what) {
676                 int where = mSpanEnds[i];
677 
678                 if (where > mGapStart)
679                     where -= mGapLength;
680 
681                 return where;
682             }
683         }
684 
685         return -1;
686     }
687 
688     /**
689      * Return the flags of the end of the specified
690      * markup object, or 0 if it is not attached to this buffer.
691      */
getSpanFlags(Object what)692     public int getSpanFlags(Object what) {
693         int count = mSpanCount;
694         Object[] spans = mSpans;
695 
696         for (int i = count - 1; i >= 0; i--) {
697             if (spans[i] == what) {
698                 return mSpanFlags[i];
699             }
700         }
701 
702         return 0;
703     }
704 
705     /**
706      * Return an array of the spans of the specified type that overlap
707      * the specified range of the buffer.  The kind may be Object.class to get
708      * a list of all the spans regardless of type.
709      */
getSpans(int queryStart, int queryEnd, Class<T> kind)710     public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
711         int spanCount = mSpanCount;
712         Object[] spans = mSpans;
713         int[] starts = mSpanStarts;
714         int[] ends = mSpanEnds;
715         int[] flags = mSpanFlags;
716         int gapstart = mGapStart;
717         int gaplen = mGapLength;
718 
719         int count = 0;
720         Object[] ret = null;
721         Object ret1 = null;
722 
723         for (int i = 0; i < spanCount; i++) {
724             int spanStart = starts[i];
725             int spanEnd = ends[i];
726 
727             if (spanStart > gapstart) {
728                 spanStart -= gaplen;
729             }
730             if (spanEnd > gapstart) {
731                 spanEnd -= gaplen;
732             }
733 
734             if (spanStart > queryEnd) {
735                 continue;
736             }
737             if (spanEnd < queryStart) {
738                 continue;
739             }
740 
741             if (spanStart != spanEnd && queryStart != queryEnd) {
742                 if (spanStart == queryEnd)
743                     continue;
744                 if (spanEnd == queryStart)
745                     continue;
746             }
747 
748             if (kind != null && !kind.isInstance(spans[i])) {
749                 continue;
750             }
751 
752             if (count == 0) {
753                 ret1 = spans[i];
754                 count++;
755             } else {
756                 if (count == 1) {
757                     ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
758                     ret[0] = ret1;
759                 }
760 
761                 int prio = flags[i] & SPAN_PRIORITY;
762                 if (prio != 0) {
763                     int j;
764 
765                     for (j = 0; j < count; j++) {
766                         int p = getSpanFlags(ret[j]) & SPAN_PRIORITY;
767 
768                         if (prio > p) {
769                             break;
770                         }
771                     }
772 
773                     System.arraycopy(ret, j, ret, j + 1, count - j);
774                     ret[j] = spans[i];
775                     count++;
776                 } else {
777                     ret[count++] = spans[i];
778                 }
779             }
780         }
781 
782         if (count == 0) {
783             return (T[]) ArrayUtils.emptyArray(kind);
784         }
785         if (count == 1) {
786             ret = (Object[]) Array.newInstance(kind, 1);
787             ret[0] = ret1;
788             return (T[]) ret;
789         }
790         if (count == ret.length) {
791             return (T[]) ret;
792         }
793 
794         Object[] nret = (Object[]) Array.newInstance(kind, count);
795         System.arraycopy(ret, 0, nret, 0, count);
796         return (T[]) nret;
797     }
798 
799     /**
800      * Return the next offset after <code>start</code> but less than or
801      * equal to <code>limit</code> where a span of the specified type
802      * begins or ends.
803      */
nextSpanTransition(int start, int limit, Class kind)804     public int nextSpanTransition(int start, int limit, Class kind) {
805         int count = mSpanCount;
806         Object[] spans = mSpans;
807         int[] starts = mSpanStarts;
808         int[] ends = mSpanEnds;
809         int gapstart = mGapStart;
810         int gaplen = mGapLength;
811 
812         if (kind == null) {
813             kind = Object.class;
814         }
815 
816         for (int i = 0; i < count; i++) {
817             int st = starts[i];
818             int en = ends[i];
819 
820             if (st > gapstart)
821                 st -= gaplen;
822             if (en > gapstart)
823                 en -= gaplen;
824 
825             if (st > start && st < limit && kind.isInstance(spans[i]))
826                 limit = st;
827             if (en > start && en < limit && kind.isInstance(spans[i]))
828                 limit = en;
829         }
830 
831         return limit;
832     }
833 
834     /**
835      * Return a new CharSequence containing a copy of the specified
836      * range of this buffer, including the overlapping spans.
837      */
subSequence(int start, int end)838     public CharSequence subSequence(int start, int end) {
839         return new SpannableStringBuilder(this, start, end);
840     }
841 
842     /**
843      * Copy the specified range of chars from this buffer into the
844      * specified array, beginning at the specified offset.
845      */
getChars(int start, int end, char[] dest, int destoff)846     public void getChars(int start, int end, char[] dest, int destoff) {
847         checkRange("getChars", start, end);
848 
849         if (end <= mGapStart) {
850             System.arraycopy(mText, start, dest, destoff, end - start);
851         } else if (start >= mGapStart) {
852             System.arraycopy(mText, start + mGapLength,
853                              dest, destoff, end - start);
854         } else {
855             System.arraycopy(mText, start, dest, destoff, mGapStart - start);
856             System.arraycopy(mText, mGapStart + mGapLength,
857                              dest, destoff + (mGapStart - start),
858                              end - mGapStart);
859         }
860     }
861 
862     /**
863      * Return a String containing a copy of the chars in this buffer.
864      */
toString()865     public String toString() {
866         int len = length();
867         char[] buf = new char[len];
868 
869         getChars(0, len, buf, 0);
870         return new String(buf);
871     }
872 
sendTextWillChange(int start, int before, int after)873     private TextWatcher[] sendTextWillChange(int start, int before, int after) {
874         TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class);
875         int n = recip.length;
876 
877         for (int i = 0; i < n; i++) {
878             recip[i].beforeTextChanged(this, start, before, after);
879         }
880 
881         return recip;
882     }
883 
sendTextChange(TextWatcher[] recip, int start, int before, int after)884     private void sendTextChange(TextWatcher[] recip, int start, int before,
885                                 int after) {
886         int n = recip.length;
887 
888         for (int i = 0; i < n; i++) {
889             recip[i].onTextChanged(this, start, before, after);
890         }
891     }
892 
sendTextHasChanged(TextWatcher[] recip)893     private void sendTextHasChanged(TextWatcher[] recip) {
894         int n = recip.length;
895 
896         for (int i = 0; i < n; i++) {
897             recip[i].afterTextChanged(this);
898         }
899     }
900 
sendSpanAdded(Object what, int start, int end)901     private void sendSpanAdded(Object what, int start, int end) {
902         SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
903         int n = recip.length;
904 
905         for (int i = 0; i < n; i++) {
906             recip[i].onSpanAdded(this, what, start, end);
907         }
908     }
909 
sendSpanRemoved(Object what, int start, int end)910     private void sendSpanRemoved(Object what, int start, int end) {
911         SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
912         int n = recip.length;
913 
914         for (int i = 0; i < n; i++) {
915             recip[i].onSpanRemoved(this, what, start, end);
916         }
917     }
918 
sendSpanChanged(Object what, int s, int e, int st, int en)919     private void sendSpanChanged(Object what, int s, int e, int st, int en) {
920         SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
921                                   SpanWatcher.class);
922         int n = recip.length;
923 
924         for (int i = 0; i < n; i++) {
925             recip[i].onSpanChanged(this, what, s, e, st, en);
926         }
927     }
928 
region(int start, int end)929     private static String region(int start, int end) {
930         return "(" + start + " ... " + end + ")";
931     }
932 
checkRange(final String operation, int start, int end)933     private void checkRange(final String operation, int start, int end) {
934         if (end < start) {
935             throw new IndexOutOfBoundsException(operation + " " +
936                                                 region(start, end) +
937                                                 " has end before start");
938         }
939 
940         int len = length();
941 
942         if (start > len || end > len) {
943             throw new IndexOutOfBoundsException(operation + " " +
944                                                 region(start, end) +
945                                                 " ends beyond length " + len);
946         }
947 
948         if (start < 0 || end < 0) {
949             throw new IndexOutOfBoundsException(operation + " " +
950                                                 region(start, end) +
951                                                 " starts before 0");
952         }
953     }
954 
isprint(char c)955     private boolean isprint(char c) { // XXX
956         if (c >= ' ' && c <= '~')
957             return true;
958         else
959             return false;
960     }
961 
962 /*
963     private static final int startFlag(int flag) {
964         return (flag >> 4) & 0x0F;
965     }
966 
967     private static final int endFlag(int flag) {
968         return flag & 0x0F;
969     }
970 
971     public void dump() { // XXX
972         for (int i = 0; i < mGapStart; i++) {
973             System.out.print('|');
974             System.out.print(' ');
975             System.out.print(isprint(mText[i]) ? mText[i] : '.');
976             System.out.print(' ');
977         }
978 
979         for (int i = mGapStart; i < mGapStart + mGapLength; i++) {
980             System.out.print('|');
981             System.out.print('(');
982             System.out.print(isprint(mText[i]) ? mText[i] : '.');
983             System.out.print(')');
984         }
985 
986         for (int i = mGapStart + mGapLength; i < mText.length; i++) {
987             System.out.print('|');
988             System.out.print(' ');
989             System.out.print(isprint(mText[i]) ? mText[i] : '.');
990             System.out.print(' ');
991         }
992 
993         System.out.print('\n');
994 
995         for (int i = 0; i < mText.length + 1; i++) {
996             int found = 0;
997             int wfound = 0;
998 
999             for (int j = 0; j < mSpanCount; j++) {
1000                 if (mSpanStarts[j] == i) {
1001                     found = 1;
1002                     wfound = j;
1003                     break;
1004                 }
1005 
1006                 if (mSpanEnds[j] == i) {
1007                     found = 2;
1008                     wfound = j;
1009                     break;
1010                 }
1011             }
1012 
1013             if (found == 1) {
1014                 if (startFlag(mSpanFlags[wfound]) == MARK)
1015                     System.out.print("(   ");
1016                 if (startFlag(mSpanFlags[wfound]) == PARAGRAPH)
1017                     System.out.print("<   ");
1018                 else
1019                     System.out.print("[   ");
1020             } else if (found == 2) {
1021                 if (endFlag(mSpanFlags[wfound]) == POINT)
1022                     System.out.print(")   ");
1023                 if (endFlag(mSpanFlags[wfound]) == PARAGRAPH)
1024                     System.out.print(">   ");
1025                 else
1026                     System.out.print("]   ");
1027             } else {
1028                 System.out.print("    ");
1029             }
1030         }
1031 
1032         System.out.print("\n");
1033     }
1034 */
1035 
1036     /**
1037      * Don't call this yourself -- exists for Canvas to use internally.
1038      * {@hide}
1039      */
drawText(Canvas c, int start, int end, float x, float y, Paint p)1040     public void drawText(Canvas c, int start, int end,
1041                          float x, float y, Paint p) {
1042         checkRange("drawText", start, end);
1043 
1044         if (end <= mGapStart) {
1045             c.drawText(mText, start, end - start, x, y, p);
1046         } else if (start >= mGapStart) {
1047             c.drawText(mText, start + mGapLength, end - start, x, y, p);
1048         } else {
1049             char[] buf = TextUtils.obtain(end - start);
1050 
1051             getChars(start, end, buf, 0);
1052             c.drawText(buf, 0, end - start, x, y, p);
1053             TextUtils.recycle(buf);
1054         }
1055     }
1056 
1057     /**
1058      * Don't call this yourself -- exists for Paint to use internally.
1059      * {@hide}
1060      */
measureText(int start, int end, Paint p)1061     public float measureText(int start, int end, Paint p) {
1062         checkRange("measureText", start, end);
1063 
1064         float ret;
1065 
1066         if (end <= mGapStart) {
1067             ret = p.measureText(mText, start, end - start);
1068         } else if (start >= mGapStart) {
1069             ret = p.measureText(mText, start + mGapLength, end - start);
1070         } else {
1071             char[] buf = TextUtils.obtain(end - start);
1072 
1073             getChars(start, end, buf, 0);
1074             ret = p.measureText(buf, 0, end - start);
1075             TextUtils.recycle(buf);
1076         }
1077 
1078         return ret;
1079     }
1080 
1081     /**
1082      * Don't call this yourself -- exists for Paint to use internally.
1083      * {@hide}
1084      */
getTextWidths(int start, int end, float[] widths, Paint p)1085     public int getTextWidths(int start, int end, float[] widths, Paint p) {
1086         checkRange("getTextWidths", start, end);
1087 
1088         int ret;
1089 
1090         if (end <= mGapStart) {
1091             ret = p.getTextWidths(mText, start, end - start, widths);
1092         } else if (start >= mGapStart) {
1093             ret = p.getTextWidths(mText, start + mGapLength, end - start,
1094                                   widths);
1095         } else {
1096             char[] buf = TextUtils.obtain(end - start);
1097 
1098             getChars(start, end, buf, 0);
1099             ret = p.getTextWidths(buf, 0, end - start, widths);
1100             TextUtils.recycle(buf);
1101         }
1102 
1103         return ret;
1104     }
1105 
1106     // Documentation from interface
setFilters(InputFilter[] filters)1107     public void setFilters(InputFilter[] filters) {
1108         if (filters == null) {
1109             throw new IllegalArgumentException();
1110         }
1111 
1112         mFilters = filters;
1113     }
1114 
1115     // Documentation from interface
getFilters()1116     public InputFilter[] getFilters() {
1117         return mFilters;
1118     }
1119 
1120     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
1121     private InputFilter[] mFilters = NO_FILTERS;
1122 
1123     private char[] mText;
1124     private int mGapStart;
1125     private int mGapLength;
1126 
1127     private Object[] mSpans;
1128     private int[] mSpanStarts;
1129     private int[] mSpanEnds;
1130     private int[] mSpanFlags;
1131     private int mSpanCount;
1132 
1133     private static final int MARK = 1;
1134     private static final int POINT = 2;
1135     private static final int PARAGRAPH = 3;
1136 
1137     private static final int START_MASK = 0xF0;
1138     private static final int END_MASK = 0x0F;
1139     private static final int START_SHIFT = 4;
1140 }
1141