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
17package android.text;
18
19import android.annotation.Nullable;
20import android.graphics.Canvas;
21import android.graphics.Paint;
22import android.util.Log;
23
24import com.android.internal.util.ArrayUtils;
25import com.android.internal.util.GrowingArrayUtils;
26
27import libcore.util.EmptyArray;
28
29import java.lang.reflect.Array;
30import java.util.IdentityHashMap;
31
32/**
33 * This is the class for text whose content and markup can both be changed.
34 */
35public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
36        Appendable, GraphicsOperations {
37    private final static String TAG = "SpannableStringBuilder";
38    /**
39     * Create a new SpannableStringBuilder with empty contents
40     */
41    public SpannableStringBuilder() {
42        this("");
43    }
44
45    /**
46     * Create a new SpannableStringBuilder containing a copy of the
47     * specified text, including its spans if any.
48     */
49    public SpannableStringBuilder(CharSequence text) {
50        this(text, 0, text.length());
51    }
52
53    /**
54     * Create a new SpannableStringBuilder containing a copy of the
55     * specified slice of the specified text, including its spans if any.
56     */
57    public SpannableStringBuilder(CharSequence text, int start, int end) {
58        int srclen = end - start;
59
60        if (srclen < 0) throw new StringIndexOutOfBoundsException();
61
62        mText = ArrayUtils.newUnpaddedCharArray(GrowingArrayUtils.growSize(srclen));
63        mGapStart = srclen;
64        mGapLength = mText.length - srclen;
65
66        TextUtils.getChars(text, start, end, mText, 0);
67
68        mSpanCount = 0;
69        mSpanInsertCount = 0;
70        mSpans = EmptyArray.OBJECT;
71        mSpanStarts = EmptyArray.INT;
72        mSpanEnds = EmptyArray.INT;
73        mSpanFlags = EmptyArray.INT;
74        mSpanMax = EmptyArray.INT;
75        mSpanOrder = EmptyArray.INT;
76        mPrioSortBuffer = EmptyArray.INT;
77        mOrderSortBuffer = EmptyArray.INT;
78
79        if (text instanceof Spanned) {
80            Spanned sp = (Spanned) text;
81            Object[] spans = sp.getSpans(start, end, Object.class);
82
83            for (int i = 0; i < spans.length; i++) {
84                if (spans[i] instanceof NoCopySpan) {
85                    continue;
86                }
87
88                int st = sp.getSpanStart(spans[i]) - start;
89                int en = sp.getSpanEnd(spans[i]) - start;
90                int fl = sp.getSpanFlags(spans[i]);
91
92                if (st < 0)
93                    st = 0;
94                if (st > end - start)
95                    st = end - start;
96
97                if (en < 0)
98                    en = 0;
99                if (en > end - start)
100                    en = end - start;
101
102                setSpan(false, spans[i], st, en, fl);
103            }
104            restoreInvariants();
105        }
106    }
107
108    public static SpannableStringBuilder valueOf(CharSequence source) {
109        if (source instanceof SpannableStringBuilder) {
110            return (SpannableStringBuilder) source;
111        } else {
112            return new SpannableStringBuilder(source);
113        }
114    }
115
116    /**
117     * Return the char at the specified offset within the buffer.
118     */
119    public char charAt(int where) {
120        int len = length();
121        if (where < 0) {
122            throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
123        } else if (where >= len) {
124            throw new IndexOutOfBoundsException("charAt: " + where + " >= length " + len);
125        }
126
127        if (where >= mGapStart)
128            return mText[where + mGapLength];
129        else
130            return mText[where];
131    }
132
133    /**
134     * Return the number of chars in the buffer.
135     */
136    public int length() {
137        return mText.length - mGapLength;
138    }
139
140    private void resizeFor(int size) {
141        final int oldLength = mText.length;
142        if (size + 1 <= oldLength) {
143            return;
144        }
145
146        char[] newText = ArrayUtils.newUnpaddedCharArray(GrowingArrayUtils.growSize(size));
147        System.arraycopy(mText, 0, newText, 0, mGapStart);
148        final int newLength = newText.length;
149        final int delta = newLength - oldLength;
150        final int after = oldLength - (mGapStart + mGapLength);
151        System.arraycopy(mText, oldLength - after, newText, newLength - after, after);
152        mText = newText;
153
154        mGapLength += delta;
155        if (mGapLength < 1)
156            new Exception("mGapLength < 1").printStackTrace();
157
158        if (mSpanCount != 0) {
159            for (int i = 0; i < mSpanCount; i++) {
160                if (mSpanStarts[i] > mGapStart) mSpanStarts[i] += delta;
161                if (mSpanEnds[i] > mGapStart) mSpanEnds[i] += delta;
162            }
163            calcMax(treeRoot());
164        }
165    }
166
167    private void moveGapTo(int where) {
168        if (where == mGapStart)
169            return;
170
171        boolean atEnd = (where == length());
172
173        if (where < mGapStart) {
174            int overlap = mGapStart - where;
175            System.arraycopy(mText, where, mText, mGapStart + mGapLength - overlap, overlap);
176        } else /* where > mGapStart */ {
177            int overlap = where - mGapStart;
178            System.arraycopy(mText, where + mGapLength - overlap, mText, mGapStart, overlap);
179        }
180
181        // TODO: be more clever (although the win really isn't that big)
182        if (mSpanCount != 0) {
183            for (int i = 0; i < mSpanCount; i++) {
184                int start = mSpanStarts[i];
185                int end = mSpanEnds[i];
186
187                if (start > mGapStart)
188                    start -= mGapLength;
189                if (start > where)
190                    start += mGapLength;
191                else if (start == where) {
192                    int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
193
194                    if (flag == POINT || (atEnd && flag == PARAGRAPH))
195                        start += mGapLength;
196                }
197
198                if (end > mGapStart)
199                    end -= mGapLength;
200                if (end > where)
201                    end += mGapLength;
202                else if (end == where) {
203                    int flag = (mSpanFlags[i] & END_MASK);
204
205                    if (flag == POINT || (atEnd && flag == PARAGRAPH))
206                        end += mGapLength;
207                }
208
209                mSpanStarts[i] = start;
210                mSpanEnds[i] = end;
211            }
212            calcMax(treeRoot());
213        }
214
215        mGapStart = where;
216    }
217
218    // Documentation from interface
219    public SpannableStringBuilder insert(int where, CharSequence tb, int start, int end) {
220        return replace(where, where, tb, start, end);
221    }
222
223    // Documentation from interface
224    public SpannableStringBuilder insert(int where, CharSequence tb) {
225        return replace(where, where, tb, 0, tb.length());
226    }
227
228    // Documentation from interface
229    public SpannableStringBuilder delete(int start, int end) {
230        SpannableStringBuilder ret = replace(start, end, "", 0, 0);
231
232        if (mGapLength > 2 * length())
233            resizeFor(length());
234
235        return ret; // == this
236    }
237
238    // Documentation from interface
239    public void clear() {
240        replace(0, length(), "", 0, 0);
241        mSpanInsertCount = 0;
242    }
243
244    // Documentation from interface
245    public void clearSpans() {
246        for (int i = mSpanCount - 1; i >= 0; i--) {
247            Object what = mSpans[i];
248            int ostart = mSpanStarts[i];
249            int oend = mSpanEnds[i];
250
251            if (ostart > mGapStart)
252                ostart -= mGapLength;
253            if (oend > mGapStart)
254                oend -= mGapLength;
255
256            mSpanCount = i;
257            mSpans[i] = null;
258
259            sendSpanRemoved(what, ostart, oend);
260        }
261        if (mIndexOfSpan != null) {
262            mIndexOfSpan.clear();
263        }
264        mSpanInsertCount = 0;
265    }
266
267    // Documentation from interface
268    public SpannableStringBuilder append(CharSequence text) {
269        int length = length();
270        return replace(length, length, text, 0, text.length());
271    }
272
273    /**
274     * Appends the character sequence {@code text} and spans {@code what} over the appended part.
275     * See {@link Spanned} for an explanation of what the flags mean.
276     * @param text the character sequence to append.
277     * @param what the object to be spanned over the appended text.
278     * @param flags see {@link Spanned}.
279     * @return this {@code SpannableStringBuilder}.
280     */
281    public SpannableStringBuilder append(CharSequence text, Object what, int flags) {
282        int start = length();
283        append(text);
284        setSpan(what, start, length(), flags);
285        return this;
286    }
287
288    // Documentation from interface
289    public SpannableStringBuilder append(CharSequence text, int start, int end) {
290        int length = length();
291        return replace(length, length, text, start, end);
292    }
293
294    // Documentation from interface
295    public SpannableStringBuilder append(char text) {
296        return append(String.valueOf(text));
297    }
298
299    // Returns true if a node was removed (so we can restart search from root)
300    private boolean removeSpansForChange(int start, int end, boolean textIsRemoved, int i) {
301        if ((i & 1) != 0) {
302            // internal tree node
303            if (resolveGap(mSpanMax[i]) >= start &&
304                    removeSpansForChange(start, end, textIsRemoved, leftChild(i))) {
305                return true;
306            }
307        }
308        if (i < mSpanCount) {
309            if ((mSpanFlags[i] & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) ==
310                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE &&
311                    mSpanStarts[i] >= start && mSpanStarts[i] < mGapStart + mGapLength &&
312                    mSpanEnds[i] >= start && mSpanEnds[i] < mGapStart + mGapLength &&
313                    // The following condition indicates that the span would become empty
314                    (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) {
315                mIndexOfSpan.remove(mSpans[i]);
316                removeSpan(i);
317                return true;
318            }
319            return resolveGap(mSpanStarts[i]) <= end && (i & 1) != 0 &&
320                removeSpansForChange(start, end, textIsRemoved, rightChild(i));
321        }
322        return false;
323    }
324
325    private void change(int start, int end, CharSequence cs, int csStart, int csEnd) {
326        // Can be negative
327        final int replacedLength = end - start;
328        final int replacementLength = csEnd - csStart;
329        final int nbNewChars = replacementLength - replacedLength;
330
331        boolean changed = false;
332        for (int i = mSpanCount - 1; i >= 0; i--) {
333            int spanStart = mSpanStarts[i];
334            if (spanStart > mGapStart)
335                spanStart -= mGapLength;
336
337            int spanEnd = mSpanEnds[i];
338            if (spanEnd > mGapStart)
339                spanEnd -= mGapLength;
340
341            if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
342                int ost = spanStart;
343                int oen = spanEnd;
344                int clen = length();
345
346                if (spanStart > start && spanStart <= end) {
347                    for (spanStart = end; spanStart < clen; spanStart++)
348                        if (spanStart > end && charAt(spanStart - 1) == '\n')
349                            break;
350                }
351
352                if (spanEnd > start && spanEnd <= end) {
353                    for (spanEnd = end; spanEnd < clen; spanEnd++)
354                        if (spanEnd > end && charAt(spanEnd - 1) == '\n')
355                            break;
356                }
357
358                if (spanStart != ost || spanEnd != oen) {
359                    setSpan(false, mSpans[i], spanStart, spanEnd, mSpanFlags[i]);
360                    changed = true;
361                }
362            }
363
364            int flags = 0;
365            if (spanStart == start) flags |= SPAN_START_AT_START;
366            else if (spanStart == end + nbNewChars) flags |= SPAN_START_AT_END;
367            if (spanEnd == start) flags |= SPAN_END_AT_START;
368            else if (spanEnd == end + nbNewChars) flags |= SPAN_END_AT_END;
369            mSpanFlags[i] |= flags;
370        }
371        if (changed) {
372            restoreInvariants();
373        }
374
375        moveGapTo(end);
376
377        if (nbNewChars >= mGapLength) {
378            resizeFor(mText.length + nbNewChars - mGapLength);
379        }
380
381        final boolean textIsRemoved = replacementLength == 0;
382        // The removal pass needs to be done before the gap is updated in order to broadcast the
383        // correct previous positions to the correct intersecting SpanWatchers
384        if (replacedLength > 0) { // no need for span fixup on pure insertion
385            while (mSpanCount > 0 &&
386                    removeSpansForChange(start, end, textIsRemoved, treeRoot())) {
387                // keep deleting spans as needed, and restart from root after every deletion
388                // because deletion can invalidate an index.
389            }
390        }
391
392        mGapStart += nbNewChars;
393        mGapLength -= nbNewChars;
394
395        if (mGapLength < 1)
396            new Exception("mGapLength < 1").printStackTrace();
397
398        TextUtils.getChars(cs, csStart, csEnd, mText, start);
399
400        if (replacedLength > 0) { // no need for span fixup on pure insertion
401            // TODO potential optimization: only update bounds on intersecting spans
402            final boolean atEnd = (mGapStart + mGapLength == mText.length);
403
404            for (int i = 0; i < mSpanCount; i++) {
405                final int startFlag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;
406                mSpanStarts[i] = updatedIntervalBound(mSpanStarts[i], start, nbNewChars, startFlag,
407                        atEnd, textIsRemoved);
408
409                final int endFlag = (mSpanFlags[i] & END_MASK);
410                mSpanEnds[i] = updatedIntervalBound(mSpanEnds[i], start, nbNewChars, endFlag,
411                        atEnd, textIsRemoved);
412            }
413            // TODO potential optimization: only fix up invariants when bounds actually changed
414            restoreInvariants();
415        }
416
417        if (cs instanceof Spanned) {
418            Spanned sp = (Spanned) cs;
419            Object[] spans = sp.getSpans(csStart, csEnd, Object.class);
420
421            for (int i = 0; i < spans.length; i++) {
422                int st = sp.getSpanStart(spans[i]);
423                int en = sp.getSpanEnd(spans[i]);
424
425                if (st < csStart) st = csStart;
426                if (en > csEnd) en = csEnd;
427
428                // Add span only if this object is not yet used as a span in this string
429                if (getSpanStart(spans[i]) < 0) {
430                    int copySpanStart = st - csStart + start;
431                    int copySpanEnd = en - csStart + start;
432                    int copySpanFlags = sp.getSpanFlags(spans[i]) | SPAN_ADDED;
433
434                    int flagsStart = (copySpanFlags & START_MASK) >> START_SHIFT;
435                    int flagsEnd = copySpanFlags & END_MASK;
436
437                    if(!isInvalidParagraphStart(copySpanStart, flagsStart) &&
438                            !isInvalidParagraphEnd(copySpanEnd, flagsEnd)) {
439                        setSpan(false, spans[i], copySpanStart, copySpanEnd, copySpanFlags);
440                    }
441                }
442            }
443            restoreInvariants();
444        }
445    }
446
447    private int updatedIntervalBound(int offset, int start, int nbNewChars, int flag, boolean atEnd,
448            boolean textIsRemoved) {
449        if (offset >= start && offset < mGapStart + mGapLength) {
450            if (flag == POINT) {
451                // A POINT located inside the replaced range should be moved to the end of the
452                // replaced text.
453                // The exception is when the point is at the start of the range and we are doing a
454                // text replacement (as opposed to a deletion): the point stays there.
455                if (textIsRemoved || offset > start) {
456                    return mGapStart + mGapLength;
457                }
458            } else {
459                if (flag == PARAGRAPH) {
460                    if (atEnd) {
461                        return mGapStart + mGapLength;
462                    }
463                } else { // MARK
464                    // MARKs should be moved to the start, with the exception of a mark located at
465                    // the end of the range (which will be < mGapStart + mGapLength since mGapLength
466                    // is > 0, which should stay 'unchanged' at the end of the replaced text.
467                    if (textIsRemoved || offset < mGapStart - nbNewChars) {
468                        return start;
469                    } else {
470                        // Move to the end of replaced text (needed if nbNewChars != 0)
471                        return mGapStart;
472                    }
473                }
474            }
475        }
476        return offset;
477    }
478
479    // Note: caller is responsible for removing the mIndexOfSpan entry.
480    private void removeSpan(int i) {
481        Object object = mSpans[i];
482
483        int start = mSpanStarts[i];
484        int end = mSpanEnds[i];
485
486        if (start > mGapStart) start -= mGapLength;
487        if (end > mGapStart) end -= mGapLength;
488
489        int count = mSpanCount - (i + 1);
490        System.arraycopy(mSpans, i + 1, mSpans, i, count);
491        System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
492        System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
493        System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);
494        System.arraycopy(mSpanOrder, i + 1, mSpanOrder, i, count);
495
496        mSpanCount--;
497
498        invalidateIndex(i);
499        mSpans[mSpanCount] = null;
500
501        // Invariants must be restored before sending span removed notifications.
502        restoreInvariants();
503
504        sendSpanRemoved(object, start, end);
505    }
506
507    // Documentation from interface
508    public SpannableStringBuilder replace(int start, int end, CharSequence tb) {
509        return replace(start, end, tb, 0, tb.length());
510    }
511
512    // Documentation from interface
513    public SpannableStringBuilder replace(final int start, final int end,
514            CharSequence tb, int tbstart, int tbend) {
515        checkRange("replace", start, end);
516
517        int filtercount = mFilters.length;
518        for (int i = 0; i < filtercount; i++) {
519            CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end);
520
521            if (repl != null) {
522                tb = repl;
523                tbstart = 0;
524                tbend = repl.length();
525            }
526        }
527
528        final int origLen = end - start;
529        final int newLen = tbend - tbstart;
530
531        if (origLen == 0 && newLen == 0 && !hasNonExclusiveExclusiveSpanAt(tb, tbstart)) {
532            // This is a no-op iif there are no spans in tb that would be added (with a 0-length)
533            // Early exit so that the text watchers do not get notified
534            return this;
535        }
536
537        TextWatcher[] textWatchers = getSpans(start, start + origLen, TextWatcher.class);
538        sendBeforeTextChanged(textWatchers, start, origLen, newLen);
539
540        // Try to keep the cursor / selection at the same relative position during
541        // a text replacement. If replaced or replacement text length is zero, this
542        // is already taken care of.
543        boolean adjustSelection = origLen != 0 && newLen != 0;
544        int selectionStart = 0;
545        int selectionEnd = 0;
546        if (adjustSelection) {
547            selectionStart = Selection.getSelectionStart(this);
548            selectionEnd = Selection.getSelectionEnd(this);
549        }
550
551        change(start, end, tb, tbstart, tbend);
552
553        if (adjustSelection) {
554            boolean changed = false;
555            if (selectionStart > start && selectionStart < end) {
556                final long diff = selectionStart - start;
557                final int offset = Math.toIntExact(diff * newLen / origLen);
558                selectionStart = start + offset;
559
560                changed = true;
561                setSpan(false, Selection.SELECTION_START, selectionStart, selectionStart,
562                        Spanned.SPAN_POINT_POINT);
563            }
564            if (selectionEnd > start && selectionEnd < end) {
565                final long diff = selectionEnd - start;
566                final int offset = Math.toIntExact(diff * newLen / origLen);
567                selectionEnd = start + offset;
568
569                changed = true;
570                setSpan(false, Selection.SELECTION_END, selectionEnd, selectionEnd,
571                        Spanned.SPAN_POINT_POINT);
572            }
573            if (changed) {
574                restoreInvariants();
575            }
576        }
577
578        sendTextChanged(textWatchers, start, origLen, newLen);
579        sendAfterTextChanged(textWatchers);
580
581        // Span watchers need to be called after text watchers, which may update the layout
582        sendToSpanWatchers(start, end, newLen - origLen);
583
584        return this;
585    }
586
587    private static boolean hasNonExclusiveExclusiveSpanAt(CharSequence text, int offset) {
588        if (text instanceof Spanned) {
589            Spanned spanned = (Spanned) text;
590            Object[] spans = spanned.getSpans(offset, offset, Object.class);
591            final int length = spans.length;
592            for (int i = 0; i < length; i++) {
593                Object span = spans[i];
594                int flags = spanned.getSpanFlags(span);
595                if (flags != Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) return true;
596            }
597        }
598        return false;
599    }
600
601    private void sendToSpanWatchers(int replaceStart, int replaceEnd, int nbNewChars) {
602        for (int i = 0; i < mSpanCount; i++) {
603            int spanFlags = mSpanFlags[i];
604
605            // This loop handles only modified (not added) spans.
606            if ((spanFlags & SPAN_ADDED) != 0) continue;
607            int spanStart = mSpanStarts[i];
608            int spanEnd = mSpanEnds[i];
609            if (spanStart > mGapStart) spanStart -= mGapLength;
610            if (spanEnd > mGapStart) spanEnd -= mGapLength;
611
612            int newReplaceEnd = replaceEnd + nbNewChars;
613            boolean spanChanged = false;
614
615            int previousSpanStart = spanStart;
616            if (spanStart > newReplaceEnd) {
617                if (nbNewChars != 0) {
618                    previousSpanStart -= nbNewChars;
619                    spanChanged = true;
620                }
621            } else if (spanStart >= replaceStart) {
622                // No change if span start was already at replace interval boundaries before replace
623                if ((spanStart != replaceStart ||
624                        ((spanFlags & SPAN_START_AT_START) != SPAN_START_AT_START)) &&
625                        (spanStart != newReplaceEnd ||
626                        ((spanFlags & SPAN_START_AT_END) != SPAN_START_AT_END))) {
627                    // TODO A correct previousSpanStart cannot be computed at this point.
628                    // It would require to save all the previous spans' positions before the replace
629                    // Using an invalid -1 value to convey this would break the broacast range
630                    spanChanged = true;
631                }
632            }
633
634            int previousSpanEnd = spanEnd;
635            if (spanEnd > newReplaceEnd) {
636                if (nbNewChars != 0) {
637                    previousSpanEnd -= nbNewChars;
638                    spanChanged = true;
639                }
640            } else if (spanEnd >= replaceStart) {
641                // No change if span start was already at replace interval boundaries before replace
642                if ((spanEnd != replaceStart ||
643                        ((spanFlags & SPAN_END_AT_START) != SPAN_END_AT_START)) &&
644                        (spanEnd != newReplaceEnd ||
645                        ((spanFlags & SPAN_END_AT_END) != SPAN_END_AT_END))) {
646                    // TODO same as above for previousSpanEnd
647                    spanChanged = true;
648                }
649            }
650
651            if (spanChanged) {
652                sendSpanChanged(mSpans[i], previousSpanStart, previousSpanEnd, spanStart, spanEnd);
653            }
654            mSpanFlags[i] &= ~SPAN_START_END_MASK;
655        }
656
657        // Handle added spans
658        for (int i = 0; i < mSpanCount; i++) {
659            int spanFlags = mSpanFlags[i];
660            if ((spanFlags & SPAN_ADDED) != 0) {
661                mSpanFlags[i] &= ~SPAN_ADDED;
662                int spanStart = mSpanStarts[i];
663                int spanEnd = mSpanEnds[i];
664                if (spanStart > mGapStart) spanStart -= mGapLength;
665                if (spanEnd > mGapStart) spanEnd -= mGapLength;
666                sendSpanAdded(mSpans[i], spanStart, spanEnd);
667            }
668        }
669    }
670
671    /**
672     * Mark the specified range of text with the specified object.
673     * The flags determine how the span will behave when text is
674     * inserted at the start or end of the span's range.
675     */
676    public void setSpan(Object what, int start, int end, int flags) {
677        setSpan(true, what, start, end, flags);
678    }
679
680    // Note: if send is false, then it is the caller's responsibility to restore
681    // invariants. If send is false and the span already exists, then this method
682    // will not change the index of any spans.
683    private void setSpan(boolean send, Object what, int start, int end, int flags) {
684        checkRange("setSpan", start, end);
685
686        int flagsStart = (flags & START_MASK) >> START_SHIFT;
687        if(isInvalidParagraphStart(start, flagsStart)) {
688            throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
689        }
690
691        int flagsEnd = flags & END_MASK;
692        if(isInvalidParagraphEnd(end, flagsEnd)) {
693            throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
694        }
695
696        // 0-length Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
697        if (flagsStart == POINT && flagsEnd == MARK && start == end) {
698            if (send) {
699                Log.e(TAG, "SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length");
700            }
701            // Silently ignore invalid spans when they are created from this class.
702            // This avoids the duplication of the above test code before all the
703            // calls to setSpan that are done in this class
704            return;
705        }
706
707        int nstart = start;
708        int nend = end;
709
710        if (start > mGapStart) {
711            start += mGapLength;
712        } else if (start == mGapStart) {
713            if (flagsStart == POINT || (flagsStart == PARAGRAPH && start == length()))
714                start += mGapLength;
715        }
716
717        if (end > mGapStart) {
718            end += mGapLength;
719        } else if (end == mGapStart) {
720            if (flagsEnd == POINT || (flagsEnd == PARAGRAPH && end == length()))
721                end += mGapLength;
722        }
723
724        if (mIndexOfSpan != null) {
725            Integer index = mIndexOfSpan.get(what);
726            if (index != null) {
727                int i = index;
728                int ostart = mSpanStarts[i];
729                int oend = mSpanEnds[i];
730
731                if (ostart > mGapStart)
732                    ostart -= mGapLength;
733                if (oend > mGapStart)
734                    oend -= mGapLength;
735
736                mSpanStarts[i] = start;
737                mSpanEnds[i] = end;
738                mSpanFlags[i] = flags;
739
740                if (send) {
741                    restoreInvariants();
742                    sendSpanChanged(what, ostart, oend, nstart, nend);
743                }
744
745                return;
746            }
747        }
748
749        mSpans = GrowingArrayUtils.append(mSpans, mSpanCount, what);
750        mSpanStarts = GrowingArrayUtils.append(mSpanStarts, mSpanCount, start);
751        mSpanEnds = GrowingArrayUtils.append(mSpanEnds, mSpanCount, end);
752        mSpanFlags = GrowingArrayUtils.append(mSpanFlags, mSpanCount, flags);
753        mSpanOrder = GrowingArrayUtils.append(mSpanOrder, mSpanCount, mSpanInsertCount);
754        invalidateIndex(mSpanCount);
755        mSpanCount++;
756        mSpanInsertCount++;
757        // Make sure there is enough room for empty interior nodes.
758        // This magic formula computes the size of the smallest perfect binary
759        // tree no smaller than mSpanCount.
760        int sizeOfMax = 2 * treeRoot() + 1;
761        if (mSpanMax.length < sizeOfMax) {
762            mSpanMax = new int[sizeOfMax];
763        }
764
765        if (send) {
766            restoreInvariants();
767            sendSpanAdded(what, nstart, nend);
768        }
769    }
770
771    private final boolean isInvalidParagraphStart(int start, int flagsStart) {
772        if (flagsStart == PARAGRAPH) {
773            if (start != 0 && start != length()) {
774                char c = charAt(start - 1);
775
776                if (c != '\n') return true;
777            }
778        }
779        return false;
780    }
781
782    private final boolean isInvalidParagraphEnd(int end, int flagsEnd) {
783        if (flagsEnd == PARAGRAPH) {
784            if (end != 0 && end != length()) {
785                char c = charAt(end - 1);
786
787                if (c != '\n') return true;
788            }
789        }
790        return false;
791    }
792
793    /**
794     * Remove the specified markup object from the buffer.
795     */
796    public void removeSpan(Object what) {
797        if (mIndexOfSpan == null) return;
798        Integer i = mIndexOfSpan.remove(what);
799        if (i != null) {
800            removeSpan(i.intValue());
801        }
802    }
803
804    /**
805     * Return externally visible offset given offset into gapped buffer.
806     */
807    private int resolveGap(int i) {
808        return i > mGapStart ? i - mGapLength : i;
809    }
810
811    /**
812     * Return the buffer offset of the beginning of the specified
813     * markup object, or -1 if it is not attached to this buffer.
814     */
815    public int getSpanStart(Object what) {
816        if (mIndexOfSpan == null) return -1;
817        Integer i = mIndexOfSpan.get(what);
818        return i == null ? -1 : resolveGap(mSpanStarts[i]);
819    }
820
821    /**
822     * Return the buffer offset of the end of the specified
823     * markup object, or -1 if it is not attached to this buffer.
824     */
825    public int getSpanEnd(Object what) {
826        if (mIndexOfSpan == null) return -1;
827        Integer i = mIndexOfSpan.get(what);
828        return i == null ? -1 : resolveGap(mSpanEnds[i]);
829    }
830
831    /**
832     * Return the flags of the end of the specified
833     * markup object, or 0 if it is not attached to this buffer.
834     */
835    public int getSpanFlags(Object what) {
836        if (mIndexOfSpan == null) return 0;
837        Integer i = mIndexOfSpan.get(what);
838        return i == null ? 0 : mSpanFlags[i];
839    }
840
841    /**
842     * Return an array of the spans of the specified type that overlap
843     * the specified range of the buffer.  The kind may be Object.class to get
844     * a list of all the spans regardless of type.
845     */
846    @SuppressWarnings("unchecked")
847    public <T> T[] getSpans(int queryStart, int queryEnd, @Nullable Class<T> kind) {
848        return getSpans(queryStart, queryEnd, kind, true);
849    }
850
851    /**
852     * Return an array of the spans of the specified type that overlap
853     * the specified range of the buffer.  The kind may be Object.class to get
854     * a list of all the spans regardless of type.
855     *
856     * @param queryStart Start index.
857     * @param queryEnd End index.
858     * @param kind Class type to search for.
859     * @param sort If true the results are sorted by the insertion order.
860     * @param <T>
861     * @return Array of the spans. Empty array if no results are found.
862     *
863     * @hide
864     */
865    public <T> T[] getSpans(int queryStart, int queryEnd, @Nullable Class<T> kind,
866                                 boolean sort) {
867        if (kind == null) return (T[]) ArrayUtils.emptyArray(Object.class);
868        if (mSpanCount == 0) return ArrayUtils.emptyArray(kind);
869        int count = countSpans(queryStart, queryEnd, kind, treeRoot());
870        if (count == 0) {
871            return ArrayUtils.emptyArray(kind);
872        }
873
874        // Safe conversion, but requires a suppressWarning
875        T[] ret = (T[]) Array.newInstance(kind, count);
876        if (sort) {
877            mPrioSortBuffer = checkSortBuffer(mPrioSortBuffer, count);
878            mOrderSortBuffer = checkSortBuffer(mOrderSortBuffer, count);
879        }
880        getSpansRec(queryStart, queryEnd, kind, treeRoot(), ret, mPrioSortBuffer,
881                mOrderSortBuffer, 0, sort);
882        if (sort) sort(ret, mPrioSortBuffer, mOrderSortBuffer);
883        return ret;
884    }
885
886    private int countSpans(int queryStart, int queryEnd, Class kind, int i) {
887        int count = 0;
888        if ((i & 1) != 0) {
889            // internal tree node
890            int left = leftChild(i);
891            int spanMax = mSpanMax[left];
892            if (spanMax > mGapStart) {
893                spanMax -= mGapLength;
894            }
895            if (spanMax >= queryStart) {
896                count = countSpans(queryStart, queryEnd, kind, left);
897            }
898        }
899        if (i < mSpanCount) {
900            int spanStart = mSpanStarts[i];
901            if (spanStart > mGapStart) {
902                spanStart -= mGapLength;
903            }
904            if (spanStart <= queryEnd) {
905                int spanEnd = mSpanEnds[i];
906                if (spanEnd > mGapStart) {
907                    spanEnd -= mGapLength;
908                }
909                if (spanEnd >= queryStart &&
910                    (spanStart == spanEnd || queryStart == queryEnd ||
911                        (spanStart != queryEnd && spanEnd != queryStart)) &&
912                        (Object.class == kind || kind.isInstance(mSpans[i]))) {
913                    count++;
914                }
915                if ((i & 1) != 0) {
916                    count += countSpans(queryStart, queryEnd, kind, rightChild(i));
917                }
918            }
919        }
920        return count;
921    }
922
923    /**
924     * Fills the result array with the spans found under the current interval tree node.
925     *
926     * @param queryStart Start index for the interval query.
927     * @param queryEnd End index for the interval query.
928     * @param kind Class type to search for.
929     * @param i Index of the current tree node.
930     * @param ret Array to be filled with results.
931     * @param priority Buffer to keep record of the priorities of spans found.
932     * @param insertionOrder Buffer to keep record of the insertion orders of spans found.
933     * @param count The number of found spans.
934     * @param sort Flag to fill the priority and insertion order buffers. If false then
935     *             the spans with priority flag will be sorted in the result array.
936     * @param <T>
937     * @return The total number of spans found.
938     */
939    @SuppressWarnings("unchecked")
940    private <T> int getSpansRec(int queryStart, int queryEnd, Class<T> kind,
941            int i, T[] ret, int[] priority, int[] insertionOrder, int count, boolean sort) {
942        if ((i & 1) != 0) {
943            // internal tree node
944            int left = leftChild(i);
945            int spanMax = mSpanMax[left];
946            if (spanMax > mGapStart) {
947                spanMax -= mGapLength;
948            }
949            if (spanMax >= queryStart) {
950                count = getSpansRec(queryStart, queryEnd, kind, left, ret, priority,
951                        insertionOrder, count, sort);
952            }
953        }
954        if (i >= mSpanCount) return count;
955        int spanStart = mSpanStarts[i];
956        if (spanStart > mGapStart) {
957            spanStart -= mGapLength;
958        }
959        if (spanStart <= queryEnd) {
960            int spanEnd = mSpanEnds[i];
961            if (spanEnd > mGapStart) {
962                spanEnd -= mGapLength;
963            }
964            if (spanEnd >= queryStart &&
965                    (spanStart == spanEnd || queryStart == queryEnd ||
966                        (spanStart != queryEnd && spanEnd != queryStart)) &&
967                        (Object.class == kind || kind.isInstance(mSpans[i]))) {
968                int spanPriority = mSpanFlags[i] & SPAN_PRIORITY;
969                int target = count;
970                if (sort) {
971                    priority[target] = spanPriority;
972                    insertionOrder[target] = mSpanOrder[i];
973                } else if (spanPriority != 0) {
974                    //insertion sort for elements with priority
975                    int j = 0;
976                    for (; j < count; j++) {
977                        int p = getSpanFlags(ret[j]) & SPAN_PRIORITY;
978                        if (spanPriority > p) break;
979                    }
980                    System.arraycopy(ret, j, ret, j + 1, count - j);
981                    target = j;
982                }
983                ret[target] = (T) mSpans[i];
984                count++;
985            }
986            if (count < ret.length && (i & 1) != 0) {
987                count = getSpansRec(queryStart, queryEnd, kind, rightChild(i), ret, priority,
988                        insertionOrder, count, sort);
989            }
990        }
991        return count;
992    }
993
994    /**
995     * Check the size of the buffer and grow if required.
996     *
997     * @param buffer Buffer to be checked.
998     * @param size Required size.
999     * @return Same buffer instance if the current size is greater than required size. Otherwise a
1000     * new instance is created and returned.
1001     */
1002    private final int[] checkSortBuffer(int[] buffer, int size) {
1003        if(size > buffer.length) {
1004            return ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(size));
1005        }
1006        return buffer;
1007    }
1008
1009    /**
1010     * An iterative heap sort implementation. It will sort the spans using first their priority
1011     * then insertion order. A span with higher priority will be before a span with lower
1012     * priority. If priorities are the same, the spans will be sorted with insertion order. A
1013     * span with a lower insertion order will be before a span with a higher insertion order.
1014     *
1015     * @param array Span array to be sorted.
1016     * @param priority Priorities of the spans
1017     * @param insertionOrder Insertion orders of the spans
1018     * @param <T> Span object type.
1019     * @param <T>
1020     */
1021    private final <T> void sort(T[] array, int[] priority, int[] insertionOrder) {
1022        int size = array.length;
1023        for (int i = size / 2 - 1; i >= 0; i--) {
1024            siftDown(i, array, size, priority, insertionOrder);
1025        }
1026
1027        for (int i = size - 1; i > 0; i--) {
1028            T v = array[0];
1029            int prio = priority[0];
1030            int insertOrder = insertionOrder[0];
1031            array[0] = array[i];
1032            priority[0] = priority[i];
1033            insertionOrder[0] = insertionOrder[i];
1034            siftDown(0, array, i, priority, insertionOrder);
1035            array[i] = v;
1036            priority[i] = prio;
1037            insertionOrder[i] = insertOrder;
1038        }
1039    }
1040
1041    /**
1042     * Helper function for heap sort.
1043     *
1044     * @param index Index of the element to sift down.
1045     * @param array Span array to be sorted.
1046     * @param size Current heap size.
1047     * @param priority Priorities of the spans
1048     * @param insertionOrder Insertion orders of the spans
1049     * @param <T> Span object type.
1050     */
1051    private final <T> void siftDown(int index, T[] array, int size, int[] priority,
1052                                    int[] insertionOrder) {
1053        T v = array[index];
1054        int prio = priority[index];
1055        int insertOrder = insertionOrder[index];
1056
1057        int left = 2 * index + 1;
1058        while (left < size) {
1059            if (left < size - 1 && compareSpans(left, left + 1, priority, insertionOrder) < 0) {
1060                left++;
1061            }
1062            if (compareSpans(index, left, priority, insertionOrder) >= 0) {
1063                break;
1064            }
1065            array[index] = array[left];
1066            priority[index] = priority[left];
1067            insertionOrder[index] = insertionOrder[left];
1068            index = left;
1069            left = 2 * index + 1;
1070        }
1071        array[index] = v;
1072        priority[index] = prio;
1073        insertionOrder[index] = insertOrder;
1074    }
1075
1076    /**
1077     * Compare two span elements in an array. Comparison is based first on the priority flag of
1078     * the span, and then the insertion order of the span.
1079     *
1080     * @param left Index of the element to compare.
1081     * @param right Index of the other element to compare.
1082     * @param priority Priorities of the spans
1083     * @param insertionOrder Insertion orders of the spans
1084     * @return
1085     */
1086    private final int compareSpans(int left, int right, int[] priority,
1087                                       int[] insertionOrder) {
1088        int priority1 = priority[left];
1089        int priority2 = priority[right];
1090        if (priority1 == priority2) {
1091            return Integer.compare(insertionOrder[left], insertionOrder[right]);
1092        }
1093        // since high priority has to be before a lower priority, the arguments to compare are
1094        // opposite of the insertion order check.
1095        return Integer.compare(priority2, priority1);
1096    }
1097
1098    /**
1099     * Return the next offset after <code>start</code> but less than or
1100     * equal to <code>limit</code> where a span of the specified type
1101     * begins or ends.
1102     */
1103    public int nextSpanTransition(int start, int limit, Class kind) {
1104        if (mSpanCount == 0) return limit;
1105        if (kind == null) {
1106            kind = Object.class;
1107        }
1108        return nextSpanTransitionRec(start, limit, kind, treeRoot());
1109    }
1110
1111    private int nextSpanTransitionRec(int start, int limit, Class kind, int i) {
1112        if ((i & 1) != 0) {
1113            // internal tree node
1114            int left = leftChild(i);
1115            if (resolveGap(mSpanMax[left]) > start) {
1116                limit = nextSpanTransitionRec(start, limit, kind, left);
1117            }
1118        }
1119        if (i < mSpanCount) {
1120            int st = resolveGap(mSpanStarts[i]);
1121            int en = resolveGap(mSpanEnds[i]);
1122            if (st > start && st < limit && kind.isInstance(mSpans[i]))
1123                limit = st;
1124            if (en > start && en < limit && kind.isInstance(mSpans[i]))
1125                limit = en;
1126            if (st < limit && (i & 1) != 0) {
1127                limit = nextSpanTransitionRec(start, limit, kind, rightChild(i));
1128            }
1129        }
1130
1131        return limit;
1132    }
1133
1134    /**
1135     * Return a new CharSequence containing a copy of the specified
1136     * range of this buffer, including the overlapping spans.
1137     */
1138    public CharSequence subSequence(int start, int end) {
1139        return new SpannableStringBuilder(this, start, end);
1140    }
1141
1142    /**
1143     * Copy the specified range of chars from this buffer into the
1144     * specified array, beginning at the specified offset.
1145     */
1146    public void getChars(int start, int end, char[] dest, int destoff) {
1147        checkRange("getChars", start, end);
1148
1149        if (end <= mGapStart) {
1150            System.arraycopy(mText, start, dest, destoff, end - start);
1151        } else if (start >= mGapStart) {
1152            System.arraycopy(mText, start + mGapLength, dest, destoff, end - start);
1153        } else {
1154            System.arraycopy(mText, start, dest, destoff, mGapStart - start);
1155            System.arraycopy(mText, mGapStart + mGapLength,
1156                    dest, destoff + (mGapStart - start),
1157                    end - mGapStart);
1158        }
1159    }
1160
1161    /**
1162     * Return a String containing a copy of the chars in this buffer.
1163     */
1164    @Override
1165    public String toString() {
1166        int len = length();
1167        char[] buf = new char[len];
1168
1169        getChars(0, len, buf, 0);
1170        return new String(buf);
1171    }
1172
1173    /**
1174     * Return a String containing a copy of the chars in this buffer, limited to the
1175     * [start, end[ range.
1176     * @hide
1177     */
1178    public String substring(int start, int end) {
1179        char[] buf = new char[end - start];
1180        getChars(start, end, buf, 0);
1181        return new String(buf);
1182    }
1183
1184    /**
1185     * Returns the depth of TextWatcher callbacks. Returns 0 when the object is not handling
1186     * TextWatchers. A return value greater than 1 implies that a TextWatcher caused a change that
1187     * recursively triggered a TextWatcher.
1188     */
1189    public int getTextWatcherDepth() {
1190        return mTextWatcherDepth;
1191    }
1192
1193    private void sendBeforeTextChanged(TextWatcher[] watchers, int start, int before, int after) {
1194        int n = watchers.length;
1195
1196        mTextWatcherDepth++;
1197        for (int i = 0; i < n; i++) {
1198            watchers[i].beforeTextChanged(this, start, before, after);
1199        }
1200        mTextWatcherDepth--;
1201    }
1202
1203    private void sendTextChanged(TextWatcher[] watchers, int start, int before, int after) {
1204        int n = watchers.length;
1205
1206        mTextWatcherDepth++;
1207        for (int i = 0; i < n; i++) {
1208            watchers[i].onTextChanged(this, start, before, after);
1209        }
1210        mTextWatcherDepth--;
1211    }
1212
1213    private void sendAfterTextChanged(TextWatcher[] watchers) {
1214        int n = watchers.length;
1215
1216        mTextWatcherDepth++;
1217        for (int i = 0; i < n; i++) {
1218            watchers[i].afterTextChanged(this);
1219        }
1220        mTextWatcherDepth--;
1221    }
1222
1223    private void sendSpanAdded(Object what, int start, int end) {
1224        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
1225        int n = recip.length;
1226
1227        for (int i = 0; i < n; i++) {
1228            recip[i].onSpanAdded(this, what, start, end);
1229        }
1230    }
1231
1232    private void sendSpanRemoved(Object what, int start, int end) {
1233        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
1234        int n = recip.length;
1235
1236        for (int i = 0; i < n; i++) {
1237            recip[i].onSpanRemoved(this, what, start, end);
1238        }
1239    }
1240
1241    private void sendSpanChanged(Object what, int oldStart, int oldEnd, int start, int end) {
1242        // The bounds of a possible SpanWatcher are guaranteed to be set before this method is
1243        // called, so that the order of the span does not affect this broadcast.
1244        SpanWatcher[] spanWatchers = getSpans(Math.min(oldStart, start),
1245                Math.min(Math.max(oldEnd, end), length()), SpanWatcher.class);
1246        int n = spanWatchers.length;
1247        for (int i = 0; i < n; i++) {
1248            spanWatchers[i].onSpanChanged(this, what, oldStart, oldEnd, start, end);
1249        }
1250    }
1251
1252    private static String region(int start, int end) {
1253        return "(" + start + " ... " + end + ")";
1254    }
1255
1256    private void checkRange(final String operation, int start, int end) {
1257        if (end < start) {
1258            throw new IndexOutOfBoundsException(operation + " " +
1259                    region(start, end) + " has end before start");
1260        }
1261
1262        int len = length();
1263
1264        if (start > len || end > len) {
1265            throw new IndexOutOfBoundsException(operation + " " +
1266                    region(start, end) + " ends beyond length " + len);
1267        }
1268
1269        if (start < 0 || end < 0) {
1270            throw new IndexOutOfBoundsException(operation + " " +
1271                    region(start, end) + " starts before 0");
1272        }
1273    }
1274
1275    /*
1276    private boolean isprint(char c) { // XXX
1277        if (c >= ' ' && c <= '~')
1278            return true;
1279        else
1280            return false;
1281    }
1282
1283    private static final int startFlag(int flag) {
1284        return (flag >> 4) & 0x0F;
1285    }
1286
1287    private static final int endFlag(int flag) {
1288        return flag & 0x0F;
1289    }
1290
1291    public void dump() { // XXX
1292        for (int i = 0; i < mGapStart; i++) {
1293            System.out.print('|');
1294            System.out.print(' ');
1295            System.out.print(isprint(mText[i]) ? mText[i] : '.');
1296            System.out.print(' ');
1297        }
1298
1299        for (int i = mGapStart; i < mGapStart + mGapLength; i++) {
1300            System.out.print('|');
1301            System.out.print('(');
1302            System.out.print(isprint(mText[i]) ? mText[i] : '.');
1303            System.out.print(')');
1304        }
1305
1306        for (int i = mGapStart + mGapLength; i < mText.length; i++) {
1307            System.out.print('|');
1308            System.out.print(' ');
1309            System.out.print(isprint(mText[i]) ? mText[i] : '.');
1310            System.out.print(' ');
1311        }
1312
1313        System.out.print('\n');
1314
1315        for (int i = 0; i < mText.length + 1; i++) {
1316            int found = 0;
1317            int wfound = 0;
1318
1319            for (int j = 0; j < mSpanCount; j++) {
1320                if (mSpanStarts[j] == i) {
1321                    found = 1;
1322                    wfound = j;
1323                    break;
1324                }
1325
1326                if (mSpanEnds[j] == i) {
1327                    found = 2;
1328                    wfound = j;
1329                    break;
1330                }
1331            }
1332
1333            if (found == 1) {
1334                if (startFlag(mSpanFlags[wfound]) == MARK)
1335                    System.out.print("(   ");
1336                if (startFlag(mSpanFlags[wfound]) == PARAGRAPH)
1337                    System.out.print("<   ");
1338                else
1339                    System.out.print("[   ");
1340            } else if (found == 2) {
1341                if (endFlag(mSpanFlags[wfound]) == POINT)
1342                    System.out.print(")   ");
1343                if (endFlag(mSpanFlags[wfound]) == PARAGRAPH)
1344                    System.out.print(">   ");
1345                else
1346                    System.out.print("]   ");
1347            } else {
1348                System.out.print("    ");
1349            }
1350        }
1351
1352        System.out.print("\n");
1353    }
1354    */
1355
1356    /**
1357     * Don't call this yourself -- exists for Canvas to use internally.
1358     * {@hide}
1359     */
1360    public void drawText(Canvas c, int start, int end, float x, float y, Paint p) {
1361        checkRange("drawText", start, end);
1362
1363        if (end <= mGapStart) {
1364            c.drawText(mText, start, end - start, x, y, p);
1365        } else if (start >= mGapStart) {
1366            c.drawText(mText, start + mGapLength, end - start, x, y, p);
1367        } else {
1368            char[] buf = TextUtils.obtain(end - start);
1369
1370            getChars(start, end, buf, 0);
1371            c.drawText(buf, 0, end - start, x, y, p);
1372            TextUtils.recycle(buf);
1373        }
1374    }
1375
1376
1377    /**
1378     * Don't call this yourself -- exists for Canvas to use internally.
1379     * {@hide}
1380     */
1381    public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
1382            float x, float y, boolean isRtl, Paint p) {
1383        checkRange("drawTextRun", start, end);
1384
1385        int contextLen = contextEnd - contextStart;
1386        int len = end - start;
1387        if (contextEnd <= mGapStart) {
1388            c.drawTextRun(mText, start, len, contextStart, contextLen, x, y, isRtl, p);
1389        } else if (contextStart >= mGapStart) {
1390            c.drawTextRun(mText, start + mGapLength, len, contextStart + mGapLength,
1391                    contextLen, x, y, isRtl, p);
1392        } else {
1393            char[] buf = TextUtils.obtain(contextLen);
1394            getChars(contextStart, contextEnd, buf, 0);
1395            c.drawTextRun(buf, start - contextStart, len, 0, contextLen, x, y, isRtl, p);
1396            TextUtils.recycle(buf);
1397        }
1398    }
1399
1400    /**
1401     * Don't call this yourself -- exists for Paint to use internally.
1402     * {@hide}
1403     */
1404    public float measureText(int start, int end, Paint p) {
1405        checkRange("measureText", start, end);
1406
1407        float ret;
1408
1409        if (end <= mGapStart) {
1410            ret = p.measureText(mText, start, end - start);
1411        } else if (start >= mGapStart) {
1412            ret = p.measureText(mText, start + mGapLength, end - start);
1413        } else {
1414            char[] buf = TextUtils.obtain(end - start);
1415
1416            getChars(start, end, buf, 0);
1417            ret = p.measureText(buf, 0, end - start);
1418            TextUtils.recycle(buf);
1419        }
1420
1421        return ret;
1422    }
1423
1424    /**
1425     * Don't call this yourself -- exists for Paint to use internally.
1426     * {@hide}
1427     */
1428    public int getTextWidths(int start, int end, float[] widths, Paint p) {
1429        checkRange("getTextWidths", start, end);
1430
1431        int ret;
1432
1433        if (end <= mGapStart) {
1434            ret = p.getTextWidths(mText, start, end - start, widths);
1435        } else if (start >= mGapStart) {
1436            ret = p.getTextWidths(mText, start + mGapLength, end - start, widths);
1437        } else {
1438            char[] buf = TextUtils.obtain(end - start);
1439
1440            getChars(start, end, buf, 0);
1441            ret = p.getTextWidths(buf, 0, end - start, widths);
1442            TextUtils.recycle(buf);
1443        }
1444
1445        return ret;
1446    }
1447
1448    /**
1449     * Don't call this yourself -- exists for Paint to use internally.
1450     * {@hide}
1451     */
1452    public float getTextRunAdvances(int start, int end, int contextStart, int contextEnd, boolean isRtl,
1453            float[] advances, int advancesPos, Paint p) {
1454
1455        float ret;
1456
1457        int contextLen = contextEnd - contextStart;
1458        int len = end - start;
1459
1460        if (end <= mGapStart) {
1461            ret = p.getTextRunAdvances(mText, start, len, contextStart, contextLen,
1462                    isRtl, advances, advancesPos);
1463        } else if (start >= mGapStart) {
1464            ret = p.getTextRunAdvances(mText, start + mGapLength, len,
1465                    contextStart + mGapLength, contextLen, isRtl, advances, advancesPos);
1466        } else {
1467            char[] buf = TextUtils.obtain(contextLen);
1468            getChars(contextStart, contextEnd, buf, 0);
1469            ret = p.getTextRunAdvances(buf, start - contextStart, len,
1470                    0, contextLen, isRtl, advances, advancesPos);
1471            TextUtils.recycle(buf);
1472        }
1473
1474        return ret;
1475    }
1476
1477    /**
1478     * Returns the next cursor position in the run.  This avoids placing the cursor between
1479     * surrogates, between characters that form conjuncts, between base characters and combining
1480     * marks, or within a reordering cluster.
1481     *
1482     * <p>The context is the shaping context for cursor movement, generally the bounds of the metric
1483     * span enclosing the cursor in the direction of movement.
1484     * <code>contextStart</code>, <code>contextEnd</code> and <code>offset</code> are relative to
1485     * the start of the string.</p>
1486     *
1487     * <p>If cursorOpt is CURSOR_AT and the offset is not a valid cursor position,
1488     * this returns -1.  Otherwise this will never return a value before contextStart or after
1489     * contextEnd.</p>
1490     *
1491     * @param contextStart the start index of the context
1492     * @param contextEnd the (non-inclusive) end index of the context
1493     * @param dir either DIRECTION_RTL or DIRECTION_LTR
1494     * @param offset the cursor position to move from
1495     * @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
1496     * CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
1497     * CURSOR_AT_OR_BEFORE, or CURSOR_AT
1498     * @param p the Paint object that is requesting this information
1499     * @return the offset of the next position, or -1
1500     * @deprecated This is an internal method, refrain from using it in your code
1501     */
1502    @Deprecated
1503    public int getTextRunCursor(int contextStart, int contextEnd, int dir, int offset,
1504            int cursorOpt, Paint p) {
1505
1506        int ret;
1507
1508        int contextLen = contextEnd - contextStart;
1509        if (contextEnd <= mGapStart) {
1510            ret = p.getTextRunCursor(mText, contextStart, contextLen,
1511                    dir, offset, cursorOpt);
1512        } else if (contextStart >= mGapStart) {
1513            ret = p.getTextRunCursor(mText, contextStart + mGapLength, contextLen,
1514                    dir, offset + mGapLength, cursorOpt) - mGapLength;
1515        } else {
1516            char[] buf = TextUtils.obtain(contextLen);
1517            getChars(contextStart, contextEnd, buf, 0);
1518            ret = p.getTextRunCursor(buf, 0, contextLen,
1519                    dir, offset - contextStart, cursorOpt) + contextStart;
1520            TextUtils.recycle(buf);
1521        }
1522
1523        return ret;
1524    }
1525
1526    // Documentation from interface
1527    public void setFilters(InputFilter[] filters) {
1528        if (filters == null) {
1529            throw new IllegalArgumentException();
1530        }
1531
1532        mFilters = filters;
1533    }
1534
1535    // Documentation from interface
1536    public InputFilter[] getFilters() {
1537        return mFilters;
1538    }
1539
1540    // Same as SpannableStringInternal
1541    @Override
1542    public boolean equals(Object o) {
1543        if (o instanceof Spanned &&
1544                toString().equals(o.toString())) {
1545            Spanned other = (Spanned) o;
1546            // Check span data
1547            Object[] otherSpans = other.getSpans(0, other.length(), Object.class);
1548            if (mSpanCount == otherSpans.length) {
1549                for (int i = 0; i < mSpanCount; ++i) {
1550                    Object thisSpan = mSpans[i];
1551                    Object otherSpan = otherSpans[i];
1552                    if (thisSpan == this) {
1553                        if (other != otherSpan ||
1554                                getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
1555                                getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
1556                                getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
1557                            return false;
1558                        }
1559                    } else if (!thisSpan.equals(otherSpan) ||
1560                            getSpanStart(thisSpan) != other.getSpanStart(otherSpan) ||
1561                            getSpanEnd(thisSpan) != other.getSpanEnd(otherSpan) ||
1562                            getSpanFlags(thisSpan) != other.getSpanFlags(otherSpan)) {
1563                        return false;
1564                    }
1565                }
1566                return true;
1567            }
1568        }
1569        return false;
1570    }
1571
1572    // Same as SpannableStringInternal
1573    @Override
1574    public int hashCode() {
1575        int hash = toString().hashCode();
1576        hash = hash * 31 + mSpanCount;
1577        for (int i = 0; i < mSpanCount; ++i) {
1578            Object span = mSpans[i];
1579            if (span != this) {
1580                hash = hash * 31 + span.hashCode();
1581            }
1582            hash = hash * 31 + getSpanStart(span);
1583            hash = hash * 31 + getSpanEnd(span);
1584            hash = hash * 31 + getSpanFlags(span);
1585        }
1586        return hash;
1587    }
1588
1589    // Primitives for treating span list as binary tree
1590
1591    // The spans (along with start and end offsets and flags) are stored in linear arrays sorted
1592    // by start offset. For fast searching, there is a binary search structure imposed over these
1593    // arrays. This structure is inorder traversal of a perfect binary tree, a slightly unusual
1594    // but advantageous approach.
1595
1596    // The value-containing nodes are indexed 0 <= i < n (where n = mSpanCount), thus preserving
1597    // logic that accesses the values as a contiguous array. Other balanced binary tree approaches
1598    // (such as a complete binary tree) would require some shuffling of node indices.
1599
1600    // Basic properties of this structure: For a perfect binary tree of height m:
1601    // The tree has 2^(m+1) - 1 total nodes.
1602    // The root of the tree has index 2^m - 1.
1603    // All leaf nodes have even index, all interior nodes odd.
1604    // The height of a node of index i is the number of trailing ones in i's binary representation.
1605    // The left child of a node i of height h is i - 2^(h - 1).
1606    // The right child of a node i of height h is i + 2^(h - 1).
1607
1608    // Note that for arbitrary n, interior nodes of this tree may be >= n. Thus, the general
1609    // structure of a recursive traversal of node i is:
1610    // * traverse left child if i is an interior node
1611    // * process i if i < n
1612    // * traverse right child if i is an interior node and i < n
1613
1614    private int treeRoot() {
1615        return Integer.highestOneBit(mSpanCount) - 1;
1616    }
1617
1618    // (i+1) & ~i is equal to 2^(the number of trailing ones in i)
1619    private static int leftChild(int i) {
1620        return i - (((i + 1) & ~i) >> 1);
1621    }
1622
1623    private static int rightChild(int i) {
1624        return i + (((i + 1) & ~i) >> 1);
1625    }
1626
1627    // The span arrays are also augmented by an mSpanMax[] array that represents an interval tree
1628    // over the binary tree structure described above. For each node, the mSpanMax[] array contains
1629    // the maximum value of mSpanEnds of that node and its descendants. Thus, traversals can
1630    // easily reject subtrees that contain no spans overlapping the area of interest.
1631
1632    // Note that mSpanMax[] also has a valid valuefor interior nodes of index >= n, but which have
1633    // descendants of index < n. In these cases, it simply represents the maximum span end of its
1634    // descendants. This is a consequence of the perfect binary tree structure.
1635    private int calcMax(int i) {
1636        int max = 0;
1637        if ((i & 1) != 0) {
1638            // internal tree node
1639            max = calcMax(leftChild(i));
1640        }
1641        if (i < mSpanCount) {
1642            max = Math.max(max, mSpanEnds[i]);
1643            if ((i & 1) != 0) {
1644                max = Math.max(max, calcMax(rightChild(i)));
1645            }
1646        }
1647        mSpanMax[i] = max;
1648        return max;
1649    }
1650
1651    // restores binary interval tree invariants after any mutation of span structure
1652    private void restoreInvariants() {
1653        if (mSpanCount == 0) return;
1654
1655        // invariant 1: span starts are nondecreasing
1656
1657        // This is a simple insertion sort because we expect it to be mostly sorted.
1658        for (int i = 1; i < mSpanCount; i++) {
1659            if (mSpanStarts[i] < mSpanStarts[i - 1]) {
1660                Object span = mSpans[i];
1661                int start = mSpanStarts[i];
1662                int end = mSpanEnds[i];
1663                int flags = mSpanFlags[i];
1664                int insertionOrder = mSpanOrder[i];
1665                int j = i;
1666                do {
1667                    mSpans[j] = mSpans[j - 1];
1668                    mSpanStarts[j] = mSpanStarts[j - 1];
1669                    mSpanEnds[j] = mSpanEnds[j - 1];
1670                    mSpanFlags[j] = mSpanFlags[j - 1];
1671                    mSpanOrder[j] = mSpanOrder[j - 1];
1672                    j--;
1673                } while (j > 0 && start < mSpanStarts[j - 1]);
1674                mSpans[j] = span;
1675                mSpanStarts[j] = start;
1676                mSpanEnds[j] = end;
1677                mSpanFlags[j] = flags;
1678                mSpanOrder[j] = insertionOrder;
1679                invalidateIndex(j);
1680            }
1681        }
1682
1683        // invariant 2: max is max span end for each node and its descendants
1684        calcMax(treeRoot());
1685
1686        // invariant 3: mIndexOfSpan maps spans back to indices
1687        if (mIndexOfSpan == null) {
1688            mIndexOfSpan = new IdentityHashMap<Object, Integer>();
1689        }
1690        for (int i = mLowWaterMark; i < mSpanCount; i++) {
1691            Integer existing = mIndexOfSpan.get(mSpans[i]);
1692            if (existing == null || existing != i) {
1693                mIndexOfSpan.put(mSpans[i], i);
1694            }
1695        }
1696        mLowWaterMark = Integer.MAX_VALUE;
1697    }
1698
1699    // Call this on any update to mSpans[], so that mIndexOfSpan can be updated
1700    private void invalidateIndex(int i) {
1701        mLowWaterMark = Math.min(i, mLowWaterMark);
1702    }
1703
1704    private static final InputFilter[] NO_FILTERS = new InputFilter[0];
1705    private InputFilter[] mFilters = NO_FILTERS;
1706
1707    private char[] mText;
1708    private int mGapStart;
1709    private int mGapLength;
1710
1711    private Object[] mSpans;
1712    private int[] mSpanStarts;
1713    private int[] mSpanEnds;
1714    private int[] mSpanMax;  // see calcMax() for an explanation of what this array stores
1715    private int[] mSpanFlags;
1716    private int[] mSpanOrder;  // store the order of span insertion
1717    private int mSpanInsertCount;  // counter for the span insertion
1718    private int[] mPrioSortBuffer;  // buffer used to sort getSpans result
1719    private int[] mOrderSortBuffer;  // buffer used to sort getSpans result
1720
1721    private int mSpanCount;
1722    private IdentityHashMap<Object, Integer> mIndexOfSpan;
1723    private int mLowWaterMark;  // indices below this have not been touched
1724
1725    // TextWatcher callbacks may trigger changes that trigger more callbacks. This keeps track of
1726    // how deep the callbacks go.
1727    private int mTextWatcherDepth;
1728
1729    // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned}
1730    private static final int MARK = 1;
1731    private static final int POINT = 2;
1732    private static final int PARAGRAPH = 3;
1733
1734    private static final int START_MASK = 0xF0;
1735    private static final int END_MASK = 0x0F;
1736    private static final int START_SHIFT = 4;
1737
1738    // These bits are not (currently) used by SPANNED flags
1739    private static final int SPAN_ADDED = 0x800;
1740    private static final int SPAN_START_AT_START = 0x1000;
1741    private static final int SPAN_START_AT_END = 0x2000;
1742    private static final int SPAN_END_AT_START = 0x4000;
1743    private static final int SPAN_END_AT_END = 0x8000;
1744    private static final int SPAN_START_END_MASK = 0xF000;
1745}
1746