FileDocCategorySizeDatePackage
SpannableStringBuilder.javaAPI DocAndroid 1.5 API35562Wed May 06 22:41:56 BST 2009android.text

SpannableStringBuilder

public class SpannableStringBuilder extends Object implements GraphicsOperations, Appendable, CharSequence, Editable, GetChars, Spannable
This is the class for text whose content and markup can both be changed.

Fields Summary
private static final InputFilter[]
NO_FILTERS
private InputFilter[]
mFilters
private char[]
mText
private int
mGapStart
private int
mGapLength
private Object[]
mSpans
private int[]
mSpanStarts
private int[]
mSpanEnds
private int[]
mSpanFlags
private int
mSpanCount
private static final int
MARK
private static final int
POINT
private static final int
PARAGRAPH
private static final int
START_MASK
private static final int
END_MASK
private static final int
START_SHIFT
Constructors Summary
public SpannableStringBuilder()
Create a new SpannableStringBuilder with empty contents

        this("");
    
public SpannableStringBuilder(CharSequence text)
Create a new SpannableStringBuilder containing a copy of the specified text, including its spans if any.

        this(text, 0, text.length());
    
public SpannableStringBuilder(CharSequence text, int start, int end)
Create a new SpannableStringBuilder containing a copy of the specified slice of the specified text, including its spans if any.

        int srclen = end - start;

        int len = ArrayUtils.idealCharArraySize(srclen + 1);
        mText = new char[len];
        mGapStart = srclen;
        mGapLength = len - srclen;

        TextUtils.getChars(text, start, end, mText, 0);

        mSpanCount = 0;
        int alloc = ArrayUtils.idealIntArraySize(0);
        mSpans = new Object[alloc];
        mSpanStarts = new int[alloc];
        mSpanEnds = new int[alloc];
        mSpanFlags = new int[alloc];

        if (text instanceof Spanned) {
            Spanned sp = (Spanned) text;
            Object[] spans = sp.getSpans(start, end, Object.class);

            for (int i = 0; i < spans.length; i++) {
                if (spans[i] instanceof NoCopySpan) {
                    continue;
                }
                
                int st = sp.getSpanStart(spans[i]) - start;
                int en = sp.getSpanEnd(spans[i]) - start;
                int fl = sp.getSpanFlags(spans[i]);

                if (st < 0)
                    st = 0;
                if (st > end - start)
                    st = end - start;

                if (en < 0)
                    en = 0;
                if (en > end - start)
                    en = end - start;

                setSpan(spans[i], st, en, fl);
            }
        }
    
Methods Summary
public android.text.SpannableStringBuilderappend(java.lang.CharSequence text)

        int length = length();
        return replace(length, length, text, 0, text.length());
    
public android.text.SpannableStringBuilderappend(java.lang.CharSequence text, int start, int end)

        int length = length();
        return replace(length, length, text, start, end);
    
public android.text.SpannableStringBuilderappend(char text)

        return append(String.valueOf(text));
    
private intchange(int start, int end, java.lang.CharSequence tb, int tbstart, int tbend)

        return change(true, start, end, tb, tbstart, tbend);
    
private intchange(boolean notify, int start, int end, java.lang.CharSequence tb, int tbstart, int tbend)

        checkRange("replace", start, end);
        int ret = tbend - tbstart;
        TextWatcher[] recipients = null;

        if (notify)
            recipients = sendTextWillChange(start, end - start,
                                            tbend - tbstart);

        for (int i = mSpanCount - 1; i >= 0; i--) {
            if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
                int st = mSpanStarts[i];
                if (st > mGapStart)
                    st -= mGapLength;

                int en = mSpanEnds[i];
                if (en > mGapStart)
                    en -= mGapLength;

                int ost = st;
                int oen = en;
                int clen = length();

                if (st > start && st <= end) {
                    for (st = end; st < clen; st++)
                        if (st > end && charAt(st - 1) == '\n")
                            break;
                }

                if (en > start && en <= end) {
                    for (en = end; en < clen; en++)
                        if (en > end && charAt(en - 1) == '\n")
                            break;
                }

                if (st != ost || en != oen)
                    setSpan(mSpans[i], st, en, mSpanFlags[i]);
            }
        }

        moveGapTo(end);

        if (tbend - tbstart >= mGapLength + (end - start))
            resizeFor(mText.length - mGapLength +
                      tbend - tbstart - (end - start));

        mGapStart += tbend - tbstart - (end - start);
        mGapLength -= tbend - tbstart - (end - start);

        if (mGapLength < 1)
            new Exception("mGapLength < 1").printStackTrace();

        TextUtils.getChars(tb, tbstart, tbend, mText, start);

        if (tb instanceof Spanned) {
            Spanned sp = (Spanned) tb;
            Object[] spans = sp.getSpans(tbstart, tbend, Object.class);

            for (int i = 0; i < spans.length; i++) {
                int st = sp.getSpanStart(spans[i]);
                int en = sp.getSpanEnd(spans[i]);

                if (st < tbstart)
                    st = tbstart;
                if (en > tbend)
                    en = tbend;

                if (getSpanStart(spans[i]) < 0) {
                    setSpan(false, spans[i],
                            st - tbstart + start,
                            en - tbstart + start,
                            sp.getSpanFlags(spans[i]));
                }
            }
        }

        // no need for span fixup on pure insertion
        if (tbend > tbstart && end - start == 0) {
            if (notify) {
                sendTextChange(recipients, start, end - start, tbend - tbstart);
                sendTextHasChanged(recipients);
            }

            return ret;
        }

        boolean atend = (mGapStart + mGapLength == mText.length);

        for (int i = mSpanCount - 1; i >= 0; i--) {
            if (mSpanStarts[i] >= start &&
                mSpanStarts[i] < mGapStart + mGapLength) {
                int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;

                if (flag == POINT || (flag == PARAGRAPH && atend))
                    mSpanStarts[i] = mGapStart + mGapLength;
                else
                    mSpanStarts[i] = start;
            }

            if (mSpanEnds[i] >= start &&
                mSpanEnds[i] < mGapStart + mGapLength) {
                int flag = (mSpanFlags[i] & END_MASK);

                if (flag == POINT || (flag == PARAGRAPH && atend))
                    mSpanEnds[i] = mGapStart + mGapLength;
                else
                    mSpanEnds[i] = start;
            }

            // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE
            // XXX send notification on removal

            if (mSpanEnds[i] < mSpanStarts[i]) {
                System.arraycopy(mSpans, i + 1,
                                 mSpans, i, mSpanCount - (i + 1));
                System.arraycopy(mSpanStarts, i + 1,
                                 mSpanStarts, i, mSpanCount - (i + 1));
                System.arraycopy(mSpanEnds, i + 1,
                                 mSpanEnds, i, mSpanCount - (i + 1));
                System.arraycopy(mSpanFlags, i + 1,
                                 mSpanFlags, i, mSpanCount - (i + 1));

                mSpanCount--;
            }
        }

        if (notify) {
            sendTextChange(recipients, start, end - start, tbend - tbstart);
            sendTextHasChanged(recipients);
        }

        return ret;
    
public charcharAt(int where)
Return the char at the specified offset within the buffer.

        int len = length();
        if (where < 0) {
            throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
        } else if (where >= len) {
            throw new IndexOutOfBoundsException("charAt: " + where +
                                                " >= length " + len);
        }

        if (where >= mGapStart)
            return mText[where + mGapLength];
        else
            return mText[where];
    
private voidcheckRange(java.lang.String operation, int start, int end)

        if (end < start) {
            throw new IndexOutOfBoundsException(operation + " " +
                                                region(start, end) +
                                                " has end before start");
        }

        int len = length();

        if (start > len || end > len) {
            throw new IndexOutOfBoundsException(operation + " " +
                                                region(start, end) +
                                                " ends beyond length " + len);
        }

        if (start < 0 || end < 0) {
            throw new IndexOutOfBoundsException(operation + " " +
                                                region(start, end) +
                                                " starts before 0");
        }
    
public voidclear()

        replace(0, length(), "", 0, 0);
    
public voidclearSpans()

        for (int i = mSpanCount - 1; i >= 0; i--) {
            Object what = mSpans[i];
            int ostart = mSpanStarts[i];
            int oend = mSpanEnds[i];

            if (ostart > mGapStart)
                ostart -= mGapLength;
            if (oend > mGapStart)
                oend -= mGapLength;

            mSpanCount = i;
            mSpans[i] = null;

            sendSpanRemoved(what, ostart, oend);
        }
    
public android.text.SpannableStringBuilderdelete(int start, int end)

        SpannableStringBuilder ret = replace(start, end, "", 0, 0);

        if (mGapLength > 2 * length())
            resizeFor(length());
        
        return ret; // == this
    
public voiddrawText(android.graphics.Canvas c, int start, int end, float x, float y, android.graphics.Paint p)
Don't call this yourself -- exists for Canvas to use internally. {@hide}

        checkRange("drawText", start, end);

        if (end <= mGapStart) {
            c.drawText(mText, start, end - start, x, y, p);
        } else if (start >= mGapStart) {
            c.drawText(mText, start + mGapLength, end - start, x, y, p);
        } else {
            char[] buf = TextUtils.obtain(end - start);

            getChars(start, end, buf, 0);
            c.drawText(buf, 0, end - start, x, y, p);
            TextUtils.recycle(buf);
        }
    
public voidgetChars(int start, int end, char[] dest, int destoff)
Copy the specified range of chars from this buffer into the specified array, beginning at the specified offset.

        checkRange("getChars", start, end);

        if (end <= mGapStart) {
            System.arraycopy(mText, start, dest, destoff, end - start);
        } else if (start >= mGapStart) {
            System.arraycopy(mText, start + mGapLength,
                             dest, destoff, end - start);
        } else {
            System.arraycopy(mText, start, dest, destoff, mGapStart - start);
            System.arraycopy(mText, mGapStart + mGapLength,
                             dest, destoff + (mGapStart - start),
                             end - mGapStart);
        }
    
public InputFilter[]getFilters()

        return mFilters;
    
public intgetSpanEnd(java.lang.Object what)
Return the buffer offset of the end of the specified markup object, or -1 if it is not attached to this buffer.

        int count = mSpanCount;
        Object[] spans = mSpans;

        for (int i = count - 1; i >= 0; i--) {
            if (spans[i] == what) {
                int where = mSpanEnds[i];

                if (where > mGapStart)
                    where -= mGapLength;

                return where;
            }
        }

        return -1;
    
public intgetSpanFlags(java.lang.Object what)
Return the flags of the end of the specified markup object, or 0 if it is not attached to this buffer.

        int count = mSpanCount;
        Object[] spans = mSpans;

        for (int i = count - 1; i >= 0; i--) {
            if (spans[i] == what) {
                return mSpanFlags[i];
            }
        }

        return 0; 
    
public intgetSpanStart(java.lang.Object what)
Return the buffer offset of the beginning of the specified markup object, or -1 if it is not attached to this buffer.

        int count = mSpanCount;
        Object[] spans = mSpans;

        for (int i = count - 1; i >= 0; i--) {
            if (spans[i] == what) {
                int where = mSpanStarts[i];

                if (where > mGapStart)
                    where -= mGapLength;

                return where;
            }
        }

        return -1;
    
public T[]getSpans(int queryStart, int queryEnd, java.lang.Class kind)
Return an array of the spans of the specified type that overlap the specified range of the buffer. The kind may be Object.class to get a list of all the spans regardless of type.

        int spanCount = mSpanCount;
        Object[] spans = mSpans;
        int[] starts = mSpanStarts;
        int[] ends = mSpanEnds;
        int[] flags = mSpanFlags;
        int gapstart = mGapStart;
        int gaplen = mGapLength;

        int count = 0;
        Object[] ret = null;
        Object ret1 = null;

        for (int i = 0; i < spanCount; i++) {
            int spanStart = starts[i];
            int spanEnd = ends[i];

            if (spanStart > gapstart) {
                spanStart -= gaplen;
            }
            if (spanEnd > gapstart) {
                spanEnd -= gaplen;
            }

            if (spanStart > queryEnd) {
                continue;
            }
            if (spanEnd < queryStart) {
                continue;
            }

            if (spanStart != spanEnd && queryStart != queryEnd) {
                if (spanStart == queryEnd)
                    continue;
                if (spanEnd == queryStart)
                    continue;
            }

            if (kind != null && !kind.isInstance(spans[i])) {
                continue;
            }

            if (count == 0) {
                ret1 = spans[i];
                count++;
            } else {
                if (count == 1) {
                    ret = (Object[]) Array.newInstance(kind, spanCount - i + 1);
                    ret[0] = ret1;
                }

                int prio = flags[i] & SPAN_PRIORITY;
                if (prio != 0) {
                    int j;

                    for (j = 0; j < count; j++) {
                        int p = getSpanFlags(ret[j]) & SPAN_PRIORITY;

                        if (prio > p) {
                            break;
                        }
                    }

                    System.arraycopy(ret, j, ret, j + 1, count - j);
                    ret[j] = spans[i];
                    count++;
                } else {
                    ret[count++] = spans[i];
                }
            }
        }

        if (count == 0) {
            return (T[]) ArrayUtils.emptyArray(kind);
        }
        if (count == 1) {
            ret = (Object[]) Array.newInstance(kind, 1);
            ret[0] = ret1;
            return (T[]) ret;
        }
        if (count == ret.length) {
            return (T[]) ret;
        }

        Object[] nret = (Object[]) Array.newInstance(kind, count);
        System.arraycopy(ret, 0, nret, 0, count);
        return (T[]) nret;
    
public intgetTextWidths(int start, int end, float[] widths, android.graphics.Paint p)
Don't call this yourself -- exists for Paint to use internally. {@hide}

        checkRange("getTextWidths", start, end);

        int ret;

        if (end <= mGapStart) {
            ret = p.getTextWidths(mText, start, end - start, widths);
        } else if (start >= mGapStart) {
            ret = p.getTextWidths(mText, start + mGapLength, end - start,
                                  widths);
        } else {
            char[] buf = TextUtils.obtain(end - start);

            getChars(start, end, buf, 0);
            ret = p.getTextWidths(buf, 0, end - start, widths);
            TextUtils.recycle(buf);
        }

        return ret;
    
public android.text.SpannableStringBuilderinsert(int where, java.lang.CharSequence tb)

        return replace(where, where, tb, 0, tb.length());
    
public android.text.SpannableStringBuilderinsert(int where, java.lang.CharSequence tb, int start, int end)

        return replace(where, where, tb, start, end);
    
private booleanisprint(char c)

 // XXX
        if (c >= ' " && c <= '~")
            return true;
        else
            return false;
    
public intlength()
Return the number of chars in the buffer.

        return mText.length - mGapLength;
    
public floatmeasureText(int start, int end, android.graphics.Paint p)
Don't call this yourself -- exists for Paint to use internally. {@hide}

        checkRange("measureText", start, end);

        float ret;

        if (end <= mGapStart) {
            ret = p.measureText(mText, start, end - start);
        } else if (start >= mGapStart) {
            ret = p.measureText(mText, start + mGapLength, end - start);
        } else {
            char[] buf = TextUtils.obtain(end - start);

            getChars(start, end, buf, 0);
            ret = p.measureText(buf, 0, end - start);
            TextUtils.recycle(buf);
        }

        return ret;
    
private voidmoveGapTo(int where)

        if (where == mGapStart)
            return;

        boolean atend = (where == length());

        if (where < mGapStart) {
            int overlap = mGapStart - where;

            System.arraycopy(mText, where,
                             mText, mGapStart + mGapLength - overlap, overlap);
        } else /* where > mGapStart */ {
            int overlap = where - mGapStart;

            System.arraycopy(mText, where + mGapLength - overlap,
                             mText, mGapStart, overlap);
        }

        // XXX be more clever
        for (int i = 0; i < mSpanCount; i++) {
            int start = mSpanStarts[i];
            int end = mSpanEnds[i];

            if (start > mGapStart)
                start -= mGapLength;
            if (start > where)
                start += mGapLength;
            else if (start == where) {
                int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;

                if (flag == POINT || (atend && flag == PARAGRAPH))
                    start += mGapLength;
            }

            if (end > mGapStart)
                end -= mGapLength;
            if (end > where)
                end += mGapLength;
            else if (end == where) {
                int flag = (mSpanFlags[i] & END_MASK);

                if (flag == POINT || (atend && flag == PARAGRAPH))
                    end += mGapLength;
            }

            mSpanStarts[i] = start;
            mSpanEnds[i] = end;
        }

        mGapStart = where;
    
public intnextSpanTransition(int start, int limit, java.lang.Class kind)
Return the next offset after start but less than or equal to limit where a span of the specified type begins or ends.

        int count = mSpanCount;
        Object[] spans = mSpans;
        int[] starts = mSpanStarts;
        int[] ends = mSpanEnds;
        int gapstart = mGapStart;
        int gaplen = mGapLength;

        if (kind == null) {
            kind = Object.class;
        }

        for (int i = 0; i < count; i++) {
            int st = starts[i];
            int en = ends[i];

            if (st > gapstart)
                st -= gaplen;
            if (en > gapstart)
                en -= gaplen;

            if (st > start && st < limit && kind.isInstance(spans[i]))
                limit = st;
            if (en > start && en < limit && kind.isInstance(spans[i]))
                limit = en;
        }

        return limit;
    
private static java.lang.Stringregion(int start, int end)

        return "(" + start + " ... " + end + ")";
    
public voidremoveSpan(java.lang.Object what)
Remove the specified markup object from the buffer.

        for (int i = mSpanCount - 1; i >= 0; i--) {
            if (mSpans[i] == what) {
                int ostart = mSpanStarts[i];
                int oend = mSpanEnds[i];

                if (ostart > mGapStart)
                    ostart -= mGapLength;
                if (oend > mGapStart)
                    oend -= mGapLength;

                int count = mSpanCount - (i + 1);

                System.arraycopy(mSpans, i + 1, mSpans, i, count);
                System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
                System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
                System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);

                mSpanCount--;
                mSpans[mSpanCount] = null;

                sendSpanRemoved(what, ostart, oend);
                return;
            }
        }
    
public android.text.SpannableStringBuilderreplace(int start, int end, java.lang.CharSequence tb)

        return replace(start, end, tb, 0, tb.length());
    
public android.text.SpannableStringBuilderreplace(int start, int end, java.lang.CharSequence tb, int tbstart, int tbend)

        int filtercount = mFilters.length;
        for (int i = 0; i < filtercount; i++) {
            CharSequence repl = mFilters[i].filter(tb, tbstart, tbend,
                                                   this, start, end);

            if (repl != null) {
                tb = repl;
                tbstart = 0;
                tbend = repl.length();
            }
        }

        if (end == start && tbstart == tbend) {
            return this;
        }

        if (end == start || tbstart == tbend) {
            change(start, end, tb, tbstart, tbend);
        } else {
            int selstart = Selection.getSelectionStart(this);
            int selend = Selection.getSelectionEnd(this);

            // XXX just make the span fixups in change() do the right thing
            // instead of this madness!

            checkRange("replace", start, end);
            moveGapTo(end);
            TextWatcher[] recipients;

            recipients = sendTextWillChange(start, end - start,
                                            tbend - tbstart);

            int origlen = end - start;

            if (mGapLength < 2)
                resizeFor(length() + 1);

            for (int i = mSpanCount - 1; i >= 0; i--) {
                if (mSpanStarts[i] == mGapStart)
                    mSpanStarts[i]++;

                if (mSpanEnds[i] == mGapStart)
                    mSpanEnds[i]++;
            }

            mText[mGapStart] = ' ";
            mGapStart++;
            mGapLength--;

            if (mGapLength < 1)
                new Exception("mGapLength < 1").printStackTrace();

            int oldlen = (end + 1) - start;

            int inserted = change(false, start + 1, start + 1,
                                  tb, tbstart, tbend);
            change(false, start, start + 1, "", 0, 0);
            change(false, start + inserted, start + inserted + oldlen - 1,
                   "", 0, 0);

            /*
             * Special case to keep the cursor in the same position
             * if it was somewhere in the middle of the replaced region.
             * If it was at the start or the end or crossing the whole
             * replacement, it should already be where it belongs.
             * TODO: Is there some more general mechanism that could
             * accomplish this?
             */
            if (selstart > start && selstart < end) {
                long off = selstart - start;

                off = off * inserted / (end - start);
                selstart = (int) off + start;

                setSpan(false, Selection.SELECTION_START, selstart, selstart,
                        Spanned.SPAN_POINT_POINT);
            }
            if (selend > start && selend < end) {
                long off = selend - start;

                off = off * inserted / (end - start);
                selend = (int) off + start;

                setSpan(false, Selection.SELECTION_END, selend, selend,
                        Spanned.SPAN_POINT_POINT);
            }

            sendTextChange(recipients, start, origlen, inserted);
            sendTextHasChanged(recipients);
        }
        return this; 
    
private voidresizeFor(int size)

        int newlen = ArrayUtils.idealCharArraySize(size + 1);
        char[] newtext = new char[newlen];

        int after = mText.length - (mGapStart + mGapLength);

        System.arraycopy(mText, 0, newtext, 0, mGapStart);
        System.arraycopy(mText, mText.length - after,
                         newtext, newlen - after, after);

        for (int i = 0; i < mSpanCount; i++) {
            if (mSpanStarts[i] > mGapStart)
                mSpanStarts[i] += newlen - mText.length;
            if (mSpanEnds[i] > mGapStart)
                mSpanEnds[i] += newlen - mText.length;
        }

        int oldlen = mText.length;
        mText = newtext;
        mGapLength += mText.length - oldlen;

        if (mGapLength < 1)
            new Exception("mGapLength < 1").printStackTrace();
    
private voidsendSpanAdded(java.lang.Object what, int start, int end)

        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
        int n = recip.length;

        for (int i = 0; i < n; i++) {
            recip[i].onSpanAdded(this, what, start, end);
        }
    
private voidsendSpanChanged(java.lang.Object what, int s, int e, int st, int en)

        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
                                  SpanWatcher.class);
        int n = recip.length;

        for (int i = 0; i < n; i++) {
            recip[i].onSpanChanged(this, what, s, e, st, en);
        }
    
private voidsendSpanRemoved(java.lang.Object what, int start, int end)

        SpanWatcher[] recip = getSpans(start, end, SpanWatcher.class);
        int n = recip.length;

        for (int i = 0; i < n; i++) {
            recip[i].onSpanRemoved(this, what, start, end);
        }
    
private voidsendTextChange(TextWatcher[] recip, int start, int before, int after)

        int n = recip.length;

        for (int i = 0; i < n; i++) {
            recip[i].onTextChanged(this, start, before, after);
        }
    
private voidsendTextHasChanged(TextWatcher[] recip)

        int n = recip.length;

        for (int i = 0; i < n; i++) {
            recip[i].afterTextChanged(this);
        }
    
private TextWatcher[]sendTextWillChange(int start, int before, int after)

        TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class);
        int n = recip.length;

        for (int i = 0; i < n; i++) {
            recip[i].beforeTextChanged(this, start, before, after);
        }

        return recip;
    
public voidsetFilters(InputFilter[] filters)

        if (filters == null) {
            throw new IllegalArgumentException();
        }

        mFilters = filters;
    
public voidsetSpan(java.lang.Object what, int start, int end, int flags)
Mark the specified range of text with the specified object. The flags determine how the span will behave when text is inserted at the start or end of the span's range.

        setSpan(true, what, start, end, flags);
    
private voidsetSpan(boolean send, java.lang.Object what, int start, int end, int flags)

        int nstart = start;
        int nend = end;

        checkRange("setSpan", start, end);

        if ((flags & START_MASK) == (PARAGRAPH << START_SHIFT)) {
            if (start != 0 && start != length()) {
                char c = charAt(start - 1);

                if (c != '\n")
                    throw new RuntimeException(
                            "PARAGRAPH span must start at paragraph boundary");
            }
        }

        if ((flags & END_MASK) == PARAGRAPH) {
            if (end != 0 && end != length()) {
                char c = charAt(end - 1);

                if (c != '\n")
                    throw new RuntimeException(
                            "PARAGRAPH span must end at paragraph boundary");
            }
        }

        if (start > mGapStart)
            start += mGapLength;
        else if (start == mGapStart) {
            int flag = (flags & START_MASK) >> START_SHIFT;

            if (flag == POINT || (flag == PARAGRAPH && start == length()))
                start += mGapLength;
        }

        if (end > mGapStart)
            end += mGapLength;
        else if (end == mGapStart) {
            int flag = (flags & END_MASK);

            if (flag == POINT || (flag == PARAGRAPH && end == length()))
                end += mGapLength;
        }

        int count = mSpanCount;
        Object[] spans = mSpans;

        for (int i = 0; i < count; i++) {
            if (spans[i] == what) {
                int ostart = mSpanStarts[i];
                int oend = mSpanEnds[i];

                if (ostart > mGapStart)
                    ostart -= mGapLength;
                if (oend > mGapStart)
                    oend -= mGapLength;

                mSpanStarts[i] = start;
                mSpanEnds[i] = end;
                mSpanFlags[i] = flags;

                if (send) 
                    sendSpanChanged(what, ostart, oend, nstart, nend);

                return;
            }
        }

        if (mSpanCount + 1 >= mSpans.length) {
            int newsize = ArrayUtils.idealIntArraySize(mSpanCount + 1);
            Object[] newspans = new Object[newsize];
            int[] newspanstarts = new int[newsize];
            int[] newspanends = new int[newsize];
            int[] newspanflags = new int[newsize];

            System.arraycopy(mSpans, 0, newspans, 0, mSpanCount);
            System.arraycopy(mSpanStarts, 0, newspanstarts, 0, mSpanCount);
            System.arraycopy(mSpanEnds, 0, newspanends, 0, mSpanCount);
            System.arraycopy(mSpanFlags, 0, newspanflags, 0, mSpanCount);

            mSpans = newspans;
            mSpanStarts = newspanstarts;
            mSpanEnds = newspanends;
            mSpanFlags = newspanflags;
        }

        mSpans[mSpanCount] = what;
        mSpanStarts[mSpanCount] = start;
        mSpanEnds[mSpanCount] = end;
        mSpanFlags[mSpanCount] = flags;
        mSpanCount++;

        if (send)
            sendSpanAdded(what, nstart, nend);
    
public java.lang.CharSequencesubSequence(int start, int end)
Return a new CharSequence containing a copy of the specified range of this buffer, including the overlapping spans.

        return new SpannableStringBuilder(this, start, end);
    
public java.lang.StringtoString()
Return a String containing a copy of the chars in this buffer.

        int len = length();
        char[] buf = new char[len];

        getChars(0, len, buf, 0);
        return new String(buf);
    
public static android.text.SpannableStringBuildervalueOf(java.lang.CharSequence source)

        if (source instanceof SpannableStringBuilder) {
            return (SpannableStringBuilder) source;
        } else {
            return new SpannableStringBuilder(source);
        }